Interactive CoffeeScript interpreter with multiline support, history persistence, Node.js integration, and top-level async/await support.
Starts a CoffeeScript REPL session with configurable options.
/**
* Start CoffeeScript REPL with custom options
* @param options - REPL configuration options
* @returns Node.js REPL server instance
*/
function start(options?: REPLOptions): REPLServer;
interface REPLOptions {
/** REPL prompt string (default: 'coffee> ') */
prompt?: string;
/** Path to history file (default: ~/.coffee_history) */
historyFile?: string;
/** Maximum input size for history (default: 10240) */
historyMaxInputSize?: number;
/** Custom evaluation function */
eval?: (input: string, context: any, filename: string, callback: Function) => void;
}
interface REPLServer {
/** Add command to REPL */
defineCommand(keyword: string, cmd: Function | { help: string, action: Function }): void;
/** Display help message */
displayPrompt(preserveCursor?: boolean): void;
/** Close REPL session */
close(): void;
}Usage Examples:
const CoffeeScript = require('coffeescript');
// Start default REPL
const repl = CoffeeScript.start();
// Start REPL with custom options
const customRepl = CoffeeScript.start({
prompt: 'cs> ',
historyFile: '/path/to/custom_history',
historyMaxInputSize: 5000
});
// Start REPL with custom evaluation
const debugRepl = CoffeeScript.start({
prompt: 'debug> ',
eval: function(input, context, filename, callback) {
console.log('Evaluating:', input);
// Use default evaluation
this.eval(input, context, filename, callback);
}
});// Access via repl.js module export
const replModule = require('coffeescript/repl');
const repl = replModule.start({ prompt: 'coffee> ' });The REPL compiles and evaluates CoffeeScript expressions interactively:
coffee> square = (x) -> x * x
[Function: square]
coffee> square 5
25
coffee> numbers = [1, 2, 3, 4, 5]
[ 1, 2, 3, 4, 5 ]
coffee> squares = (square num for num in numbers)
[ 1, 4, 9, 16, 25 ]
coffee> {name, age} = {name: 'Alice', age: 30, city: 'NYC'}
{ name: 'Alice', age: 30 }
coffee> console.log "Hello #{name}, you are #{age} years old"
Hello Alice, you are 30 years old
undefinedThe REPL supports multiline expressions and automatically detects incomplete input:
coffee> if true
...... console.log 'This is true'
...... result = 42
...... else
...... console.log 'This is false'
...... result = 0
......
This is true
42
coffee> class Calculator
...... constructor: (@name) ->
......
...... add: (a, b) ->
...... console.log "#{@name}: #{a} + #{b} = #{a + b}"
...... a + b
......
[class Calculator]
coffee> calc = new Calculator 'MyCalc'
Calculator { name: 'MyCalc' }
coffee> calc.add 3, 4
MyCalc: 3 + 4 = 7
7The REPL maintains persistent command history:
# History is automatically saved to ~/.coffee_history
coffee> square = (x) -> x * x
coffee> square 5
coffee> # Press Up arrow to navigate history
coffee> square 5 # Previous command recalledHistory features:
The REPL provides tab completion for variables, properties, and methods:
coffee> myObject = {name: 'test', value: 42, calculate: -> @value * 2}
coffee> myO<TAB> # Completes to myObject
coffee> myObject.<TAB> # Shows: name, value, calculate
coffee> Math.<TAB> # Shows: abs, ceil, floor, max, min, etc.
coffee> console.<TAB> # Shows: log, error, warn, info, etc.The REPL supports top-level await for asynchronous operations:
coffee> fs = require 'fs'
coffee> files = await fs.promises.readdir '.'
[ 'package.json', 'src', 'lib', 'test' ]
coffee> content = await fs.promises.readFile 'package.json', 'utf8'
coffee> JSON.parse(content).name
'my-project'
coffee> # Async function definition
coffee> fetchData = (delay) ->
...... new Promise (resolve) ->
...... setTimeout (-> resolve "Data after #{delay}ms"), delay
......
coffee> result = await fetchData 1000
Data after 1000ms
coffee> # Multiple async operations
coffee> [result1, result2] = await Promise.all [
...... fetchData(500),
...... fetchData(300)
....... ]
Data after 300ms
Data after 500msThe REPL has full access to Node.js APIs and modules:
coffee> path = require 'path'
coffee> path.join '/users', 'alice', 'documents'
'/users/alice/documents'
coffee> process.version
'v16.14.0'
coffee> os = require 'os'
coffee> os.platform()
'darwin'
coffee> # Global variables available
coffee> __filename
'/path/to/current/repl'
coffee> __dirname
'/path/to/current'
coffee> global.myGlobal = 'shared data'
'shared data'The REPL provides detailed error reporting with CoffeeScript context:
coffee> invalidSyntax ->
SyntaxError: unexpected ->
at Object.<anonymous> (repl:1:15)
at repl:1:1
coffee> undefinedVariable
ReferenceError: undefinedVariable is not defined
at repl:1:1
coffee> # Runtime errors show helpful context
coffee> divide = (a, b) ->
...... throw new Error "Division by zero" if b is 0
...... a / b
......
coffee> divide 10, 0
Error: Division by zero
at divide (repl:2:5)
at repl:1:1Add custom commands to the REPL:
const CoffeeScript = require('coffeescript');
const repl = CoffeeScript.start();
// Add custom help command
repl.defineCommand('help', {
help: 'Show custom help',
action: function() {
console.log('Custom CoffeeScript REPL Commands:');
console.log('.help - Show this help');
console.log('.clear - Clear current context');
console.log('.save <filename> - Save session to file');
this.displayPrompt();
}
});
// Add save command
repl.defineCommand('save', {
help: 'Save session to file',
action: function(filename) {
const fs = require('fs');
if (!filename) {
console.log('Usage: .save <filename>');
} else {
// Save logic here
console.log(`Session saved to ${filename}`);
}
this.displayPrompt();
}
});Create a REPL with custom evaluation logic:
const CoffeeScript = require('coffeescript');
function customEval(input, context, filename, callback) {
// Log all input
console.log(`[DEBUG] Evaluating: ${input.trim()}`);
try {
// Pre-process input
const processedInput = input.replace(/debug\s+/, 'console.log ');
// Use CoffeeScript compilation
const compiled = CoffeeScript.compile(processedInput, {
bare: true,
filename: 'repl'
});
// Evaluate in context
const result = eval(compiled);
callback(null, result);
} catch (error) {
callback(error);
}
}
const repl = CoffeeScript.start({
prompt: 'debug> ',
eval: customEval
});Configure REPL environment variables:
# Custom history file location
export COFFEE_HISTORY="/path/to/custom/history"
# Custom prompt
export COFFEE_PROMPT="cs> "
# Disable colors
export NO_COLOR=1
# Node.js options
export NODE_OPTIONS="--max-old-space-size=4096"
coffee # Starts REPL with custom configurationEmbed CoffeeScript REPL in applications:
const CoffeeScript = require('coffeescript');
const net = require('net');
// Create TCP server with CoffeeScript REPL
const server = net.createServer(socket => {
const repl = CoffeeScript.start({
prompt: 'remote> ',
input: socket,
output: socket,
eval: function(input, context, filename, callback) {
// Add application context
context.app = myApplication;
context.db = database;
// Use default evaluation
return CoffeeScript.eval(input, context, filename, callback);
}
});
repl.on('exit', () => socket.end());
});
server.listen(3000, () => {
console.log('CoffeeScript REPL server listening on port 3000');
});const CoffeeScript = require('coffeescript');
// Start REPL with pre-populated context
const repl = CoffeeScript.start();
// Add utilities to context
repl.context._ = require('underscore');
repl.context.fs = require('fs');
repl.context.path = require('path');
// Add custom functions
repl.context.loadJson = function(filename) {
const fs = require('fs');
return JSON.parse(fs.readFileSync(filename, 'utf8'));
};
repl.context.saveJson = function(filename, data) {
const fs = require('fs');
fs.writeFileSync(filename, JSON.stringify(data, null, 2));
};
// Now available in REPL:
// coffee> data = loadJson 'config.json'
// coffee> data.version = '2.0.0'
// coffee> saveJson 'config.json', dataconst CoffeeScript = require('coffeescript');
const fs = require('fs');
class REPLSession {
constructor(options = {}) {
this.repl = CoffeeScript.start({
prompt: options.prompt || 'session> ',
...options
});
this.history = [];
this.setupLogging();
}
setupLogging() {
const originalEval = this.repl.eval;
this.repl.eval = (input, context, filename, callback) => {
this.history.push({
input: input.trim(),
timestamp: new Date()
});
return originalEval.call(this.repl, input, context, filename, (err, result) => {
if (!err) {
this.history[this.history.length - 1].result = result;
}
callback(err, result);
});
};
}
saveSession(filename) {
fs.writeFileSync(filename, JSON.stringify(this.history, null, 2));
}
loadSession(filename) {
const session = JSON.parse(fs.readFileSync(filename, 'utf8'));
session.forEach(entry => {
console.log(`${entry.timestamp}: ${entry.input}`);
if (entry.result !== undefined) {
console.log(`=> ${entry.result}`);
}
});
}
}
// Usage
const session = new REPLSession({ prompt: 'logged> ' });
// After some interaction:
// session.saveSession('my-session.json');