diff --git a/src/bun/file_io.ts b/src/bun/file_io.ts index a48a9b3..20d8297 100644 --- a/src/bun/file_io.ts +++ b/src/bun/file_io.ts @@ -1,18 +1,16 @@ -if (!('Bun' in globalThis)) throw new Error('This module requires Bun'); - import { IFileIO } from '../common/runtime.ts'; import { appendFile } from 'node:fs/promises'; const BunFileIO: IFileIO = { openFile: async (path: string): Promise => { - const file = await globalThis.Bun.file(path); + const file = await Bun.file(path); return await file.text(); }, appendFile: async function (path: string, contents: string): Promise { return await appendFile(path, contents); }, saveFile: async function (path: string, contents: string): Promise { - await globalThis.Bun.write(path, contents); + await Bun.write(path, contents); return; }, }; diff --git a/src/bun/mod.ts b/src/bun/mod.ts index e2363b6..743d89d 100644 --- a/src/bun/mod.ts +++ b/src/bun/mod.ts @@ -1,4 +1,3 @@ -if (!('Bun' in globalThis)) throw new Error('This module requires Bun'); /** * The main entrypoint when using Bun as the runtime */ diff --git a/src/bun/terminal_io.ts b/src/bun/terminal_io.ts index 1792fe6..b7971af 100644 --- a/src/bun/terminal_io.ts +++ b/src/bun/terminal_io.ts @@ -1,4 +1,3 @@ -if (!('Bun' in globalThis)) throw new Error('This module requires Bun'); /** * Wrap the runtime-specific hook into stdin */ diff --git a/src/bun/test_base.ts b/src/bun/test_base.ts index b9d7f3d..312acde 100644 --- a/src/bun/test_base.ts +++ b/src/bun/test_base.ts @@ -1,4 +1,3 @@ -if (!('Bun' in globalThis)) throw new Error('This module requires Bun'); /** * Adapt the bun test interface to the shared testing interface */ diff --git a/src/common/editor.ts b/src/common/editor.ts index 020157c..9058c46 100644 --- a/src/common/editor.ts +++ b/src/common/editor.ts @@ -8,170 +8,108 @@ import { ctrlKey, isControl, maxAdd, + none, posSub, + readKey, some, - strlen, truncate, } from './fns.ts'; -import { log, LogLevel } from './runtime.ts'; +import { getRuntime, log, LogLevel } from './runtime.ts'; import { ITerminalSize, Position } from './types.ts'; -export enum EditorMode { - Normal = 'Normal', - Find = 'Find', - Save = 'Save', - Prompt = 'Prompt', -} - -class Prompt { - private constructor( - public basePrompt: string, - private callback: (query: string, char: string) => void, - public key: string = '', - public answer: string = '', - ) {} - - public static from( - basePrompt: string, - callback: (query: string, char: string) => void = ( - query: string, - key: string, - ) => { - log('Default prompt callback', LogError.Warning); - }, - ): Prompt { - return new Prompt(basePrompt, callback); - } - - public backspace(): void { - this.answer = truncate(this.answer, strlen(this.answer) - 1); - } - - public append(char: string): void { - this.answer += char; - } - - public cb(): void { - this.callback(this.answer, this.key); - } - - public render(): string { - if (this.basePrompt.includes('%s')) { - return this.basePrompt.replace('%s', this.answer); - } else { - return `${this.basePrompt}${this.answer}`; - } - } -} - class Editor { - /** - * How to handle the stdin stream - * @private - */ - public mode: EditorMode = EditorMode.Normal; /** * The document being edited * @private */ - public document: Document; + #document: Document; /** * The output buffer for the terminal * @private */ - protected buffer: Buffer; + #buffer: Buffer; /** * The size of the screen in rows/columns * @private */ - protected screen: ITerminalSize; + #screen: ITerminalSize; /** * The current location of the mouse cursor * @private */ - protected cursor: Position; + #cursor: Position; /** * The current scrolling offset */ - protected offset: Position; + #offset: Position; /** * The scrolling offset for the rendered row * @private */ - protected renderX: number = 0; + #renderX: number = 0; /** * The name of the currently open file * @private */ - protected filename: string = ''; - /** - * Current input prompt state - */ - public _prompt: Prompt | null = null; + #filename: string = ''; /** * A message to display at the bottom of the screen * @private */ - public statusMessage: string = ''; + #statusMessage: string = ''; /** * Timeout for status messages * @private */ - protected statusTimeout: number = 0; + #statusTimeout: number = 0; /** * The number of times required to quit a dirty document * @private */ - protected quitTimes: number = SCROLL_QUIT_TIMES; + #quitTimes: number = SCROLL_QUIT_TIMES; constructor(terminalSize: ITerminalSize) { - this.buffer = new Buffer(); + this.#buffer = new Buffer(); // Subtract two rows from the terminal size // for displaying the status bar // and message bar - this.screen = terminalSize; - this.screen.rows -= 2; + this.#screen = terminalSize; + this.#screen.rows -= 2; - this.cursor = Position.default(); - this.offset = Position.default(); - this.document = Document.default(); + this.#cursor = Position.default(); + this.#offset = Position.default(); + this.#document = Document.default(); } private get numRows(): number { - return this.document.numRows; + return this.#document.numRows; } private get currentRow(): Row | null { - return this.document.row(this.cursor.y); + return this.#document.row(this.#cursor.y); } public async open(filename: string): Promise { - await this.document.open(filename); - this.filename = filename; + await this.#document.open(filename); + this.#filename = filename; return this; } public async save(): Promise { - if (this.filename !== '') { - await this.document.save(this.filename); - this.setStatusMessage(`${this.filename} was saved to disk.`); - return; + if (this.#filename === '') { + const filename = await this.prompt('Save as: %s (ESC to cancel)'); + if (filename === null) { + this.setStatusMessage('Save aborted'); + return; + } + + this.#filename = filename; } - this.prompt('Save as: %s (ESC to cancel)', (name: string, key: string) => { - if (key === KeyCommand.Enter) { - this.mode = EditorMode.Normal; - this.filename = name; - return this.save(); - } - - if (name === null || key === KeyCommand.Escape) { - this.mode = EditorMode.Normal; - this.setStatusMessage('Save aborted'); - } - }); + await this.#document.save(this.#filename); + this.setStatusMessage(`${this.#filename} was saved to disk.`); } // -------------------------------------------------------------------------- @@ -183,96 +121,204 @@ class Editor { * @param input - the decoded chunk of stdin */ public async processKeyPress(input: string): Promise { - switch (this.mode) { - case EditorMode.Find: - log(this, LogLevel.Debug); - // this._prompt = Prompt.from('Search: %s (Use ESC/Arrows/Enter)'); - this.find(); - this.processPromptKeyPress(input); - // this.find(); - return true; + switch (input) { + // ---------------------------------------------------------------------- + // Ctrl-key chords + // ---------------------------------------------------------------------- + case ctrlKey('f'): + await this.find(); + break; - case EditorMode.Save: - log(this, LogLevel.Debug); - this.save(); - this.processPromptKeyPress(input); - // this.save(); - return true; + case ctrlKey('s'): + await this.save(); + break; - case EditorMode.Prompt: - return this.processPromptKeyPress(input); + case ctrlKey('q'): + if (this.#quitTimes > 0 && this.#document.dirty) { + this.setStatusMessage( + 'WARNING!!! File has unsaved changes. ' + + `Press Ctrl-Q ${this.#quitTimes} more times to quit.`, + ); + this.#quitTimes--; + return true; + } + await this.clearScreen(); + return false; - case EditorMode.Normal: // fall through - default: - return this.processNormalKeyPress(input); - } + // ---------------------------------------------------------------------- + // Movement keys + // ---------------------------------------------------------------------- - await this.refreshScreen(); - } + case KeyCommand.Home: + this.#cursor.x = 0; + break; - public prompt( - p: string, - callback: (query: string, char: string) => void, - ): string | null { - if (this._prompt === null) { - this._prompt = Prompt.from(p, callback); - } - - switch (this._prompt.key) { - // End the prompt - case KeyCommand.Escape: - this.mode = EditorMode.Normal; - - this.setStatusMessage(''); - return null; - - // Return the input and end the prompt - case KeyCommand.Enter: - this.mode = EditorMode.Normal; - - if (this._prompt.answer.length > 0) { - this.setStatusMessage(''); - return this._prompt.answer; + case KeyCommand.End: + if (this.currentRow !== null) { + this.#cursor.x = this.currentRow.size; } break; - default: - // Nothing to do here + case KeyCommand.PageUp: + case KeyCommand.PageDown: + { + if (input === KeyCommand.PageUp) { + this.#cursor.y = this.#offset.y; + } else if (input === KeyCommand.PageDown) { + this.#cursor.y = maxAdd( + this.#offset.y, + this.#screen.rows - 1, + this.numRows, + ); + } + + let times = this.#screen.rows; + while (times--) { + this.moveCursor( + input === KeyCommand.PageUp + ? KeyCommand.ArrowUp + : KeyCommand.ArrowDown, + ); + } + } + break; + + case KeyCommand.ArrowUp: + case KeyCommand.ArrowDown: + case KeyCommand.ArrowRight: + case KeyCommand.ArrowLeft: + this.moveCursor(input); + break; + + // ---------------------------------------------------------------------- + // Text manipulation keys + // ---------------------------------------------------------------------- + + case KeyCommand.Enter: + this.#document.insertNewline(this.#cursor); + this.#cursor.x = 0; + this.#cursor.y++; + break; + + case KeyCommand.Delete: + this.#document.delete(this.#cursor); + break; + + case KeyCommand.Backspace: + { + if (this.#cursor.x > 0 || this.#cursor.y > 0) { + this.moveCursor(KeyCommand.ArrowLeft); + this.#document.delete(this.#cursor); + } + } + break; + + // ---------------------------------------------------------------------- + // Direct input + // ---------------------------------------------------------------------- + + default: { + if (!this.shouldFilter(input)) { + this.#document.insert(this.#cursor, input); + this.#cursor.x++; + } + } } - return this._prompt.answer ?? null; + if (this.#quitTimes < SCROLL_QUIT_TIMES) { + this.#quitTimes = SCROLL_QUIT_TIMES; + this.setStatusMessage(''); + } + + return true; + } + + public async prompt( + p: string, + callback?: (query: string, char: string) => void, + ): Promise { + const { term } = await getRuntime(); + + let res = ''; + const maybeCallback = (query: string, char: string) => { + if (callback !== undefined) { + callback(query, char); + } + }; + + outer: while (true) { + if (p.includes('%s')) { + this.setStatusMessage(p.replace('%s', res)); + } else { + this.setStatusMessage(`${p}${res}`); + } + + await this.refreshScreen(); + for await (const chunk of term.inputLoop()) { + const char = readKey(chunk); + if (none(char)) { + continue; + } + + switch (char) { + // Remove the last character from the prompt input + case KeyCommand.Backspace: + case KeyCommand.Delete: + res = truncate(res, res.length - 1); + maybeCallback(res, char); + continue outer; + + // End the prompt + case KeyCommand.Escape: + this.setStatusMessage(''); + maybeCallback(res, char); + + return null; + + // Return the input and end the prompt + case KeyCommand.Enter: + if (res.length > 0) { + this.setStatusMessage(''); + maybeCallback(res, char); + return res; + } + break; + + // Add to the prompt result + default: + if (!isControl(char)) { + res += char; + } + } + + maybeCallback(res, char); + } + } } /** * Find text within the document. This is roughly equivalent to the * `editorFindCallback` function in the kilo tutorial. */ - public find(): void { - const savedCursor = Position.from(this.cursor); - const savedOffset = Position.from(this.offset); + public async find(): Promise { + const savedCursor = Position.from(this.#cursor); + const savedOffset = Position.from(this.#offset); - this.prompt( + const query = await this.prompt( 'Search: %s (Use ESC/Arrows/Enter)', (query: string, key: string) => { if (key === KeyCommand.Enter || key === KeyCommand.Escape) { - this.mode = EditorMode.Normal; if (key === KeyCommand.Escape) { - this.document.resetFind(); - // Return to document position before search - // when you cancel the search (press the escape key) - if (query === null) { - this.cursor = Position.from(savedCursor); - this.offset = Position.from(savedOffset); - } + this.#document.resetFind(); } return null; } - if (query.length > 0) { - const pos = this.document.find(query, key); + if (some(query) && query.length > 0) { + const pos = this.#document.find(query, key); if (pos !== null) { // We have a match here - this.cursor = pos; + this.#cursor = pos; this.scroll(); } else { this.setStatusMessage('Not found'); @@ -280,6 +326,13 @@ class Editor { } }, ); + + // Return to document position before search + // when you cancel the search (press the escape key) + if (query === null) { + this.#cursor = Position.from(savedCursor); + this.#offset = Position.from(savedOffset); + } } /** @@ -309,61 +362,63 @@ class Editor { private moveCursor(char: string): void { switch (char) { case KeyCommand.ArrowLeft: - if (this.cursor.x > 0) { - this.cursor.x--; - } else if (this.cursor.y > 0) { - this.cursor.y--; - this.cursor.x = (this.currentRow !== null) ? this.currentRow.size : 0; + if (this.#cursor.x > 0) { + this.#cursor.x--; + } else if (this.#cursor.y > 0) { + this.#cursor.y--; + this.#cursor.x = (this.currentRow !== null) + ? this.currentRow.size + : 0; } break; case KeyCommand.ArrowRight: if ( - this.currentRow !== null && this.cursor.x < this.currentRow.size + this.currentRow !== null && this.#cursor.x < this.currentRow.size ) { - this.cursor.x++; + this.#cursor.x++; } else if ( this.currentRow !== null && - this.cursor.x === this.currentRow.size + this.#cursor.x === this.currentRow.size ) { - this.cursor.y++; - this.cursor.x = 0; + this.#cursor.y++; + this.#cursor.x = 0; } break; case KeyCommand.ArrowUp: - if (this.cursor.y > 0) { - this.cursor.y--; + if (this.#cursor.y > 0) { + this.#cursor.y--; } break; case KeyCommand.ArrowDown: - if (this.cursor.y < this.numRows) { - this.cursor.y++; + if (this.#cursor.y < this.numRows) { + this.#cursor.y++; } break; } const rowLen = this.currentRow?.size ?? 0; - if (this.cursor.x > rowLen) { - this.cursor.x = rowLen; + if (this.#cursor.x > rowLen) { + this.#cursor.x = rowLen; } } private scroll(): void { - this.renderX = 0; + this.#renderX = 0; if (this.currentRow !== null) { - this.renderX = this.currentRow.cxToRx(this.cursor.x); + this.#renderX = this.currentRow.cxToRx(this.#cursor.x); } - if (this.cursor.y < this.offset.y) { - this.offset.y = this.cursor.y; + if (this.#cursor.y < this.#offset.y) { + this.#offset.y = this.#cursor.y; } - if (this.cursor.y >= this.offset.y + this.screen.rows) { - this.offset.y = this.cursor.y - this.screen.rows + 1; + if (this.#cursor.y >= this.#offset.y + this.#screen.rows) { + this.#offset.y = this.#cursor.y - this.#screen.rows + 1; } - if (this.renderX < this.offset.x) { - this.offset.x = this.renderX; + if (this.#renderX < this.#offset.x) { + this.#offset.x = this.#renderX; } - if (this.renderX >= this.offset.x + this.screen.cols) { - this.offset.x = this.renderX - this.screen.cols + 1; + if (this.#renderX >= this.#offset.x + this.#screen.cols) { + this.#offset.x = this.#renderX - this.#screen.cols + 1; } } @@ -373,8 +428,8 @@ class Editor { public setStatusMessage(msg: string): void { // TODO: consider some sort of formatting for passed strings - this.statusMessage = msg; - this.statusTimeout = Date.now(); + this.#statusMessage = msg; + this.#statusTimeout = Date.now(); } /** @@ -382,281 +437,106 @@ class Editor { */ public async refreshScreen(): Promise { this.scroll(); - this.buffer.append(Ansi.HideCursor); - this.buffer.append(Ansi.ResetCursor); + this.#buffer.append(Ansi.HideCursor); + this.#buffer.append(Ansi.ResetCursor); this.drawRows(); this.drawStatusBar(); this.drawMessageBar(); - this.buffer.append( + this.#buffer.append( Ansi.moveCursor( - this.cursor.y - this.offset.y, - this.renderX - this.offset.x, + this.#cursor.y - this.#offset.y, + this.#renderX - this.#offset.x, ), ); - this.buffer.append(Ansi.ShowCursor); + this.#buffer.append(Ansi.ShowCursor); - await this.buffer.flush(); + await this.#buffer.flush(); } - public async clearScreen(): Promise { - this.buffer.append(Ansi.ClearScreen); - this.buffer.append(Ansi.ResetCursor); + private async clearScreen(): Promise { + this.#buffer.append(Ansi.ClearScreen); + this.#buffer.append(Ansi.ResetCursor); - await this.buffer.flush(); + await this.#buffer.flush(); } private drawRows(): void { - for (let y = 0; y < this.screen.rows; y++) { - this.buffer.append(Ansi.ClearLine); - const fileRow = y + this.offset.y; + for (let y = 0; y < this.#screen.rows; y++) { + this.#buffer.append(Ansi.ClearLine); + const fileRow = y + this.#offset.y; if (fileRow >= this.numRows) { this.drawPlaceholderRow(fileRow); } else { this.drawFileRow(fileRow); } - this.buffer.appendLine(); + this.#buffer.appendLine(); } } private drawFileRow(y: number): void { - const row = this.document.row(y); + const row = this.#document.row(y); if (row === null) { log(`Trying to draw non-existent row '${y}'`, LogLevel.Warning); return this.drawPlaceholderRow(y); } const len = Math.min( - posSub(row.rsize, this.offset.x), - this.screen.cols, + posSub(row.rsize, this.#offset.x), + this.#screen.cols, ); - this.buffer.append(row.render(this.offset.x, len)); + this.#buffer.append(row.render(this.#offset.x, len)); } private drawPlaceholderRow(y: number): void { - if (y === Math.trunc(this.screen.rows / 2) && this.document.isEmpty()) { + if (y === Math.trunc(this.#screen.rows / 2) && this.#document.isEmpty()) { const message = `Scroll editor -- version ${SCROLL_VERSION}`; - const messageLen = (message.length > this.screen.cols) - ? this.screen.cols + const messageLen = (message.length > this.#screen.cols) + ? this.#screen.cols : message.length; - let padding = Math.trunc((this.screen.cols - messageLen) / 2); + let padding = Math.trunc((this.#screen.cols - messageLen) / 2); if (padding > 0) { - this.buffer.append('~'); + this.#buffer.append('~'); padding -= 1; - this.buffer.append(' '.repeat(padding)); + this.#buffer.append(' '.repeat(padding)); } - this.buffer.append(message, messageLen); + this.#buffer.append(message, messageLen); } else { - this.buffer.append('~'); + this.#buffer.append('~'); } } private drawStatusBar(): void { - this.buffer.append(Ansi.InvertColor); - const name = (this.filename !== '') ? this.filename : '[No Name]'; - const modified = (this.document.dirty) ? '(modified)' : ''; + this.#buffer.append(Ansi.InvertColor); + const name = (this.#filename !== '') ? this.#filename : '[No Name]'; + const modified = (this.#document.dirty) ? '(modified)' : ''; const status = `${truncate(name, 20)} - ${this.numRows} lines ${modified}`; - const rStatus = `${this.cursor.y + 1}/${this.numRows}`; - let len = Math.min(status.length, this.screen.cols); - this.buffer.append(status, len); + const rStatus = `${this.#cursor.y + 1}/${this.numRows}`; + let len = Math.min(status.length, this.#screen.cols); + this.#buffer.append(status, len); - while (len < this.screen.cols) { - if (this.screen.cols - len === rStatus.length) { - this.buffer.append(rStatus); + while (len < this.#screen.cols) { + if (this.#screen.cols - len === rStatus.length) { + this.#buffer.append(rStatus); break; } else { - this.buffer.append(' '); + this.#buffer.append(' '); len++; } } - this.buffer.appendLine(Ansi.ResetFormatting); + this.#buffer.appendLine(Ansi.ResetFormatting); } private drawMessageBar(): void { - this.buffer.append(Ansi.ClearLine); - const msgLen = this.statusMessage.length; - if (msgLen > 0 && (Date.now() - this.statusTimeout < 5000)) { - this.buffer.append(this.statusMessage, this.screen.cols); + this.#buffer.append(Ansi.ClearLine); + const msgLen = this.#statusMessage.length; + if (msgLen > 0 && (Date.now() - this.#statusTimeout < 5000)) { + this.#buffer.append(this.#statusMessage, this.#screen.cols); } } - - // -------------------------------------------------------------------------- - // Terminal input parsing - // -------------------------------------------------------------------------- - - private async processNormalKeyPress(input: string): Promise { - switch (input) { - // ---------------------------------------------------------------------- - // Ctrl-key chords - // ---------------------------------------------------------------------- - case ctrlKey('f'): - this.mode = EditorMode.Find; - // break; - return this.processKeyPress(input); - - case ctrlKey('s'): - this.mode = EditorMode.Save; - return this.processKeyPress(input); - - // await this.save(); - // break; - - case ctrlKey('q'): - if (this.quitTimes > 0 && this.document.dirty) { - this.setStatusMessage( - 'WARNING!!! File has unsaved changes. ' + - `Press Ctrl-Q ${this.quitTimes} more times to quit.`, - ); - this.quitTimes--; - return true; - } - await this.clearScreen(); - return false; - - // ---------------------------------------------------------------------- - // Movement keys - // ---------------------------------------------------------------------- - - case KeyCommand.Home: - this.cursor.x = 0; - break; - - case KeyCommand.End: - if (this.currentRow !== null) { - this.cursor.x = this.currentRow.size; - } - break; - - case KeyCommand.PageUp: - case KeyCommand.PageDown: - { - if (input === KeyCommand.PageUp) { - this.cursor.y = this.offset.y; - } else if (input === KeyCommand.PageDown) { - this.cursor.y = maxAdd( - this.offset.y, - this.screen.rows - 1, - this.numRows, - ); - } - - let times = this.screen.rows; - while (times--) { - this.moveCursor( - input === KeyCommand.PageUp - ? KeyCommand.ArrowUp - : KeyCommand.ArrowDown, - ); - } - } - break; - - case KeyCommand.ArrowUp: - case KeyCommand.ArrowDown: - case KeyCommand.ArrowRight: - case KeyCommand.ArrowLeft: - this.moveCursor(input); - break; - - // ---------------------------------------------------------------------- - // Text manipulation keys - // ---------------------------------------------------------------------- - - case KeyCommand.Enter: - this.document.insertNewline(this.cursor); - this.cursor.x = 0; - this.cursor.y++; - break; - - case KeyCommand.Delete: - this.document.delete(this.cursor); - break; - - case KeyCommand.Backspace: - { - if (this.cursor.x > 0 || this.cursor.y > 0) { - this.moveCursor(KeyCommand.ArrowLeft); - this.document.delete(this.cursor); - } - } - break; - - // ---------------------------------------------------------------------- - // Direct input - // ---------------------------------------------------------------------- - - default: { - if (!this.shouldFilter(input)) { - this.document.insert(this.cursor, input); - this.cursor.x++; - } - } - } - - if (this.quitTimes < SCROLL_QUIT_TIMES) { - this.quitTimes = SCROLL_QUIT_TIMES; - this.setStatusMessage(''); - } - - return true; - } - - private async processPromptKeyPress(char: string): Promise { - log(char, LogLevel.Debug); - log(this, LogLevel.Debug); - - if (this._prompt === null) { - log('Prompt should not be null here', LogLevel.Warning); - this.mode = EditorMode.Normal; - - return true; - } - - this.setStatusMessage(this._prompt.render()); - await this.refreshScreen(); - - this._prompt.key = char; - switch (char) { - // Remove the last character from the prompt input - case KeyCommand.Backspace: - case KeyCommand.Delete: - this._prompt.backspace(); - this._prompt.cb(); - break; - - // End the prompt - case KeyCommand.Escape: - this.mode = EditorMode.Normal; - - this.setStatusMessage(''); - this._prompt.cb(); - break; - - // Return the input and end the prompt - case KeyCommand.Enter: - this.mode = EditorMode.Normal; - - if (this._prompt.answer.length > 0) { - this.setStatusMessage(''); - this._prompt.cb(); - } - break; - - // Add to the prompt result - default: - if (!isControl(char)) { - this._prompt.append(char); - } - } - - // this.setStatusMessage(this._prompt.render()); - // await this.refreshScreen(); - - return true; - } } export default Editor; diff --git a/src/common/main.ts b/src/common/main.ts index 98228ec..8b616db 100644 --- a/src/common/main.ts +++ b/src/common/main.ts @@ -51,6 +51,9 @@ export async function main() { if (!shouldLoop) { return; } + + // Render output + await editor.refreshScreen(); } } } diff --git a/src/common/runtime.ts b/src/common/runtime.ts index 2e7fae6..96dcb2d 100644 --- a/src/common/runtime.ts +++ b/src/common/runtime.ts @@ -30,7 +30,7 @@ let scrollRuntime: IRuntime | null = null; export function log(s: unknown, level: LogLevel = LogLevel.Notice): void { getRuntime().then(({ file }) => { - const raw = JSON.stringify(s, null, 2); + const raw = typeof s === 'string' ? s : JSON.stringify(s, null, 2); const output = `${level}: ${raw}\n`; const outputFile = (level === LogLevel.Error) @@ -85,16 +85,13 @@ export async function getRuntime(): Promise { const pkg = await import(path); if ('default' in pkg) { scrollRuntime = pkg.default; + if (scrollRuntime !== null) { + return Promise.resolve(scrollRuntime); + } } - - if (scrollRuntime !== null) { - return Promise.resolve(scrollRuntime); - } - - return Promise.reject('Missing default import'); } - return Promise.resolve(scrollRuntime); + return Promise.reject('Missing default import'); } /** diff --git a/src/deno/file_io.ts b/src/deno/file_io.ts index ab71f86..c81194d 100644 --- a/src/deno/file_io.ts +++ b/src/deno/file_io.ts @@ -1,7 +1,3 @@ -if (!('Deno' in globalThis)) { - throw new Error('This module requires Deno to run'); -} - import { IFileIO } from '../common/runtime.ts'; const DenoFileIO: IFileIO = { diff --git a/src/deno/mod.ts b/src/deno/mod.ts index 09fea74..c5a48f8 100644 --- a/src/deno/mod.ts +++ b/src/deno/mod.ts @@ -1,6 +1,3 @@ -if (!('Deno' in globalThis)) { - throw new Error('This module requires Deno to run'); -} /** * The main entrypoint when using Deno as the runtime */ diff --git a/src/deno/terminal_io.ts b/src/deno/terminal_io.ts index f22e070..7032393 100644 --- a/src/deno/terminal_io.ts +++ b/src/deno/terminal_io.ts @@ -1,6 +1,3 @@ -if (!('Deno' in globalThis)) { - throw new Error('This module requires Deno to run'); -} import { readKey } from '../common/fns.ts'; import { ITerminal, ITerminalSize } from '../common/types.ts'; diff --git a/src/deno/test_base.ts b/src/deno/test_base.ts index 57a7be3..0ebaeac 100644 --- a/src/deno/test_base.ts +++ b/src/deno/test_base.ts @@ -1,6 +1,3 @@ -if (!('Deno' in globalThis)) { - throw new Error('This module requires Deno to run'); -} import { ITestBase } from '../common/types.ts'; import { stdAssert } from './deps.ts'; const {