Add new runtime for tsx
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good

This commit is contained in:
Timothy Warren 2024-07-05 15:51:30 -04:00
parent e656ad3112
commit 8d2ba868b0
11 changed files with 174 additions and 7 deletions

View File

@ -9,7 +9,7 @@ coverage: bun-test deno-coverage
check: deno-check bun-check
docs:
deno doc --html --unstable-ffi --name="Scroll" ./src/scroll.ts ./src/common/mod.ts ./src/deno/mod.ts ./src/bun/mod.ts
deno doc --html --unstable-ffi --name="Scroll" ./src/scroll.ts ./src/common/*.ts ./src/deno/mod.ts ./src/bun/mod.ts
# Reformat the code
fmt:
@ -23,6 +23,7 @@ quality: check test
# Clean up any generated files
clean:
rm -f test.file
rm -rf .deno-cover
rm -rf coverage
rm -rf docs
@ -66,3 +67,16 @@ deno-coverage:
# Run with deno
deno-run file="":
deno run --allow-all --allow-ffi --deny-hrtime --unstable-ffi ./src/scroll.ts {{file}}
##########################################################################################
# tsx(Node JS)-specific commands
##########################################################################################
# Test with tsx (NodeJS)
tsx-test:
npx tsx --test './src/common/all_test.ts'
# Run with tsx (NodeJS)
tsx-run file="":
npx tsx ./src/scroll.ts {{file}}

View File

@ -2,5 +2,8 @@
"dependencies": {},
"devDependencies": {
"bun-types": "^1.0.11"
}
},
"scripts": {
},
"type": "module"
}

View File

@ -6,8 +6,11 @@ import { IRuntime, RunTimeType } from '../common/runtime.ts';
import BunTerminalIO from './terminal_io.ts';
import BunFileIO from './file_io.ts';
import * as process from 'node:process';
import process from 'node:process';
/**
* The Bun Runtime implementation
*/
const BunRuntime: IRuntime = {
name: RunTimeType.Bun,
file: BunFileIO,

View File

@ -28,7 +28,7 @@ export function readKey(raw: Uint8Array): string {
return parsed;
}
// Some keycodes have multiple potential inputs
// Some key codes have multiple potential inputs
switch (parsed) {
case '\x1b[1~':
case '\x1b[7~':

View File

@ -13,7 +13,7 @@ enum OptionType {
const isOption = <T>(v: any): v is Option<T> => v instanceof Option;
class OptionInnerNone<T> {
class OptionInnerNone {
public type: OptionType = OptionType.None;
}
@ -23,7 +23,7 @@ class OptionInnerSome<T> {
constructor(public value: T) {}
}
type OptionInnerType<T> = OptionInnerNone<T> | OptionInnerSome<T>;
type OptionInnerType<T> = OptionInnerNone | OptionInnerSome<T>;
const isSome = <T>(v: OptionInnerType<T>): v is OptionInnerSome<T> =>
'value' in v && v.type === OptionType.Some;
@ -50,6 +50,9 @@ export class Option<T> {
: new OptionInnerNone();
}
/**
* The equivalent of the Rust `Option`.`None` type
*/
public static get None(): Option<any> {
return Option._None;
}

View File

@ -1,3 +1,6 @@
/**
* Functions/Methods that depend on the current runtime to function
*/
import process from 'node:process';
import { IRuntime, ITestBase } from './types.ts';
import { noop } from './fns.ts';
@ -11,9 +14,13 @@ export type { IFileIO, IRuntime, ITerminal } from './types.ts';
export enum RunTimeType {
Bun = 'bun',
Deno = 'deno',
Tsx = 'tsx',
Unknown = 'common',
}
/**
* The label for type/severity of the log entry
*/
export enum LogLevel {
Debug = 'Debug',
Info = 'Info',
@ -62,7 +69,7 @@ export function die(s: string | Error): void {
* Determine which Typescript runtime we are operating under
*/
export function runtimeType(): RunTimeType {
let runtime = RunTimeType.Unknown;
let runtime = RunTimeType.Tsx;
if ('Deno' in globalThis) {
runtime = RunTimeType.Deno;

View File

@ -7,6 +7,9 @@ import DenoFileIO from './file_io.ts';
import * as node_process from 'node:process';
/**
* The Deno Runtime implementation
*/
const DenoRuntime: IRuntime = {
name: RunTimeType.Deno,
file: DenoFileIO,

25
src/tsx/file_io.ts Normal file
View File

@ -0,0 +1,25 @@
import { appendFile, readFile, writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import { IFileIO } from '../common/runtime.ts';
const TsxFileIO: IFileIO = {
openFile: async function (path: string): Promise<string> {
const filePath = resolve(path);
const contents = await readFile(filePath, { encoding: 'utf8' });
return contents;
},
appendFile: async function (path: string, contents: string): Promise<void> {
const filePath = resolve(path);
await appendFile(filePath, contents);
},
saveFile: async function (path: string, contents: string): Promise<void> {
const filePath = resolve(path);
await writeFile(filePath, contents);
},
};
export default TsxFileIO;

26
src/tsx/mod.ts Normal file
View File

@ -0,0 +1,26 @@
/**
* 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';
/**
* The Tsx Runtime implementation
*/
const TsxRuntime: IRuntime = {
name: RunTimeType.Tsx,
file: TsxFileIO,
term: TsxTerminalIO,
onEvent: (eventName: string, handler) => process.on(eventName, handler),
onExit: (cb: () => void): void => {
process.on('beforeExit', cb);
process.on('exit', cb);
process.on('SIGINT', cb);
},
exit: (code?: number) => process.exit(code),
};
export default TsxRuntime;

42
src/tsx/terminal_io.ts Normal file
View File

@ -0,0 +1,42 @@
import process from 'node:process';
import { readKey } from '../common/fns.ts';
import { ITerminal, ITerminalSize } from '../common/types.ts';
const TsxTerminalIO: ITerminal = {
argv: (process.argv.length > 2) ? process.argv.slice(2) : [],
inputLoop: async function* (): AsyncGenerator<Uint8Array, null> {
yield (await TsxTerminalIO.readStdinRaw()) ?? new Uint8Array(0);
return null;
},
getTerminalSize: function getTerminalSize(): Promise<ITerminalSize> {
const [cols, rows] = process.stdout.getWindowSize();
return Promise.resolve({
rows,
cols,
});
},
readStdin: async function (): Promise<string | null> {
const raw = await TsxTerminalIO.readStdinRaw();
return readKey(raw ?? new Uint8Array(0));
},
readStdinRaw: function (): Promise<Uint8Array | null> {
return new Promise((resolve) => {
process.stdin.setRawMode(true).resume().once(
'data',
(buffer: Uint8Array) => {
resolve(buffer);
process.stdin.setRawMode(false);
},
);
});
},
writeStdout: function (s: string): Promise<void> {
process.stdout.write(s);
return Promise.resolve();
},
};
export default TsxTerminalIO;

41
src/tsx/test_base.ts Normal file
View File

@ -0,0 +1,41 @@
/**
* Adapt the node test interface to the shared testing interface
*/
// @ts-ignore: Only in newer versions of node
import {
deepStrictEqual,
notStrictEqual,
strictEqual,
} from 'node:assert/strict';
// @ts-ignore: Only in newer versions of node
import { describe, it } from 'node: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) => {
it(testName, groupObj[testName]);
});
});
});
}
const TsxTestBase: ITestBase = {
assertEquals: (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),
assertNull: (actual: unknown) => strictEqual(actual, null),
assertStrictEquals: (actual: unknown, expected: unknown) =>
strictEqual(actual, expected),
assertTrue: (actual: boolean) => strictEqual(actual, true),
testSuite,
};
export default TsxTestBase;