When we write node.js code in Typesript, we need to compile it in TSC and then run it in Node.js, which is a bit of a hassle, so we use TS-Node to run the TS code directly, saving the compilation phase.
Is it amazing how TS-Node runs ts code?
In fact, the principle is not difficult, today let’s implement a TS-Node.
Related basis
Implementing TS-Node requires the following basic knowledge:
- require hook
- Repl module, VM module
- ts compiler api
Let’s start with the basics
require hook
When node.js requires a JS Module, it calls module.load, module._extensions [‘.js’], and module._compile, respectively, before executing it.
The same process applies to ts modules, JSON modules, etc., so we only need to modify the module. _extensions[extensions] method to achieve the purpose of hook:
require.extensions['.ts'] = function(module, filename) {
// Modify the code
module._compile(modified code, filename); }Copy the code
For example, we registered the ts handler function, so that when processing the TS module, this method will be called, so we can compile it in this way, that is how TS-Node can execute ts directly.
Repl module
Node.js provides the repl module to create a command line interaction environment for Read, Evaluate, Print, and Loop in a question-and-answer fashion. Ts-node also supports the REPL mode. You can write TS code directly and then execute it based on the repL module extension.
The REPL API looks like this: to create a repL interaction with the start method, you can specify prompt, and you can implement eval’s processing logic:
const repl = require('repl');
const r = repl.start({
prompt: '-. - > '.eval: myEval
});
function myEval(cmd, context, filename, callback) {
// Process the input command
callback(null, the content after treatment); }Copy the code
Repl execution has a context, in this case r.text, in which we execute code using the VM module:
const vm = require('vm');
constRes = vm.runInContext(code to execute, r.context);Copy the code
Together, these two modules allow for a question-and-answer command line interaction, and ts compilation can be done with eval, allowing direct execution of TS code.
ts compiler api
Ts Compiler we mainly use TSC’s command-line tools, but it also provides a compilation API, called TS Compiler API. We need to directly call the Compiler API to compile the tool.
The API for converting TS code to JS code is this:
const{outputText} = ts. TranspileModule (ts code, {compilerOptions: {
strict: false.sourceMap: false.// Other compilation options}});Copy the code
Of course, TS also provides a type-checking API, which will be expanded in a later article because of the large number of parameters. Here, transpileModule’s API is enough.
After understanding the three aspects of require Hook, REPL and VM, AND TS Compiler API, the implementation principle of TS-Node is clearly outlined. Let’s implement it.
Implement ts – node
Direct execution mode
You can use ts-node + a TS file to execute the ts file directly by modifying the require hook (module._extensions [‘.ts’]).
Do ts compilation in require hook, and then directly execute the compiled JS, so as to achieve the effect of directly execute TS file.
So we override the module._extensions [‘.ts’] method to read the contents of the file, then call TS.transpilemodule to convert ts to JS, and then call module._compile to process the compiled JS.
In this way, we can execute the TS module directly. The specific module path is executed through the command line argument, which can be obtained with process.argv.
const path = require('path');
const ts = require('typescript');
const fs = require('fs');
const filePath = process.argv[2];
require.extensions['.ts'] = function(module, filename) {
const fileFullPath = path.resolve(__dirname, filename);
const content = fs.readFileSync(fileFullPath, 'utf-8');
const { outputText } = ts.transpileModule(content, {
compilerOptions: require('./tsconfig.json')});module._compile(outputText, filename);
}
require(filePath);
Copy the code
We prepare a ts file like test.ts:
const a = 1;
const b = 2;
function add(a: number, b: number) :number {
return a + b;
}
console.log(add(a, b));
Copy the code
Then use the tool hook. Js to run:
As you can see, TS is successfully executed, which is how TS-Node works.
Of course, there is more logic in detail, but the main principle is the Require Hook + TS Compiler API.
Repl mode
Ts-node allows you to start a repL environment, enter ts code and execute it interactively. It is based on an extension to the Node.js repl module, which compiles TS in a custom eval function. The API of vm.runInContext is then used to execute the JS code in the context of the REPL.
We also start a repl environment and set up the prompt and custom eval implementation.
const repl = require('repl');
const r = repl.start({
prompt: '-. - > '.eval: myEval
});
function myEval(cmd, context, filename, callback) {}Copy the code
The eval implementation is to compile the ts code as js and execute the compiled JS code with vm.runInContext, specifying the context of the repl:
function myEval(cmd, context, filename, callback) {
const { outputText } = ts.transpileModule(cmd, {
compilerOptions: {
strict: false.sourceMap: false}});const res = vm.runInContext(outputText, r.context);
callback(null, res);
}
Copy the code
We can also extend the repL context by injecting a who environment variable:
Object.defineProperty(r.context, 'who', {
configurable: false.enumerable: true.value: 'God said, let there be light.'
});
Copy the code
Let’s test the effect:
As you can see, a repL environment is launched with the prompt changed to -.- >, and you can execute ts code directly, as well as access the global variable who.
This is the general principle behind ts-Node’s REPL mode: REPL + VM + TS Compiler API.
The full code is as follows:
const repl = require('repl');
const ts = require('typescript');
const vm = require('vm');
const r = repl.start({
prompt: '-. - > '.eval: myEval
});
Object.defineProperty(r.context, 'who', {
configurable: false.enumerable: true.value: 'God said, let there be light.'
});
function myEval(cmd, context, filename, callback) {
const { outputText } = ts.transpileModule(cmd, {
compilerOptions: {
strict: false.sourceMap: false}});const res = vm.runInContext(outputText, r.context);
callback(null, res);
}
Copy the code
conclusion
Ts-node can execute TS code directly without manual compilation. To understand this, we implemented a simple TS-Node that supports both direct execution and REPL modes.
The principle of direct execution is to use the TS Compiler API in the Module. _Extensions [ext] to convert the code and then execute it. The effect is to execute ts code directly.
Repl is an extension of node.js’s repl module that allows you to customize prompts, context, eval logic, etc. We compiled it in EVAL using the TS Compiler API. The compiled JS is then executed in the repL’s context via vm.runInContext. The effect is that you can execute ts code directly in the REPL.
Of course, there are many more details to complete TS-Node, but we already know the general principles, as well as the Require Hooks, THE REPL and VM modules, and the TS Compiler API.
digression
In fact, the principle of TS-Node is written at the request of a classmate, you have to read nodeJS tool source can also tell me (can add my wechat), free to provide source code with read + simple implementation of the service, but will do some screening.