Skip to content

Commit

Permalink
feat: make passing in a typechecker optional and document its operation
Browse files Browse the repository at this point in the history
  • Loading branch information
wessberg committed Jul 22, 2022
1 parent 7e5078c commit 3966d4b
Show file tree
Hide file tree
Showing 8 changed files with 15 additions and 10 deletions.
5 changes: 5 additions & 0 deletions README.md
Expand Up @@ -162,6 +162,11 @@ else {
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.

### Behavior without a typechecker

If you do not have access to a typechecker, for example if you don't have a TypeScript _Program_ to work with, you can avoid passing in a typechecker as an option.
This will work for evaluating literal values, but `ts-evaluator` won't be able to resolve and dealias symbols and identifiers.

### Setting up an environment

You can define the kind of environment that `evaluate()` assumes when evaluating the given Node. By default, a CommonJS-based `Node` environment is assumed, to align with what you would get simply by running `node` with no arguments.
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/evaluate-options.ts
Expand Up @@ -6,7 +6,7 @@ import {TS} from "../type/ts.js";

export interface EvaluateOptions {
node: TS.Statement | TS.Declaration | TS.Expression;
typeChecker: TS.TypeChecker;
typeChecker?: TS.TypeChecker;
typescript?: typeof TS;
environment?: Partial<IEnvironment>;
logLevel?: LogLevelKind;
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/evaluator/evaluate-binary-expression.ts
Expand Up @@ -148,7 +148,7 @@ export function evaluateBinaryExpression(options: EvaluatorOptions<TS.BinaryExpr
// Update to the left-value within the environment if it exists there and has been updated
if (leftIdentifier != null) {
const innerLeftIdentifier = getInnerNode(node.left, typescript);
const leftIdentifierSymbol = typeChecker.getSymbolAtLocation(innerLeftIdentifier);
const leftIdentifierSymbol = typeChecker?.getSymbolAtLocation(innerLeftIdentifier);
const leftIdentifierValueDeclaration = leftIdentifierSymbol?.valueDeclaration;

const bestLexicalEnvironment =
Expand Down
4 changes: 2 additions & 2 deletions src/interpreter/evaluator/evaluate-enum-member.ts
Expand Up @@ -6,13 +6,13 @@ import {TS} from "../../type/ts.js";
* Evaluates, or attempts to evaluate, an EnumMember
*/
export function evaluateEnumMember({node, typeChecker, evaluate, environment, statementTraversalStack}: EvaluatorOptions<TS.EnumMember>, parent: IndexLiteral): void {
const constantValue = typeChecker.getConstantValue(node) as number | string;
const constantValue = typeChecker?.getConstantValue(node);
const propertyName = evaluate.nodeWithValue(node.name, environment, statementTraversalStack) as IndexLiteralKey;

// If it is a String enum, all keys will be initialized to strings
if (typeof constantValue === "string") {
parent[propertyName] = constantValue;
} else {
parent[(parent[propertyName] = constantValue)] = propertyName;
parent[(parent[propertyName] = constantValue ?? 0)] = propertyName;
}
}
6 changes: 3 additions & 3 deletions src/interpreter/evaluator/evaluate-identifier.ts
Expand Up @@ -24,14 +24,14 @@ export function evaluateIdentifier(options: EvaluatorOptions<TS.Identifier | TS.

// Try to get a symbol for whatever the identifier points to and take its value declaration.
// It may not have a symbol, for example if it is an inlined expression such as an initializer or a Block
const symbol = typescript.isShorthandPropertyAssignment(node.parent) ? typeChecker.getShorthandAssignmentValueSymbol(node.parent) : typeChecker.getSymbolAtLocation(node);
const symbol = typescript.isShorthandPropertyAssignment(node.parent) ? typeChecker?.getShorthandAssignmentValueSymbol(node.parent) : typeChecker?.getSymbolAtLocation(node);
let valueDeclaration: TS.Declaration | undefined = symbol == null ? undefined : symbol.valueDeclaration;

if (symbol != null && valueDeclaration == null) {
try {
// The symbol may be aliasing another one - which may have a value declaration
const aliasedSymbol = typeChecker.getAliasedSymbol(symbol);
valueDeclaration = aliasedSymbol.valueDeclaration;
const aliasedSymbol = typeChecker?.getAliasedSymbol(symbol);
valueDeclaration = aliasedSymbol?.valueDeclaration;
} catch {
// OK, it didn't alias anything
}
Expand Down
Expand Up @@ -12,7 +12,7 @@ export function evaluateSourceFileAsNamespaceObject({node, environment, evaluate
const objectCtor = getFromLexicalEnvironment(node, environment, "Object")!.literal as ObjectConstructor;
const namespaceObject: IndexLiteral = objectCtor.create(objectCtor.prototype);

const moduleSymbol = typeChecker.getSymbolAtLocation(node);
const moduleSymbol = typeChecker?.getSymbolAtLocation(node);
if (moduleSymbol != null) {
const exports = moduleSymbol.exports;
if (exports != null) {
Expand Down
2 changes: 1 addition & 1 deletion src/interpreter/evaluator/evaluator-options.ts
Expand Up @@ -9,7 +9,7 @@ import {TS} from "../../type/ts.js";

export interface EvaluatorOptions<T extends TS.Node | TS.NodeArray<TS.Node>> {
node: T;
typeChecker: TS.TypeChecker;
typeChecker?: TS.TypeChecker;
typescript: typeof TS;
evaluate: NodeEvaluator;
environment: LexicalEnvironment;
Expand Down
Expand Up @@ -5,7 +5,7 @@ import {ReportingOptionsSanitized} from "../../reporting/i-reporting-options.js"
import {TS} from "../../../type/ts.js";

export interface ICreateNodeEvaluatorOptions {
typeChecker: TS.TypeChecker;
typeChecker?: TS.TypeChecker;
typescript: typeof TS;
policy: EvaluatePolicySanitized;
reporting: ReportingOptionsSanitized;
Expand Down

0 comments on commit 3966d4b

Please sign in to comment.