建立 REPL 环境

我们首先来创建一个 REPL 环境,所谓 REPL,是 Read-Evaluate-Print Loop 的简写。 这样我们就可以在终端输入语句,然后查看输出,并不断进行这个循环直到停止使用。

Node.js 的标准库本身提供了一个叫 repl 的模块,我们只需要几行代码,就可以非常迅速的建立起一个 REPL 环境。

import repl from 'repl';

repl.start({
  prompt: 'db.js >> ',
});
import repl from 'repl';

repl.start({
  prompt: 'db.js >> ',
});

默认情况下,它会将输入的语句作为 JavaScript 来解析执行,但我们在这里需要自己来接管这个解析过程,因此需要在启动的参数里传入一个 eval 参数:

repl.start({
  prompt: 'db.js >> ',
  eval: () => {
    callback(null, `Unrecognized command.`);
  },
});
repl.start({
  prompt: 'db.js >> ',
  eval: () => {
    callback(null, `Unrecognized command.`);
  },
});

虽然从代码上看我们对于任何输入都会返回一个 Unrecognized command 的输出,但实际上对于自带的这个 repl 模块,它很贴心的提供了一些内置的特殊命令,你可以通过输入 .help 来查看。应该可以看到下面这样的输出:

db.js >> .help
.break    Sometimes you get stuck, this gets you out
.clear    Break, and also clear the local context
.editor   Enter editor mode
.exit     Exit the REPL
.help     Print this help message
.load     Load JS from a file into the REPL session
.save     Save all evaluated commands in this REPL session to a file

Press Ctrl+C to abort current expression, Ctrl+D to exit the REPL
db.js >> .help
.break    Sometimes you get stuck, this gets you out
.clear    Break, and also clear the local context
.editor   Enter editor mode
.exit     Exit the REPL
.help     Print this help message
.load     Load JS from a file into the REPL session
.save     Save all evaluated commands in this REPL session to a file

Press Ctrl+C to abort current expression, Ctrl+D to exit the REPL

这样我们也不需要自己再去实现退出这样的指令了。

你可以使用 tsc 编译器将其编译为 JavaScript,或者使用 ts-node 这样的工具来直接运行这个文件,就可以跑起来了。为了测试,我们可以试试输入 test 并回车,可以看到下面这样的输出:

db.js >> test
Unrecognized command 'test'.
db.js >> test
Unrecognized command 'test'.

再试试输入 .exit 并回车,就可以退出这个执行了。

eval 函数的签名是这样的:

type REPLEval = (
  this: REPLServer,
  evalCmd: string,
  context: Context,
  file: string,
  cb: (err: Error | null, result: any) => void
) => void;
type REPLEval = (
  this: REPLServer,
  evalCmd: string,
  context: Context,
  file: string,
  cb: (err: Error | null, result: any) => void
) => void;

我们可以拿到用户输入的 evalCmd 和一些上下文信息,通过最后一个回调函数的参数来返回结果并结束这一次循环。 后续我们可以加入通过解析用户的命令来去执行不同的操作并展示结果。

下面我们就来实现一个最简单的 key-value 数据库。