diff --git a/src/bun/mod.ts b/src/bun/mod.ts index 1084bb8..65197ad 100644 --- a/src/bun/mod.ts +++ b/src/bun/mod.ts @@ -3,10 +3,9 @@ */ import { IRuntime, RunTimeType } from '../common/runtime.ts'; -import TerminalIO from '../tsx/terminal_io.ts'; -import FileIO from '../tsx/file_io.ts'; - -import process from 'node:process'; +import { process } from '../common/runtime/node.ts'; +import TerminalIO from '../common/runtime/terminal_io.ts'; +import FileIO from '../common/runtime/file_io.ts'; /** * The Bun Runtime implementation diff --git a/src/bun/test_base.ts b/src/bun/test_base.ts index 89dc27c..edd965d 100644 --- a/src/bun/test_base.ts +++ b/src/bun/test_base.ts @@ -1,34 +1,19 @@ /** * Adapt the bun test interface to the shared testing interface */ -import { deepStrictEqual, notStrictEqual, strictEqual } from 'node:assert'; import { describe, test } from 'bun:test'; -import { ITestBase } from '../common/types.ts'; - -export function testSuite(testObj: any) { - Object.keys(testObj).forEach((group) => { - describe(group, () => { - const groupObj = testObj[group]; - Object.keys(groupObj).forEach((testName) => { - test(testName, groupObj[testName]); +import AbstractTestBase from '../common/runtime/test_base.ts'; +class BunTestBase extends AbstractTestBase { + public static testSuite(testObj: any): void { + Object.keys(testObj).forEach((group) => { + describe(group, () => { + const groupObj = testObj[group]; + Object.keys(groupObj).forEach((testName) => { + test(testName, groupObj[testName]); + }); }); }); - }); + } } -const BunTestBase: ITestBase = { - assertEquivalent: (actual: unknown, expected: unknown) => - deepStrictEqual(actual, expected), - assertExists: (actual: unknown) => notStrictEqual(actual, undefined), - assertFalse: (actual: boolean) => strictEqual(actual, false), - assertInstanceOf: (actual: unknown, expectedType: any) => - strictEqual(actual instanceof expectedType, true), - assertNotEquals: (actual: unknown, expected: unknown) => - notStrictEqual(actual, expected), - assertEquals: (actual: unknown, expected: unknown) => - strictEqual(actual, expected), - assertTrue: (actual: boolean) => strictEqual(actual, true), - testSuite, -}; - export default BunTestBase; diff --git a/src/common/document.ts b/src/common/document.ts index 97ba79a..90560bd 100644 --- a/src/common/document.ts +++ b/src/common/document.ts @@ -1,7 +1,7 @@ import Row from './row.ts'; import { arrayInsert, maxAdd, minSub } from './fns.ts'; import Option, { None, Some } from './option.ts'; -import { getRuntime, logDebug } from './runtime.ts'; +import { getRuntime, logDebug, logWarning } from './runtime.ts'; import { Position, SearchDirection } from './types.ts'; export class Document { @@ -17,7 +17,7 @@ export class Document { this.dirty = false; } - get numRows(): number { + public get numRows(): number { return this.#rows.length; } @@ -66,6 +66,10 @@ export class Document { direction: SearchDirection = SearchDirection.Forward, ): Option { if (at.y >= this.numRows) { + logWarning('Trying to search beyond the end of the current file', { + at, + document: this, + }); return None; } diff --git a/src/common/main.ts b/src/common/main.ts index 8b616db..dbc2d62 100644 --- a/src/common/main.ts +++ b/src/common/main.ts @@ -1,6 +1,6 @@ -import process from 'node:process'; +import { process } from './runtime/node.ts'; import { readKey } from './fns.ts'; -import { getRuntime, logError } from './runtime.ts'; +import { getRuntime, logError, logWarning } from './runtime.ts'; import Editor from './editor.ts'; export async function main() { @@ -43,7 +43,9 @@ export async function main() { for await (const char of term.inputLoop()) { const parsed = readKey(char); if (char.length === 0 || parsed.length === 0) { - continue; + logWarning('Empty input returned from runtime input loop'); + + return; } // Process input diff --git a/src/common/runtime.ts b/src/common/runtime.ts index 8f05b95..3597cc6 100644 --- a/src/common/runtime.ts +++ b/src/common/runtime.ts @@ -1,7 +1,7 @@ /** * Functions/Methods that depend on the current runtime to function */ -import process from 'node:process'; +import { process } from './runtime/node.ts'; import Ansi from './ansi.ts'; import { IRuntime, ITerminalSize, ITestBase } from './types.ts'; import { noop } from './fns.ts'; diff --git a/src/tsx/file_io.ts b/src/common/runtime/file_io.ts similarity index 85% rename from src/tsx/file_io.ts rename to src/common/runtime/file_io.ts index 03b4040..e810782 100644 --- a/src/tsx/file_io.ts +++ b/src/common/runtime/file_io.ts @@ -1,9 +1,9 @@ import { appendFile, readFile, writeFile } from 'node:fs/promises'; import { resolve } from 'node:path'; -import { IFileIO } from '../common/runtime.ts'; +import { IFileIO } from '../runtime.ts'; -const TsxFileIO: IFileIO = { +const CommonFileIO: IFileIO = { openFile: async function (path: string): Promise { const filePath = resolve(path); const contents = await readFile(filePath, { encoding: 'utf8' }); @@ -22,4 +22,4 @@ const TsxFileIO: IFileIO = { }, }; -export default TsxFileIO; +export default CommonFileIO; diff --git a/src/common/runtime/node.ts b/src/common/runtime/node.ts new file mode 100644 index 0000000..095eaf1 --- /dev/null +++ b/src/common/runtime/node.ts @@ -0,0 +1,3 @@ +import * as process from 'node:process'; + +export { process }; diff --git a/src/tsx/terminal_io.ts b/src/common/runtime/terminal_io.ts similarity index 70% rename from src/tsx/terminal_io.ts rename to src/common/runtime/terminal_io.ts index 667f852..b270ddd 100644 --- a/src/tsx/terminal_io.ts +++ b/src/common/runtime/terminal_io.ts @@ -1,12 +1,11 @@ -import process from 'node:process'; +import { process } from './node.ts'; +import { readKey } from '../fns.ts'; +import { ITerminal, ITerminalSize } from '../types.ts'; -import { readKey } from '../common/fns.ts'; -import { ITerminal, ITerminalSize } from '../common/types.ts'; - -const TsxTerminalIO: ITerminal = { +const CommonTerminalIO: ITerminal = { argv: (process.argv.length > 2) ? process.argv.slice(2) : [], inputLoop: async function* (): AsyncGenerator { - yield (await TsxTerminalIO.readStdinRaw()) ?? new Uint8Array(0); + yield (await CommonTerminalIO.readStdinRaw()) ?? new Uint8Array(0); return null; }, @@ -19,7 +18,7 @@ const TsxTerminalIO: ITerminal = { }); }, readStdin: async function (): Promise { - const raw = await TsxTerminalIO.readStdinRaw(); + const raw = await CommonTerminalIO.readStdinRaw(); return readKey(raw ?? new Uint8Array(0)); }, readStdinRaw: function (): Promise { @@ -38,4 +37,4 @@ const TsxTerminalIO: ITerminal = { }, }; -export default TsxTerminalIO; +export default CommonTerminalIO; diff --git a/src/common/runtime/test_base.ts b/src/common/runtime/test_base.ts new file mode 100644 index 0000000..9424be0 --- /dev/null +++ b/src/common/runtime/test_base.ts @@ -0,0 +1,88 @@ +/** + * Adapt the node test interface to the shared testing interface + */ +import { deepStrictEqual, notStrictEqual, strictEqual } from 'node:assert'; +import Option from '../option.ts'; + +export interface ITestBase { + assertEquivalent(actual: unknown, expected: unknown): void; + assertExists(actual: unknown): void; + assertInstanceOf(actual: unknown, expectedType: any): void; + assertNotEquals(actual: unknown, expected: unknown): void; + assertEquals(actual: unknown, expected: unknown): void; + assertTrue(actual: boolean): void; + assertFalse(actual: boolean): void; + assertSome(actual: Option): void; + assertNone(actual: Option): void; + /** + * Convert the nested test object into a test suite for the current runtime + */ + testSuite(testObj: any): void; +} + +abstract class AbstractTestBase implements Partial { + /** + * The values (often objects) have all the same property values + */ + public static assertEquivalent(actual: unknown, expected: unknown): void { + return deepStrictEqual(actual, expected); + } + + /** + * The value is not null or undefined + */ + public static assertExists(actual: unknown): void { + return notStrictEqual(actual, undefined); + } + + /** + * `actual` is an object implementing `expectedType` + */ + public static assertInstanceOf(actual: unknown, expectedType: any): void { + return strictEqual(actual instanceof expectedType, true); + } + + /** + * The values are not exactly equal (Different instance, type, value, etc) + */ + public static assertNotEquals(actual: unknown, expected: unknown): void { + return notStrictEqual(actual, expected); + } + + /** + * The values are exactly the same + */ + public static assertEquals(actual: unknown, expected: unknown): void { + return strictEqual(actual, expected); + } + + /** + * The value is true + */ + public static assertTrue(actual: boolean): void { + return strictEqual(actual, true); + } + + /** + * The value is false + */ + public static assertFalse(actual: boolean): void { + return strictEqual(actual, false); + } + + /** + * The value is a `Some` type `Option` + */ + public static assertSome(actual: Option): void { + return AbstractTestBase.assertTrue(actual.isSome()); + } + + /** + * The value is a `None` type `Option` + */ + public static assertNone(actual: Option): void { + return AbstractTestBase.assertTrue(actual.isNone()); + } +} + +export default AbstractTestBase; diff --git a/src/common/types.ts b/src/common/types.ts index ad7f03f..9f62d23 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -1,6 +1,7 @@ import { RunTimeType } from './runtime.ts'; export { Position } from './position.ts'; +export type { ITestBase } from './runtime/test_base.ts'; /** * The size of terminal in rows and columns @@ -128,66 +129,3 @@ export interface IRuntime { */ exit(code?: number): void; } - -// ---------------------------------------------------------------------------- -// Testing -// ---------------------------------------------------------------------------- - -/** - * The shared test interface, so tests can be run by both runtimes - */ -export interface ITestBase { - /** - * The values (often objects) have all the same property values - * - * @param actual - * @param expected - */ - assertEquivalent(actual: unknown, expected: unknown): void; - - /** - * The value is not null or undefined - * - * @param actual - */ - assertExists(actual: unknown): void; - - /** - * The value is false - * - * @param actual - */ - assertFalse(actual: boolean): void; - - /** - * `actual` is an object implementing `expectedType` - * - * @param actual - * @param expectedType - */ - assertInstanceOf(actual: unknown, expectedType: any): void; - - /** - * The values are not exactly equal (Different instance, type, value, etc) - * - * @param actual - * @param expected - */ - assertNotEquals(actual: unknown, expected: unknown): void; - - /** - * The values are exactly the same - * - * @param actual - * @param expected - */ - assertEquals(actual: unknown, expected: unknown): void; - - /** - * The value is true - * - * @param actual - */ - assertTrue(actual: boolean): void; - testSuite(testObj: any): void; -} diff --git a/src/deno/test_base.ts b/src/deno/test_base.ts index 99b236a..461c941 100644 --- a/src/deno/test_base.ts +++ b/src/deno/test_base.ts @@ -1,31 +1,15 @@ -import { ITestBase } from '../common/types.ts'; -import { stdAssert } from './deps.ts'; -const { - assertEquals, - assertExists, - assertInstanceOf, - assertNotEquals, - assertStrictEquals, -} = stdAssert; - -export function testSuite(testObj: any) { - Object.keys(testObj).forEach((group) => { - const groupObj = testObj[group]; - Object.keys(groupObj).forEach((testName) => { - Deno.test(testName, groupObj[testName]); +// @ts-ignore The import exists, but tsc complains +import { test } from 'node:test'; +import AbstractTestBase from '../common/runtime/test_base.ts'; +class DenoTestBase extends AbstractTestBase { + public static testSuite(testObj: any) { + Object.keys(testObj).forEach((group) => { + const groupObj = testObj[group]; + Object.keys(groupObj).forEach((testName) => { + test(testName, groupObj[testName]); + }); }); - }); + } } -const DenoTestBase: ITestBase = { - assertEquivalent: assertEquals, - assertExists, - assertInstanceOf, - assertNotEquals, - assertEquals: assertStrictEquals, - assertTrue: (actual: boolean) => assertStrictEquals(actual, true), - assertFalse: (actual: boolean) => assertStrictEquals(actual, false), - testSuite, -}; - export default DenoTestBase; diff --git a/src/tsx/mod.ts b/src/tsx/mod.ts index 6635e68..fe7904e 100644 --- a/src/tsx/mod.ts +++ b/src/tsx/mod.ts @@ -2,17 +2,16 @@ * The main entrypoint when using Tsx as the runtime */ import { IRuntime, RunTimeType } from '../common/runtime.ts'; -import TsxTerminalIO from './terminal_io.ts'; -import TsxFileIO from './file_io.ts'; - -import process from 'node:process'; +import { process } from '../common/runtime/node.ts'; +import TsxTerminalIO from '../common/runtime/terminal_io.ts'; +import FileIO from '../common/runtime/file_io.ts'; /** * The Tsx Runtime implementation */ const TsxRuntime: IRuntime = { name: RunTimeType.Tsx, - file: TsxFileIO, + file: FileIO, term: TsxTerminalIO, onEvent: (eventName: string, handler) => process.on(eventName, handler), onExit: (cb: () => void): void => { diff --git a/src/tsx/test_base.ts b/src/tsx/test_base.ts index 49eb714..bac42d3 100644 --- a/src/tsx/test_base.ts +++ b/src/tsx/test_base.ts @@ -1,39 +1,21 @@ -// @ts-nocheck: Bun is used for typescript checks, bun does not have 'node:assert' or 'node:test' /** * Adapt the node test interface to the shared testing interface */ -import { - deepStrictEqual, - notStrictEqual, - strictEqual, -} from 'node:assert/strict'; +// @ts-ignore The import exists, but tsc complains import { describe, it } from 'node:test'; -import { ITestBase } from '../common/types.ts'; +import AbstractTestBase from '../common/runtime/test_base.ts'; -export function testSuite(testObj: any) { - Object.keys(testObj).forEach((group) => { - describe(group, () => { - const groupObj = testObj[group]; - Object.keys(groupObj).forEach((testName) => { - it(testName, groupObj[testName]); +class TsxTestBase extends AbstractTestBase { + public static testSuite(testObj: any): void { + Object.keys(testObj).forEach((group) => { + describe(group, () => { + const groupObj = testObj[group]; + Object.keys(groupObj).forEach((testName) => { + it(testName, groupObj[testName]); + }); }); }); - }); + } } -const TsxTestBase: ITestBase = { - assertEquivalent: (actual: unknown, expected: unknown) => - deepStrictEqual(actual, expected), - assertExists: (actual: unknown) => notStrictEqual(actual, undefined), - assertFalse: (actual: boolean) => strictEqual(actual, false), - assertInstanceOf: (actual: unknown, expectedType: any) => - strictEqual(actual instanceof expectedType, true), - assertNotEquals: (actual: unknown, expected: unknown) => - notStrictEqual(actual, expected), - assertEquals: (actual: unknown, expected: unknown) => - strictEqual(actual, expected), - assertTrue: (actual: boolean) => strictEqual(actual, true), - testSuite, -}; - export default TsxTestBase; diff --git a/tsconfig.json b/tsconfig.json index a803573..392800a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,8 @@ "skipLibCheck": true, "composite": true, "downlevelIteration": true, - "allowSyntheticDefaultImports": true + "esModuleInterop": false, + "allowSyntheticDefaultImports": false }, "exclude": ["src/deno"] }