Update ShellDirective to use ts-node interpreter by default

This commit is contained in:
Garrett Mills 2022-08-07 00:47:44 -05:00
parent 710b6cb535
commit b8cf8499d2
2 changed files with 57 additions and 7 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@extollo/lib", "name": "@extollo/lib",
"version": "0.13.3", "version": "0.13.4",
"description": "The framework library that lifts up your code.", "description": "The framework library that lifts up your code.",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",

View File

@ -1,15 +1,25 @@
import {Directive} from '../Directive' import {Directive, OptionDefinition} from '../Directive'
import * as colors from 'colors/safe' import * as colors from 'colors/safe'
import * as repl from 'repl' import * as repl from 'repl'
import {DependencyKey} from '../../di' import * as tsNode from 'ts-node'
import {globalRegistry} from '../../util'
/** /**
* Launch an interactive REPL shell from within the application. * Launch an interactive REPL shell from within the application.
* This is very useful for debugging and testing things during development. * This is very useful for debugging and testing things during development.
*
* By default, the shell launches a TypeScript interpreter, but you can use
* the `--js` flag to get a JavaScript interpreter.
*
* @example
* ```sh
* pnpm cli -- shell
* pnpm cli -- shell --js
* ```
*/ */
export class ShellDirective extends Directive { export class ShellDirective extends Directive {
protected options: any = { protected options: any = {
welcome: `powered by Extollo, © ${(new Date()).getFullYear()} Garrett Mills\nAccess your application using the "app" global.`, welcome: `powered by Extollo, © ${(new Date()).getFullYear()} Garrett Mills\nAccess your application using the "app" global and @extollo/lib using the "lib" global.`,
prompt: `${colors.blue('(')}extollo${colors.blue(') ➤ ')}`, prompt: `${colors.blue('(')}extollo${colors.blue(') ➤ ')}`,
} }
@ -31,17 +41,57 @@ export class ShellDirective extends Directive {
return '' return ''
} }
getOptions(): OptionDefinition[] {
return [
'--js | launch in JavaScript mode instead of TypeScript',
]
}
async handle(): Promise<void> { async handle(): Promise<void> {
const state: any = { const state: any = {
globalRegistry,
app: this.app(), app: this.app(),
lib: await import('../../index'), lib: await import('../../index'),
make: (target: DependencyKey, ...parameters: any[]) => this.make(target, ...parameters), exports: {},
} }
await new Promise<void>(res => { await new Promise<void>(res => {
// Currently, there's no way to programmatically access the async context
// of the REPL from this directive w/o requiring the user to perform manual
// actions. So, instead, override the context on the GlobalRegistry to make
// the current one the global default.
globalRegistry.forceContextOverride()
// Create the ts-node compiler service.
const replService = tsNode.createRepl()
const service = tsNode.create({...replService.evalAwarePartialHost})
replService.setService(service)
// We global these values into the REPL's state directly (using the `state` object
// above), but since we're using a separate ts-node interpreter, we need to make it
// aware of the globals using declaration syntax.
replService.evalCode(`
declare const lib: typeof import('@extollo/lib');
declare const app: typeof lib['Application'];
declare const globalRegistry: typeof lib['globalRegistry'];
`)
// Print the welome message and start the interpreter
this.nativeOutput(this.options.welcome) this.nativeOutput(this.options.welcome)
this.repl = repl.start(this.options.prompt) this.repl = repl.start({
// Causes the REPL to use the ts-node interpreter service:
eval: !this.option('js', false) ? (...args) => replService.nodeEval(...args) : undefined,
prompt: this.options.prompt,
useGlobal: true,
useColors: true,
terminal: true,
preview: true,
})
// Add our globals into the REPL's context
Object.assign(this.repl.context, state) Object.assign(this.repl.context, state)
// Wait for the REPL to exit
this.repl.on('exit', () => res()) this.repl.on('exit', () => res())
}) })
} }