Add new runtime for tsx
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good
This commit is contained in:
parent
e656ad3112
commit
8d2ba868b0
16
justfile
16
justfile
@ -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}}
|
||||
|
||||
|
@ -2,5 +2,8 @@
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"bun-types": "^1.0.11"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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~':
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
25
src/tsx/file_io.ts
Normal 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
26
src/tsx/mod.ts
Normal 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
42
src/tsx/terminal_io.ts
Normal 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
41
src/tsx/test_base.ts
Normal 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;
|
Loading…
Reference in New Issue
Block a user