From 0235d12927201f18f984639f5e488103e1265e27 Mon Sep 17 00:00:00 2001 From: Michael Klement Date: Tue, 6 Aug 2013 21:28:34 -0400 Subject: [PATCH 1/5] Make the REPL use the global context to be consistent with the node REPL. This will make packages that modify prototypes - e.g. 'colors', 'sugar' - work as expected. To verify that the `node` REPL uses the global context, execute `global === module.exports.repl.context`. Note: Tests pass, except `cluster.coffee`, which, however, failed even before these modifications. --- lib/coffee-script/repl.js | 3 ++- src/repl.coffee | 4 +++- test/repl.coffee | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/coffee-script/repl.js b/lib/coffee-script/repl.js index 9a3580df..ac24c441 100644 --- a/lib/coffee-script/repl.js +++ b/lib/coffee-script/repl.js @@ -18,6 +18,7 @@ prompt: 'coffee> ', historyFile: process.env.HOME ? path.join(process.env.HOME, '.coffee_history') : void 0, historyMaxInputSize: 10240, + useGlobal: true, "eval": function(input, context, filename, cb) { var Assign, Block, Literal, Value, ast, err, js, _ref1; input = input.replace(/\uFF00/g, '\n'); @@ -30,7 +31,7 @@ bare: true, locals: Object.keys(context) }); - return cb(null, vm.runInContext(js, context, filename)); + return cb(null, context === global ? vm.runInThisContext(js, filename) : vm.runInContext(js, context, filename)); } catch (_error) { err = _error; updateSyntaxError(err, input); diff --git a/src/repl.coffee b/src/repl.coffee index 33051262..f120c934 100644 --- a/src/repl.coffee +++ b/src/repl.coffee @@ -9,6 +9,8 @@ replDefaults = prompt: 'coffee> ', historyFile: path.join process.env.HOME, '.coffee_history' if process.env.HOME historyMaxInputSize: 10240 + # Make the REPL use the global context by default so as to (a) be consistent with the `node` REPL and, therefore, (b) make packages that modify prototypes - e.g., 'colors', 'sugar' - work as expected. + useGlobal: yes eval: (input, context, filename, cb) -> # XXX: multiline hack. input = input.replace /\uFF00/g, '\n' @@ -27,7 +29,7 @@ replDefaults = new Assign (new Value new Literal '_'), ast, '=' ] js = ast.compile bare: yes, locals: Object.keys(context) - cb null, vm.runInContext(js, context, filename) + cb null, if context is global then vm.runInThisContext(js, filename) else vm.runInContext(js, context, filename) catch err # AST's `compile` does not add source code information to syntax errors. updateSyntaxError err, input diff --git a/test/repl.coffee b/test/repl.coffee index 084cb1d3..b619c26d 100644 --- a/test/repl.coffee +++ b/test/repl.coffee @@ -102,10 +102,11 @@ testRepl "existential assignment of previously declared variable", (input, outpu eq '42', output.lastWrite() testRepl "keeps running after runtime error", (input, output) -> + input.emitLine 'a = 0' # Note: with the REPL option `useGlobal` set to true, variables are retained across tests. input.emitLine 'a = b' eq 0, output.lastWrite().indexOf 'ReferenceError: b is not defined' input.emitLine 'a' - eq 'undefined', output.lastWrite() + eq '0', output.lastWrite() process.on 'exit', -> fs.unlinkSync historyFile From 675095efbec691187a7a55c9334bf5bbabbb2e02 Mon Sep 17 00:00:00 2001 From: Michael Klement Date: Wed, 7 Aug 2013 08:59:27 -0400 Subject: [PATCH 2/5] Amended - Make the REPL *CLI* use the global context so as to be consistent with the `node` REPL CLI. (My apologies: In the previous commit I accidentally made `useGlobal: yes` the default for _programmatic_ use also, but the intent was to only do it for the stand-alone *CLI*.) 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. Note that, by contrast, programmatic use (`require 'repl'`) will continue to default to a NON-global context - again, consistent with node's behavior. --- lib/coffee-script/command.js | 9 ++++++--- lib/coffee-script/repl.js | 1 - src/command.coffee | 20 ++++++++++++-------- src/repl.coffee | 2 -- test/repl.coffee | 3 +-- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/coffee-script/command.js b/lib/coffee-script/command.js index 2040e621..39b53457 100644 --- a/lib/coffee-script/command.js +++ b/lib/coffee-script/command.js @@ -53,8 +53,11 @@ optionParser = null; exports.run = function() { - var literals, source, _i, _len, _results; + var literals, replCliOpts, source, _i, _len, _results; parseOptions(); + replCliOpts = { + useGlobal: true + }; if (opts.nodejs) { return forkNode(); } @@ -65,7 +68,7 @@ return version(); } if (opts.interactive) { - return require('./repl').start(); + return require('./repl').start(replCliOpts); } if (opts.watch && !fs.watch) { return printWarn("The --watch feature depends on Node v0.6.0+. You are running " + process.version + "."); @@ -77,7 +80,7 @@ return compileScript(null, sources[0]); } if (!sources.length) { - return require('./repl').start(); + return require('./repl').start(replCliOpts); } literals = opts.run ? sources.splice(1) : []; process.argv = process.argv.slice(0, 2).concat(literals); diff --git a/lib/coffee-script/repl.js b/lib/coffee-script/repl.js index ac24c441..3249660c 100644 --- a/lib/coffee-script/repl.js +++ b/lib/coffee-script/repl.js @@ -18,7 +18,6 @@ prompt: 'coffee> ', historyFile: process.env.HOME ? path.join(process.env.HOME, '.coffee_history') : void 0, historyMaxInputSize: 10240, - useGlobal: true, "eval": function(input, context, filename, cb) { var Assign, Block, Literal, Value, ast, err, js, _ref1; input = input.replace(/\uFF00/g, '\n'); diff --git a/src/command.coffee b/src/command.coffee index b2198849..67274f68 100644 --- a/src/command.coffee +++ b/src/command.coffee @@ -25,7 +25,7 @@ printWarn = (line) -> process.stderr.write line + '\n' hidden = (file) -> /^\.|~$/.test file -# The help banner that is printed when `coffee` is called without arguments. +# The help banner that is printed in conjunction with `-h`/`--help`. BANNER = ''' Usage: coffee [options] path/to/script.coffee -- [args] @@ -65,15 +65,19 @@ optionParser = null # `--` will be passed verbatim to your script as arguments in `process.argv` exports.run = -> parseOptions() - return forkNode() if opts.nodejs - return usage() if opts.help - return version() if opts.version - return require('./repl').start() if opts.interactive + # 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 + return forkNode() if opts.nodejs + return usage() if opts.help + return version() if opts.version + return require('./repl').start(replCliOpts) if opts.interactive if opts.watch and not fs.watch return printWarn "The --watch feature depends on Node v0.6.0+. You are running #{process.version}." - return compileStdio() if opts.stdio - return compileScript null, sources[0] if opts.eval - return require('./repl').start() unless sources.length + return compileStdio() if opts.stdio + return compileScript null, sources[0] if opts.eval + return require('./repl').start(replCliOpts) unless sources.length literals = if opts.run then sources.splice 1 else [] process.argv = process.argv[0..1].concat literals process.argv[0] = 'coffee' diff --git a/src/repl.coffee b/src/repl.coffee index f120c934..917c4a23 100644 --- a/src/repl.coffee +++ b/src/repl.coffee @@ -9,8 +9,6 @@ replDefaults = prompt: 'coffee> ', historyFile: path.join process.env.HOME, '.coffee_history' if process.env.HOME historyMaxInputSize: 10240 - # Make the REPL use the global context by default so as to (a) be consistent with the `node` REPL and, therefore, (b) make packages that modify prototypes - e.g., 'colors', 'sugar' - work as expected. - useGlobal: yes eval: (input, context, filename, cb) -> # XXX: multiline hack. input = input.replace /\uFF00/g, '\n' diff --git a/test/repl.coffee b/test/repl.coffee index b619c26d..084cb1d3 100644 --- a/test/repl.coffee +++ b/test/repl.coffee @@ -102,11 +102,10 @@ testRepl "existential assignment of previously declared variable", (input, outpu eq '42', output.lastWrite() testRepl "keeps running after runtime error", (input, output) -> - input.emitLine 'a = 0' # Note: with the REPL option `useGlobal` set to true, variables are retained across tests. input.emitLine 'a = b' eq 0, output.lastWrite().indexOf 'ReferenceError: b is not defined' input.emitLine 'a' - eq '0', output.lastWrite() + eq 'undefined', output.lastWrite() process.on 'exit', -> fs.unlinkSync historyFile From 70994d4b507bc896d03343184b2607119d6b109b Mon Sep 17 00:00:00 2001 From: Michael Klement Date: Wed, 7 Aug 2013 11:40:11 -0400 Subject: [PATCH 3/5] Refactored inline-if into more readable multi-line statement. --- lib/coffee-script/repl.js | 5 +++-- src/repl.coffee | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/coffee-script/repl.js b/lib/coffee-script/repl.js index 3249660c..a2e4e57d 100644 --- a/lib/coffee-script/repl.js +++ b/lib/coffee-script/repl.js @@ -19,7 +19,7 @@ historyFile: process.env.HOME ? path.join(process.env.HOME, '.coffee_history') : void 0, historyMaxInputSize: 10240, "eval": function(input, context, filename, cb) { - var Assign, Block, Literal, Value, ast, err, js, _ref1; + var Assign, Block, Literal, Value, ast, err, js, result, _ref1; input = input.replace(/\uFF00/g, '\n'); input = input.replace(/^\(([\s\S]*)\n\)$/m, '$1'); _ref1 = require('./nodes'), Block = _ref1.Block, Assign = _ref1.Assign, Value = _ref1.Value, Literal = _ref1.Literal; @@ -30,7 +30,8 @@ bare: true, locals: Object.keys(context) }); - return cb(null, context === global ? vm.runInThisContext(js, filename) : vm.runInContext(js, context, filename)); + result = context === global ? vm.runInThisContext(js, filename) : vm.runInContext(js, context, filename); + return cb(null, result); } catch (_error) { err = _error; updateSyntaxError(err, input); diff --git a/src/repl.coffee b/src/repl.coffee index 917c4a23..59696f4d 100644 --- a/src/repl.coffee +++ b/src/repl.coffee @@ -27,7 +27,11 @@ replDefaults = new Assign (new Value new Literal '_'), ast, '=' ] js = ast.compile bare: yes, locals: Object.keys(context) - cb null, if context is global then vm.runInThisContext(js, filename) else vm.runInContext(js, context, filename) + result = if context is global + vm.runInThisContext js, filename + else + vm.runInContext js, context, filename + cb null, result catch err # AST's `compile` does not add source code information to syntax errors. updateSyntaxError err, input From 3e9d01d6c64dbcf087c709f23648d655636d09cd Mon Sep 17 00:00:00 2001 From: Michael Klement Date: Tue, 3 Sep 2013 17:27:13 -0400 Subject: [PATCH 4/5] Make the REPL *CLI* use the global context so as to be consistent with the `node` REPL CLI. 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. Note that, by contrast, programmatic use (`require 'repl'`) will continue to default to a NON-global context - again, consistent with node's behavior. --- lib/coffee-script/command.js | 9 ++++++--- src/command.coffee | 20 ++++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/coffee-script/command.js b/lib/coffee-script/command.js index bc7d03ea..2f1118e4 100644 --- a/lib/coffee-script/command.js +++ b/lib/coffee-script/command.js @@ -53,8 +53,11 @@ optionParser = null; exports.run = function() { - var literals, source, _i, _len, _results; + var literals, replCliOpts, source, _i, _len, _results; parseOptions(); + replCliOpts = { + useGlobal: true + }; if (opts.nodejs) { return forkNode(); } @@ -65,7 +68,7 @@ return version(); } if (opts.interactive) { - return require('./repl').start(); + return require('./repl').start(replCliOpts); } if (opts.watch && !fs.watch) { return printWarn("The --watch feature depends on Node v0.6.0+. You are running " + process.version + "."); @@ -77,7 +80,7 @@ return compileScript(null, sources[0]); } if (!sources.length) { - return require('./repl').start(); + return require('./repl').start(replCliOpts); } literals = opts.run ? sources.splice(1) : []; process.argv = process.argv.slice(0, 2).concat(literals); diff --git a/src/command.coffee b/src/command.coffee index 42e2feac..f9d9428f 100644 --- a/src/command.coffee +++ b/src/command.coffee @@ -25,7 +25,7 @@ printWarn = (line) -> process.stderr.write line + '\n' hidden = (file) -> /^\.|~$/.test file -# The help banner that is printed when `coffee` is called without arguments. +# The help banner that is printed in conjunction with `-h`/`--help`. BANNER = ''' Usage: coffee [options] path/to/script.coffee -- [args] @@ -65,15 +65,19 @@ optionParser = null # `--` will be passed verbatim to your script as arguments in `process.argv` exports.run = -> parseOptions() - return forkNode() if opts.nodejs - return usage() if opts.help - return version() if opts.version - return require('./repl').start() if opts.interactive + # 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 + return forkNode() if opts.nodejs + return usage() if opts.help + return version() if opts.version + return require('./repl').start(replCliOpts) if opts.interactive if opts.watch and not fs.watch return printWarn "The --watch feature depends on Node v0.6.0+. You are running #{process.version}." - return compileStdio() if opts.stdio - return compileScript null, sources[0] if opts.eval - return require('./repl').start() unless sources.length + return compileStdio() if opts.stdio + return compileScript null, sources[0] if opts.eval + return require('./repl').start(replCliOpts) unless sources.length literals = if opts.run then sources.splice 1 else [] process.argv = process.argv[0..1].concat literals process.argv[0] = 'coffee' From fceff1729c4dd1c4c1079e84de2dd0080dd2a868 Mon Sep 17 00:00:00 2001 From: Michael Klement Date: Tue, 3 Sep 2013 17:27:13 -0400 Subject: [PATCH 5/5] Make the REPL *CLI* use the global context so as to be consistent with the `node` REPL CLI. 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. Note that, by contrast, programmatic use (`require 'repl'`) will continue to default to a NON-global context - again, consistent with node's behavior. --- lib/coffee-script/command.js | 9 ++++++--- src/command.coffee | 20 ++++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/coffee-script/command.js b/lib/coffee-script/command.js index bc7d03ea..2f1118e4 100644 --- a/lib/coffee-script/command.js +++ b/lib/coffee-script/command.js @@ -53,8 +53,11 @@ optionParser = null; exports.run = function() { - var literals, source, _i, _len, _results; + var literals, replCliOpts, source, _i, _len, _results; parseOptions(); + replCliOpts = { + useGlobal: true + }; if (opts.nodejs) { return forkNode(); } @@ -65,7 +68,7 @@ return version(); } if (opts.interactive) { - return require('./repl').start(); + return require('./repl').start(replCliOpts); } if (opts.watch && !fs.watch) { return printWarn("The --watch feature depends on Node v0.6.0+. You are running " + process.version + "."); @@ -77,7 +80,7 @@ return compileScript(null, sources[0]); } if (!sources.length) { - return require('./repl').start(); + return require('./repl').start(replCliOpts); } literals = opts.run ? sources.splice(1) : []; process.argv = process.argv.slice(0, 2).concat(literals); diff --git a/src/command.coffee b/src/command.coffee index 42e2feac..f9d9428f 100644 --- a/src/command.coffee +++ b/src/command.coffee @@ -25,7 +25,7 @@ printWarn = (line) -> process.stderr.write line + '\n' hidden = (file) -> /^\.|~$/.test file -# The help banner that is printed when `coffee` is called without arguments. +# The help banner that is printed in conjunction with `-h`/`--help`. BANNER = ''' Usage: coffee [options] path/to/script.coffee -- [args] @@ -65,15 +65,19 @@ optionParser = null # `--` will be passed verbatim to your script as arguments in `process.argv` exports.run = -> parseOptions() - return forkNode() if opts.nodejs - return usage() if opts.help - return version() if opts.version - return require('./repl').start() if opts.interactive + # 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 + return forkNode() if opts.nodejs + return usage() if opts.help + return version() if opts.version + return require('./repl').start(replCliOpts) if opts.interactive if opts.watch and not fs.watch return printWarn "The --watch feature depends on Node v0.6.0+. You are running #{process.version}." - return compileStdio() if opts.stdio - return compileScript null, sources[0] if opts.eval - return require('./repl').start() unless sources.length + return compileStdio() if opts.stdio + return compileScript null, sources[0] if opts.eval + return require('./repl').start(replCliOpts) unless sources.length literals = if opts.run then sources.splice 1 else [] process.argv = process.argv[0..1].concat literals process.argv[0] = 'coffee'