Security News
RubyGems.org Adds New Maintainer Role
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
@wessberg/ts-evaluator
Advanced tools
An interpreter for Typescript that can evaluate an arbitrary Node within a Typescript AST
@wessberg/ts-evaluator is a TypeScript evaluation library that allows you to evaluate TypeScript code at compile-time. It can be used to execute TypeScript code, inspect the results, and perform various compile-time computations.
Evaluate TypeScript Code
This feature allows you to evaluate TypeScript code and get the result. In this example, a simple addition function is evaluated, and the result is logged.
const { evaluate } = require('@wessberg/ts-evaluator');
const result = evaluate({
code: 'const add = (a: number, b: number) => a + b; add(2, 3);'
});
console.log(result); // Outputs: 5
Inspect TypeScript AST
This feature allows you to inspect the Abstract Syntax Tree (AST) of the evaluated TypeScript code. By setting the `inspect` option to true, you can get detailed information about the code structure.
const { evaluate } = require('@wessberg/ts-evaluator');
const result = evaluate({
code: 'const add = (a: number, b: number) => a + b; add(2, 3);',
inspect: true
});
console.log(result); // Outputs the AST of the evaluated code
Compile-time Computations
This feature allows you to perform complex compile-time computations. In this example, a recursive function to calculate the factorial of a number is evaluated, and the result is logged.
const { evaluate } = require('@wessberg/ts-evaluator');
const result = evaluate({
code: 'const factorial = (n: number): number => n <= 1 ? 1 : n * factorial(n - 1); factorial(5);'
});
console.log(result); // Outputs: 120
ts-node is a TypeScript execution environment and REPL for Node.js. It allows you to run TypeScript code directly without precompiling. Unlike @wessberg/ts-evaluator, ts-node is more focused on runtime execution rather than compile-time evaluation.
The TypeScript compiler itself (typescript) can be used to compile TypeScript code to JavaScript. While it doesn't provide direct evaluation capabilities like @wessberg/ts-evaluator, it is the underlying tool that powers TypeScript code transformation and type checking.
Babel is a JavaScript compiler that can also handle TypeScript through plugins. It focuses on transforming TypeScript code to JavaScript, similar to the TypeScript compiler, but with a broader range of plugins and transformations. It doesn't provide direct evaluation capabilities.
An interpreter for Typescript that can evaluate an arbitrary Node within a Typescript AST
This library is an implementation of an interpreter for Typescript that can evaluate any Expression
, ExpressionStatement
or Declaration
within a Typescript AST.
Rather than interpreting a program, or a sequence of Statement
s, this library takes a Node within an existing AST and evaluates it based on its' lexical environment.
This makes the library an effective companion if you're building a linter, framework, language service, partial evaluator, or something else where you may want to know the computed value of a specific Node at any point in an AST.
One strength of this plugin is that it opens up entirely new use cases such as partial evaluation directly in the editor experience, for example to catch non-syntactic bugs that would only occur on runtime, or more advanced diagnostic for frameworks.
To that end, several policy options can be provided to configure restrictions in terms of what is allowed to be evaluated, such as IO and Network access.
Additionally, ts-evaluator
supports both a Browser environment, a Node environment, and a pure ECMAScript environment. See Setting up an environment for more details.
If you are looking for a Typescript REPL, or a way to execute a full Typescript program, you're looking for something like ts-node instead.
$ npm install @wessberg/ts-evaluator
$ yarn add @wessberg/ts-evaluator
Let's start off with a very basic example:
import {evaluate} from "@wessberg/ts-evaluator";
const result = evaluate({
node: someNode,
typeChecker: someTypeChecker
});
// If a value was produced
if (result.success) {
console.log(result.value);
}
// If an error occurred
else {
console.log(result.reason);
}
In this example, the referenced bindings within the lexical environment of the Node will be discovered and evaluated before producing a final value. This means that you don't have to evaluate the entire program to produce a value which may potentially be a much faster operation.
You can define the kind of environment that evaluate()
assumes when evaluating the given Node. By default, a Node
environment is assumed.
The following environment presets are supported:
ECMA
- Assumes a pure ECMAScript environment. This means that no other globals than those that are defined in the ECMAScript spec such as Math
, Promise
, Object
, etc, are available.NODE
(default) - Assumes a Node environment. This means that built-in modules such as fs
and path
can be resolved, and Node-specific globals such as process
is present.BROWSER
- Assumes a Browser environment. This means that DOM APIs are available and Browser-specific globals such as window
is present.Beyond presets, you can provide additional globals or override those that comes from the presets. Here's how you can configure environment options:
const result = evaluate({
// ...
environment: {
// The "Node" environment is the default one. You can simply omit this key if you are targeting a Node environment
preset: EnvironmentPresetKind.NODE,
extra: {
someGlobal: "someValue"
}
}
});
With great power comes great responsibility. If you are embedding this plugin in, say, a language service plugin to enhance the editing experience in an editor, you may want to apply some restrictions as to what can be evaluated.
By default, IO writes, network calls, and spawning child processes are restricted. You can customize this to your liking:
const result = evaluate({
// ...
policy: {
deterministic: false,
network: false,
console: false,
maxOps: Infinity,
maxOpDuration: Infinity,
io: {
read: true,
write: false
},
process: {
exit: false,
spawnChild: false
}
}
});
Here's an explainer of the individual policies:
deterministic
(default: false
) - If deterministic
is true
, only code constructs that always evaluate to the same value is permitted. This means that things like Math.random()
or new Date()
without arguments, as well as network calls are restricted.
This is useful if you are trying to statically analyze something and need to make sure that the value won't change for each invocation.
network
(default: false
) - If network
is true
, network activity is allowed, such as sending an HTTP request or hooking up a server.
console
(default: false
) - If console
is true
, logging to the console within evaluated code will produce the side-effect of actually logging to the console of the parent process. Usually, this is unwanted, since you're most likely only interested in the
evaluated value, not so much the side-effects, but you can override this behavior by setting console
to true
.
maxOps
(default: Infinity
) - If maxOps
is anything less than Infinity, evaluation will stop when the provided amount of operations has been performed. This is useful to opt-out of running CPU-intensive code, especially if you are embedding this library in an editor or a linter.
maxOpDuration
(default: Infinity
) - If maxOpDuration
is anything less than Infinity, evaluation will stop when the provided amount of milliseconds have passed. This is useful to opt-out of long-running operations, especially if you are embedding this library in an editor or a linter.
io
(default: {read: true, write: false}
) - If io
permits READ
operations, files can be read from disk. If io
permits WRITE
operations, files can be written to disk.
process
(default: {exit: false, spawnChild: false}
) - If process
permits exit
operations, the evaluated code is permitted to exit the parent process. If process
permits spawnChild
operations, the evaluated code is permitted to spawn child processes.
You can get information about the evaluation process with various levels of logging. By default, nothing is logged, but you can override this behavior:
const result = evaluate({
// ...
logLevel: LogLevelKind.DEBUG
});
Here's an explainer of the different log levels:
LogLevelKind.SILENT
(default) - By default, nothing is logged to the console.LogLevelKind.INFO
- Intermediate results are logged to the console.LogLevelKind.VERBOSE
- Everything that is logged with LogLevelKind.INFO
as well as lexical environment bindings are logged to the consoleLogLevelKind.DEBUG
- Everything that is logged with LogLevelKind.VERBOSE
as well as all visited Nodes during evaluation are logged to the consoleYou can tap into the evaluation process with reporting hooks that will be invoked with useful information while an evaluation is in progress. These are useful if you want to understand more about the execution path and work with it programmatically.
const result = evaluate({
// ...
reporting: {
reportBindings: entry => doSomething(entry),
reportTraversal: entry => someArray.push(entry.node),
reportIntermediateResults: entry => doSomeOtherThing(entry),
reportErrors: entry => doSomethingWithError(entry)
}
});
Here's an explainer of the different reporting hooks:
reportBindings(entry: IBindingReportEntry) => void|(Promise<void>)
- Will be invoked for each time a value is bound to the lexical environment of a Node. This is useful to track mutations throughout code execution, for example to understand when and where variables are declared and/or mutated.reportTraversal(entry: ITraversalReportEntry) => void|(Promise<void>)
- Will be invoked for each time a new Node is visited while evaluating. This is useful to track the path through the AST, for example to compute code coverage.reportIntermediateResults(entry: IIntermediateResultReportEntry) => void|(Promise<void>)
- Will be invoked for each intermediate result that has been evaluated before producing a final result. This allows you to work programmatically with all expression values during code execution.reportErrors(entry: IErrorReportEntry) => void|(Promise<void>)
- Will be invoked for each error that is thrown, both when evaluating a result, and for subsequent invocations on, for example, returned function instances. Holds a reference to the error, as well ast the AST node that threw or caused the Error.Do you want to contribute? Awesome! Please follow these recommendations.
Frederik Wessberg Twitter: @FredWessberg Lead Developer |
Become a backer and get your name, avatar, and Twitter handle listed here.
This is, after all, a virtual machine written on top of another virtual machine (V8), which is built in a dynamically typed high-level language (EcmaScript). This library is not built to be
comparable in performance to raw V8 execution speed. However, since ts-evaluator
doesn't require a compile-step and works directly on an AST, for small operations it will most likely be several magnitudes faster than
both ts-node
and compiling to JavaScript with tsc
and executing directly.
MIT © Frederik Wessberg (@FredWessberg) (Website)
FAQs
An interpreter for Typescript that can evaluate an arbitrary Node within a Typescript AST
The npm package @wessberg/ts-evaluator receives a total of 143,559 weekly downloads. As such, @wessberg/ts-evaluator popularity was classified as popular.
We found that @wessberg/ts-evaluator demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.
Security News
Research
Socket's threat research team has detected five malicious npm packages targeting Roblox developers, deploying malware to steal credentials and personal data.