Comparing version 1.0.3 to 1.1.0
{ | ||
"type": "module", | ||
"name": "bun-repl", | ||
"version": "1.0.3", | ||
"version": "1.1.0", | ||
"description": "Experimental unofficial REPL for Bun", | ||
@@ -28,3 +28,4 @@ "main": "src/module/repl.ts", | ||
"@swc/core": "^1.2.224", | ||
"pretty-ms": "^8.0.0" | ||
"pretty-ms": "^8.0.0", | ||
"rustybun": "^0.1.2" | ||
}, | ||
@@ -31,0 +32,0 @@ "devDependencies": { |
@@ -23,6 +23,4 @@ # bun-repl [![GitHub version][github-image]][github-url] [![GitHub code size in bytes][size-image]][github-url] [![license][license-image]][license-url] | ||
Press `↑`+`Enter` to travel up the execution history. | ||
Press `↑` and `↓` to travel up or down the execution history. | ||
Press `↓`+`Enter` to travel back down. | ||
## Features | ||
@@ -46,10 +44,4 @@ | ||
* Multi-line inputs are not supported. | ||
* Reason: Same as below. | ||
* Pressing `←` and `→` to traverse the input with the cursor is not supported. | ||
* Reason: Same as below. | ||
* The execution history is buggy to navigate and doesn't support backspacing past the history entry. | ||
* Reason: Bun's current lack of support for event-based streams limits us to `prompt()`, which provides little to no control over the input as its being written. This also makes it impossible to intercept keypresses/combinations. | ||
* There is a space between the execution history entry and your appended code. | ||
* Reason: `prompt()` automatically inserts a space at the end, with no way to turn it off. Yes, quite annoying. | ||
* To preserve lexically-scoped variables (`let` & `const`) across REPL runs, they need to be converted to `var`, which disrupts their behavior, especially `const`'s | ||
* Reason: The library used for prompts (`rustybun`) doesn't support this. | ||
* To preserve lexically-scoped variables (`let` & `const`) across REPL runs, they need to be converted to `var`, which disrupts their behavior, especially `const`'s (This also requires using non-strict mode) | ||
* Reason: Usage of `eval()` which has its own lexical scope. | ||
@@ -56,0 +48,0 @@ |
@@ -64,2 +64,7 @@ const colors = { | ||
export default { ...colors, bg: bgColors }; | ||
function bool(bool: boolean, important: boolean = false) { | ||
if (bool) return `${colors.greenBright}true${colors.reset}`; | ||
else return `${important ? colors.redBright : colors.gray}false${colors.reset}`; | ||
} | ||
export default { ...colors, bg: bgColors, bool }; |
@@ -57,2 +57,8 @@ declare global { | ||
interface PartialNPMResponse { | ||
'dist-tags': { | ||
latest: string | ||
} | ||
} | ||
interface ImportMeta { | ||
@@ -59,0 +65,0 @@ require: (moduleIdentifier: string) => unknown; |
@@ -5,3 +5,4 @@ /* eslint-disable unicorn/custom-error-definition */ | ||
import $ from '../colors'; | ||
import util from 'util'; | ||
import bun from 'bun'; | ||
import { SafeInspect } from '../utils'; | ||
@@ -60,3 +61,3 @@ class NotImplementedError extends Error { | ||
depth: 2, | ||
colors: Bun.enableANSIColors, | ||
colors: bun.enableANSIColors, | ||
customInspect: true, | ||
@@ -77,15 +78,16 @@ showProxy: true, | ||
export const writer: REPLWriterFunction = function writer(val: any): string { | ||
return util.inspect(val, (<REPLWriterFunction>writer).options); | ||
}; | ||
writer.options = new Proxy(REPLWriterOptionsDefaults, { | ||
set(target, p, value) { | ||
console.warn(`${$.yellow+$.dim}(warning) repl.writer.options are currently not all fully respected by the REPL.${$.reset}`); | ||
// @ts-expect-error Temporary warning Proxy | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment | ||
target[p] = value; | ||
return true; | ||
} | ||
export const writer = function writer(val: any): string { | ||
return (SafeInspect(val, (<REPLWriterFunction>writer).options) ?? bun.inspect(val)); | ||
} as REPLWriterFunction; | ||
Object.defineProperty(writer, 'options', { | ||
value: new Proxy(REPLWriterOptionsDefaults, { | ||
set(target, p, value) { | ||
console.warn(`${$.yellow+$.dim}(warning) repl.writer.options are currently not all fully respected by the REPL.${$.reset}`); | ||
// @ts-expect-error Temporary warning Proxy | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment | ||
target[p] = value; | ||
return true; | ||
} | ||
}), enumerable: true | ||
}); | ||
} | ||
@@ -92,0 +94,0 @@ |
136
src/repl.ts
@@ -10,7 +10,8 @@ #!/usr/bin/env bun | ||
import Transpiler from './transpiler'; | ||
import REPLState from './replstate'; | ||
import REPLHistory from './replhistory'; | ||
import REPLManager from './replmanager'; | ||
import pkgjson from './pkgjson'; | ||
import { debuglog, IS_DEBUG } from './debug'; | ||
debuglog(process.argv); | ||
const helpFlag = process.argv.includes('-h') || process.argv.includes('--help'); | ||
@@ -28,8 +29,2 @@ if (helpFlag) { | ||
interface PartialNPMResponse { | ||
'dist-tags': { | ||
latest: string | ||
} | ||
} | ||
if (process.argv.includes('--update')) { | ||
@@ -51,22 +46,57 @@ console.log(`${$.dim}Checking for updates...${$.reset}`); | ||
let evalFlag = process.argv.indexOf('-e'); | ||
if (evalFlag === -1) evalFlag = process.argv.indexOf('--eval'); | ||
let printFlag = process.argv.indexOf('-p'); | ||
if (printFlag === -1) printFlag = process.argv.indexOf('--print'); | ||
if (evalFlag !== -1 && printFlag !== -1) { | ||
console.error(`bun-repl: Incompatible options "${$.whiteBright}--eval${$.red}" and "${$.whiteBright}--print${$.red}"`); | ||
console.error(`For more information try ${$.whiteBright}--help${$.reset}`); | ||
process.exit(0); | ||
} | ||
const singleshot = evalFlag !== -1 || printFlag !== -1; | ||
const singleshotCode = process.argv.slice((evalFlag !== -1 ? evalFlag : printFlag) + 1).join(' '); | ||
const validFlags = [ | ||
'-h', '--help', '-v', '--version', '--update', '--debug', '-e', '--eval', '-p', '--print' | ||
] as const; | ||
if (process.argv.length > 2) { | ||
for (const arg of process.argv.slice(2)) { | ||
if (['-e', '--eval', '-p', '--print'].includes(arg)) break; | ||
if (validFlags.includes(arg as typeof validFlags[0])) continue; | ||
console.error(`bun-repl: Unknown option "${$.whiteBright+arg+$.red}"`); | ||
console.error(`For more information try ${$.whiteBright}--help${$.reset}`); | ||
process.exit(0); | ||
} | ||
} | ||
const realm = new ShadowRealm(); | ||
// Inject realm globals | ||
await realm.execFile('./realm.mjs'); | ||
const replState = new REPLState(); | ||
const replHistory = new REPLHistory(); | ||
const repl = new REPLManager(); | ||
const transpiler = new Transpiler(); | ||
console.log(`Welcome to Bun.js ${process.version} (${process.revision ? 'canary-'+process.revision : 'release'}) | ||
Type ".help" for more information.`); | ||
debuglog(`${$.dim}INFO: Debug mode enabled.${$.reset}`); | ||
// Prepare realm environment | ||
await realm.execFile('./realm.mjs'); | ||
MainLoop: for (;;) { | ||
if (!singleshot) { | ||
console.log( | ||
`Welcome to Bun.js ${process.version} (${process.revision ? 'canary-' + process.revision : 'release'})\n` + | ||
`Type ".help" for more information.` | ||
); | ||
debuglog(`${$.dim}INFO: Debug mode enabled.${$.reset}`); | ||
} | ||
let userInput = prompt(`>${replState.historyEntry ? ' ' + replState.historyEntry : ''}`)?.trim(); | ||
MainLoop: do { | ||
const userInput = singleshot ? singleshotCode : repl.promptline(); | ||
if (!userInput) { | ||
if (singleshot) { | ||
console.error(`bun-repl: Option "${$.whiteBright+(evalFlag !== -1 ? '--eval' : '--print')+$.red}" requires a value.`); | ||
console.error(`For more information try ${$.whiteBright}--help${$.reset}`); | ||
process.exit(0); | ||
} else continue; | ||
} | ||
if (userInput === 'undefined') { | ||
console.log($.gray+userInput+$.reset); | ||
continue; | ||
} | ||
userInput ??= replState.historyEntry ? '' : 'undefined'; | ||
if (replState.historyEntry) userInput = replState.historyEntry + userInput; | ||
if (userInput.startsWith('.')) { | ||
if (userInput.startsWith('.') && !singleshot) { | ||
const command = userInput.slice(1).split(/[ \t]+/)[0]; | ||
@@ -79,3 +109,3 @@ switch (command) { | ||
default: | ||
console.error(`Unknown REPL command ".${command}", type ".help" for more information.`); | ||
console.error(`Unknown REPL command "${$.whiteBright}.${command}${$.red}", type "${$.whiteBright}.help${$.red}" for more information.`); | ||
continue MainLoop; | ||
@@ -85,10 +115,3 @@ } | ||
if (userInput.endsWith('\u001B[A')) { replState.historyEntry = replHistory.consume(); continue; } | ||
if (userInput.endsWith('\u001B[B')) { replState.historyEntry = replHistory.unconsume(); continue; } | ||
replState.historyEntry = ''; | ||
replHistory.add(userInput); | ||
replHistory.resetPos(); | ||
let transpiled: string = transpiler.preAdjust(userInput); | ||
let transpiled: string = transpiler.preprocess(userInput); | ||
debuglog($.dim+'preprocess:', transpiled.trim()+$.reset); | ||
@@ -103,3 +126,3 @@ try { | ||
debuglog($.dim+'transpile:', transpiled.trim()+$.reset); | ||
transpiled = transpiler.postAdjust(transpiled); | ||
transpiled = transpiler.postprocess(transpiled); | ||
debuglog($.dim+'postprocess:', transpiled.trim()+$.reset); | ||
@@ -124,3 +147,3 @@ | ||
REPLGlobal.console.error('This is most likely a bug, please report it at ${pkgjson.bugs?.url ?? 'the project\'s GitHub repository'}'); | ||
} else REPLGlobal.console.log(val); | ||
} else if (${!singleshot || printFlag !== -1}) REPLGlobal.console.log(val); | ||
`); | ||
@@ -132,14 +155,4 @@ } catch (error) { | ||
} | ||
} | ||
} while (!singleshot); | ||
function printHelp(): void { | ||
console.log(`Commands: | ||
.help = Print this message. | ||
.info = Print REPL information. | ||
.clear = Clear the screen. | ||
.exit = Exit the REPL. | ||
Press Ctrl+C or Ctrl+Z to forcefully terminate the REPL.`); | ||
} | ||
function printCLIHelp(): void { | ||
@@ -149,19 +162,34 @@ console.log(`Usage: bun-repl [options] | ||
Options: | ||
-h, --help = Display this message. | ||
-v, --version = Show installed bun-repl version. | ||
--update = Check for bun-repl updates. | ||
--debug = Print debug information while running. | ||
-h, --help Display this message. | ||
-v, --version Show installed bun-repl version. | ||
-p, --print <...> Evaluates given code, prints result and exits. | ||
-e, --eval <...> Evaluates given code and silently exits. | ||
--update Check for bun-repl updates. | ||
--debug Print debug information while running. | ||
* Options with <...> as argument must be passed last. | ||
Environment variables: | ||
None`); | ||
BUN_REPL_PROMPT A string to override the default "> " prompt with. | ||
BUN_REPL_MODE Either "sloppy" or "strict" (Not implemented)`); | ||
} | ||
function printHelp(): void { | ||
console.log(`Commands & keybinds: | ||
.help Print this message. | ||
.info Print REPL information. | ||
.clear Clear the screen. ${$.gray}(Ctrl+L)${$.reset} | ||
.exit Exit the REPL. ${$.gray}(Ctrl+C / Ctrl+D)${$.reset}`); | ||
} | ||
function printInfo(): void { | ||
console.log(`bun-repl v${pkgjson.version} | ||
Installed at: ${path.join(import.meta.dir, '..')} | ||
Installed at: ${$.cyan+path.join(import.meta.dir, '..')+$.reset} | ||
Bun version: ${process.version} | ||
SWC version: ${swc.version as string} | ||
Color mode: ${Bun.enableANSIColors} | ||
Debug mode: ${IS_DEBUG} | ||
Current session uptime: ${prettyms(performance.now())}`); | ||
SWC version: v${swc.version as string} | ||
Color mode: ${$.bool(Bun.enableANSIColors)} | ||
Debug mode: ${$.bool(IS_DEBUG)} | ||
Current session uptime: ${$.yellow+prettyms(performance.now())+$.reset}`); | ||
} | ||
process.exit(0); // Prevents a segfault when exiting |
@@ -21,3 +21,3 @@ import swc from '@swc/core'; | ||
// REPL-specific adjustments needed for the code to work in a REPL context. (Ran before transpile) | ||
preAdjust(code: string): string { | ||
preprocess(code: string): string { | ||
return code | ||
@@ -44,3 +44,3 @@ .replace(/import(?:(?:(?:[ \n\t]+([^ *\n\t{},]+)[ \n\t]*(?:,|[ \n\t]+))?([ \n\t]*\{(?:[ \n\t]*[^ \n\t"'{}]+[ \n\t]*,?)+\})?[ \n\t]*)|[ \n\t]*\*[ \n\t]*as[ \n\t]+([^ \n\t{}]+)[ \n\t]+)from[ \n\t]*(?:['"])([^'"\n]+)(['"])/g, | ||
// REPL-specific adjustments needed for the code to work in a REPL context. (Ran after transpile) | ||
postAdjust(code: string): string { | ||
postprocess(code: string): string { | ||
return code | ||
@@ -47,0 +47,0 @@ .replace(/(?:let|const) ([A-Za-z_$\d]+? ?=.)/g, |
@@ -19,6 +19,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ | ||
/** | ||
* Safely call an object's method. | ||
* Safely call a function on an object as the `this`. | ||
* If an error occurs, just treat it as non-existent. | ||
*/ | ||
export function SafeCall<F extends (...x: any[]) => any>(fn: F, thisArg: any, ...args: Parameters<F>): ReturnType<F> | undefined { | ||
export function SafeThiscall<F extends (...x: any[]) => any>(fn: F, thisArg: any, ...args: Parameters<F>): ReturnType<F> | undefined { | ||
try { | ||
@@ -25,0 +25,0 @@ return fn?.apply?.(thisArg, args); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
107940
847
3
3966
19
52
+ Addedrustybun@^0.1.2
+ Added@swc/core@1.10.16(transitive)
+ Added@swc/core-darwin-arm64@1.10.16(transitive)
+ Added@swc/core-darwin-x64@1.10.16(transitive)
+ Added@swc/core-linux-arm-gnueabihf@1.10.16(transitive)
+ Added@swc/core-linux-arm64-gnu@1.10.16(transitive)
+ Added@swc/core-linux-arm64-musl@1.10.16(transitive)
+ Added@swc/core-linux-x64-gnu@1.10.16(transitive)
+ Added@swc/core-linux-x64-musl@1.10.16(transitive)
+ Added@swc/core-win32-arm64-msvc@1.10.16(transitive)
+ Added@swc/core-win32-ia32-msvc@1.10.16(transitive)
+ Added@swc/core-win32-x64-msvc@1.10.16(transitive)
+ Addedrustybun@0.1.2(transitive)
- Removed@swc/core@1.10.18(transitive)
- Removed@swc/core-darwin-arm64@1.10.18(transitive)
- Removed@swc/core-darwin-x64@1.10.18(transitive)
- Removed@swc/core-linux-arm-gnueabihf@1.10.18(transitive)
- Removed@swc/core-linux-arm64-gnu@1.10.18(transitive)
- Removed@swc/core-linux-arm64-musl@1.10.18(transitive)
- Removed@swc/core-linux-x64-gnu@1.10.18(transitive)
- Removed@swc/core-linux-x64-musl@1.10.18(transitive)
- Removed@swc/core-win32-arm64-msvc@1.10.18(transitive)
- Removed@swc/core-win32-ia32-msvc@1.10.18(transitive)
- Removed@swc/core-win32-x64-msvc@1.10.18(transitive)