Skip to content

Commit

Permalink
feat: add
Browse files Browse the repository at this point in the history
  • Loading branch information
wessberg committed Jul 23, 2022
1 parent a601460 commit e121d66
Show file tree
Hide file tree
Showing 53 changed files with 435 additions and 328 deletions.
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ If you are looking for a Typescript REPL, or a way to _execute_ a full Typescrip

[Become a sponsor/backer](https://github.com/wessberg/ts-evaluator?sponsor=1) and get your logo listed here.

| <a href="https://usebubbles.com"><img alt="Bubbles" src="https://uploads-ssl.webflow.com/5d682047c28b217055606673/5e5360be16879c1d0dca6514_icon-thin-128x128%402x.png" height="70" /></a> | <a href="https://github.com/cblanc"><img alt="Christopher Blanchard" src="https://avatars0.githubusercontent.com/u/2160685?s=400&v=4" height="70" /></a> | <a href="https://github.com/ideal-postcodes"><img alt="Ideal Postcodes" src="https://avatars.githubusercontent.com/u/4996310?s=200&v=4" height="70" /></a> | <a href="https://www.xerox.com"><img alt="Xerox" src="https://avatars.githubusercontent.com/u/9158512?s=200&v=4" height="70" /></a> | <a href="https://changelog.me"><img alt="Trent Raymond" src="https://avatars.githubusercontent.com/u/1509616?v=4" height="70" /></a> | <a href="https://scrubtheweb.com"><img alt="scrubtheweb" src="https://avatars.githubusercontent.com/u/41668218?v=4" height="70" /></a> | <a href="https://github.com/hjoelh"><img alt="Joel" src="https://avatars.githubusercontent.com/u/68335961?v=4" height="70" /></a> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| [Bubbles](https://usebubbles.com)<br><strong>Twitter</strong>: [@usebubbles](https://twitter.com/usebubbles) | [Christopher Blanchard](https://github.com/cblanc) | [Ideal Postcodes](https://github.com/ideal-postcodes) | [Xerox](https://www.xerox.com) | [Trent Raymond](https://changelog.me) | [scrubtheweb](https://scrubtheweb.com) | [Joel](https://github.com/hjoelh) |
| <a href="https://usebubbles.com"><img alt="Bubbles" src="https://uploads-ssl.webflow.com/5d682047c28b217055606673/5e5360be16879c1d0dca6514_icon-thin-128x128%402x.png" height="70" /></a> | <a href="https://www.xerox.com"><img alt="Xerox" src="https://avatars.githubusercontent.com/u/9158512?s=200&v=4" height="70" /></a> | <a href="https://changelog.me"><img alt="Trent Raymond" src="https://avatars.githubusercontent.com/u/1509616?v=4" height="70" /></a> | <a href="https://scrubtheweb.com"><img alt="scrubtheweb" src="https://avatars.githubusercontent.com/u/41668218?v=4" height="70" /></a> | <a href="https://github.com/hjoelh"><img alt="Joel" src="https://avatars.githubusercontent.com/u/68335961?v=4" height="70" /></a> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| [Bubbles](https://usebubbles.com)<br><strong>Twitter</strong>: [@usebubbles](https://twitter.com/usebubbles) | [Xerox](https://www.xerox.com) | [Trent Raymond](https://changelog.me) | [scrubtheweb](https://scrubtheweb.com) | [Joel](https://github.com/hjoelh) |

### Patreon

Expand Down Expand Up @@ -95,6 +95,7 @@ If you are looking for a Typescript REPL, or a way to _execute_ a full Typescrip
- [Custom TypeScript version](#custom-typescript-version)
- [Logging](#logging)
- [Reporting](#reporting)
- [Module overrides](#module-overrides)
- [Contributing](#contributing)
- [Maintainers](#maintainers)
- [FAQ](#faq)
Expand Down Expand Up @@ -298,6 +299,23 @@ Here's an explainer of the different reporting hooks:
- `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.

### Module overrides

Sometimes, evaluating identifiers require resolving identifiers across source files. When a typechecker is passed in, so long as the identifier is resolvable as part of the compilation unit,
this won't ever be an issue. However, when a type checker is not passed in, or in case you want to override the result of requiring an external module, you can use the `moduleOverrides` option.

For example, you may want to pass in as shim for a built-in module.
To use it, pass a record where the keys are module specifiers and the value is what requiring that module should resolve to. For example:

```ts
{
fs: {
readFileSync: () => {},
// ...
}
}
```

<!-- SHADOW_SECTION_CONTRIBUTING_START -->

## Contributing
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"@types/semver": "^7.3.10",
"@typescript-eslint/eslint-plugin": "^5.30.7",
"@typescript-eslint/parser": "^5.30.7",
"@wessberg/ts-config": "^2.0.3",
"@wessberg/ts-config": "^2.0.4",
"@wessberg/prettier-config": "^1.0.0",
"rollup-plugin-ts": "3.0.2",
"ava": "^3.15.0",
Expand All @@ -62,7 +62,7 @@
"husky": "^8.0.1",
"memfs": "^3.4.7",
"np": "7.6.2",
"npm-check-updates": "^15.3.4",
"npm-check-updates": "^16.0.0",
"pnpm": "^7.6.0",
"prettier": "^2.7.1",
"pretty-quick": "^3.1.3",
Expand Down
34 changes: 17 additions & 17 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions src/interpreter/evaluate-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,12 @@ export interface EvaluateOptions {
logLevel?: LogLevelKind;
policy?: Partial<EvaluatePolicy>;
reporting?: ReportingOptions;

/**
* A record of implementations for module specifiers that will override whatever is resolvable via
* traditional require(...) evaluation.
* Useful when/if you want to shim other modules inside the compilation unit contex of the evaluation,
* much like local identifiers can be overridden with the `environment` option.
*/
moduleOverrides?: Record<string, unknown>;
}
2 changes: 2 additions & 0 deletions src/interpreter/evaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export function evaluate({
typeChecker,
node,
environment: {preset = "NODE", extra = {}} = {},
moduleOverrides = {},
typescript = TSModule,
logLevel = LogLevelKind.SILENT,
policy: {
Expand Down Expand Up @@ -100,6 +101,7 @@ export function evaluate({
typescript,
logger,
stack,
moduleOverrides,
reporting: reporting,
nextNode: nextNode => (currentNode = nextNode)
});
Expand Down
1 change: 1 addition & 0 deletions src/interpreter/evaluator/evaluator-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface EvaluatorOptions<T extends TS.Node | TS.NodeArray<TS.Node>> {
environment: LexicalEnvironment;
policy: EvaluatePolicySanitized;
reporting: ReportingOptionsSanitized;
moduleOverrides?: Record<string, unknown>;
stack: Stack;
statementTraversalStack: StatementTraversalStack;
logger: Logger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {TS} from "../../../type/ts.js";
/**
* Creates a Node Evaluator
*/
export function createNodeEvaluator({typeChecker, typescript, policy, logger, stack, reporting, nextNode}: ICreateNodeEvaluatorOptions): NodeEvaluator {
export function createNodeEvaluator({typeChecker, typescript, policy, logger, stack, reporting, nextNode, moduleOverrides}: ICreateNodeEvaluatorOptions): NodeEvaluator {
let ops = 0;

const handleNewNode = (node: TS.Node, statementTraversalStack: StatementTraversalStack) => {
Expand Down Expand Up @@ -101,7 +101,8 @@ export function createNodeEvaluator({typeChecker, typescript, policy, logger, st
environment,
stack,
logger,
statementTraversalStack
statementTraversalStack,
moduleOverrides
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface ICreateNodeEvaluatorOptions {
typescript: typeof TS;
policy: EvaluatePolicySanitized;
reporting: ReportingOptionsSanitized;
moduleOverrides?: Record<string, unknown>;
logger: Logger;
stack: Stack;
nextNode(node: TS.Node): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,22 @@ export function getImplementationForDeclarationWithinDeclarationFile(options: Ev

try {
// eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/no-var-requires
const module = require(moduleDeclaration.name.text);
const module = options.moduleOverrides?.[moduleDeclaration.name.text] ?? require(moduleDeclaration.name.text);
return typescript.isModuleDeclaration(node) ? module : module[name] ?? module;
} catch (ex) {
if (ex instanceof EvaluationError) throw ex;
else throw new ModuleNotFoundError({node: moduleDeclaration, path: moduleDeclaration.name.text});
}
}


export function getImplementationFromExternalFile(name: string, moduleSpecifier: string, options: EvaluatorOptions<TS.Node>): Literal {
export function getImplementationFromExternalFile(name: string, moduleSpecifier: string, options: EvaluatorOptions<TS.Node>): Literal {
const {node} = options;

const require = getFromLexicalEnvironment(node, options.environment, "require")!.literal as NodeRequire;

try {
// eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/no-var-requires
const module = require(moduleSpecifier);
const module = options.moduleOverrides?.[moduleSpecifier] ?? require(moduleSpecifier);
return module[name] ?? module.default ?? module;
} catch (ex) {
if (ex instanceof EvaluationError) throw ex;
Expand Down
16 changes: 8 additions & 8 deletions test/array-binding-pattern/array-binding-pattern.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import test from "ava";
import {executeProgram} from "../setup/execute-program.js";
import {withTypeScript} from "../setup/ts-macro.js";

test("Can handle ArrayBindingPatterns in VariableDeclarations. #1", withTypeScript, (t, {typescript}) => {
test("Can handle ArrayBindingPatterns in VariableDeclarations. #1", withTypeScript, (t, {typescript, useTypeChecker}) => {
const {result} = executeProgram(
// language=TypeScript
`
Expand All @@ -12,14 +12,14 @@ test("Can handle ArrayBindingPatterns in VariableDeclarations. #1", withTypeScri
})();
`,
"(() =>",
{typescript}
{typescript, useTypeChecker}
);

if (!result.success) t.fail(result.reason.stack);
else t.deepEqual(result.value, 2);
});

test("Can handle ArrayBindingPatterns in VariableDeclarations. #2", withTypeScript, (t, {typescript}) => {
test("Can handle ArrayBindingPatterns in VariableDeclarations. #2", withTypeScript, (t, {typescript, useTypeChecker}) => {
const {result} = executeProgram(
// language=TypeScript
`
Expand All @@ -29,14 +29,14 @@ test("Can handle ArrayBindingPatterns in VariableDeclarations. #2", withTypeScri
})();
`,
"(() =>",
{typescript}
{typescript, useTypeChecker}
);

if (!result.success) t.fail(result.reason.stack);
else t.deepEqual(result.value, 2);
});

test("Can handle ArrayBindingPatterns in VariableDeclarations. #3", withTypeScript, (t, {typescript}) => {
test("Can handle ArrayBindingPatterns in VariableDeclarations. #3", withTypeScript, (t, {typescript, useTypeChecker}) => {
const {result} = executeProgram(
// language=TypeScript
`
Expand All @@ -46,14 +46,14 @@ test("Can handle ArrayBindingPatterns in VariableDeclarations. #3", withTypeScri
})();
`,
"(() =>",
{typescript}
{typescript, useTypeChecker}
);

if (!result.success) t.fail(result.reason.stack);
else t.deepEqual(result.value, 2);
});

test("Can handle ArrayBindingPatterns in ParameterDeclarations. #1", withTypeScript, (t, {typescript}) => {
test("Can handle ArrayBindingPatterns in ParameterDeclarations. #1", withTypeScript, (t, {typescript, useTypeChecker}) => {
const {result} = executeProgram(
// language=TypeScript
`
Expand All @@ -62,7 +62,7 @@ test("Can handle ArrayBindingPatterns in ParameterDeclarations. #1", withTypeScr
})([1, {destructured: 2}, 3]);
`,
"(([, {destructured: alias}]) =>",
{typescript}
{typescript, useTypeChecker}
);

if (!result.success) t.fail(result.reason.stack);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import test from "ava";
import {executeProgram} from "../setup/execute-program.js";
import {withTypeScript} from "../setup/ts-macro.js";

test("Can handle ArrayLiteralExpressions. #1", withTypeScript, (t, {typescript}) => {
test("Can handle ArrayLiteralExpressions. #1", withTypeScript, (t, {typescript, useTypeChecker}) => {
const {result} = executeProgram(
// language=TypeScript
`
(["foo", "bar"])
`,
"([",
{typescript}
{typescript, useTypeChecker}
);

if (!result.success) t.fail(result.reason.stack);
Expand Down
4 changes: 2 additions & 2 deletions test/assignments/assignments.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import test from "ava";
import {executeProgram} from "../setup/execute-program.js";
import {withTypeScript} from "../setup/ts-macro.js";

test("Can evaluate a CallExpression for a function with variable assignments. #1", withTypeScript, (t, {typescript}) => {
test("Can evaluate a CallExpression for a function with variable assignments. #1", withTypeScript, (t, {typescript, useTypeChecker}) => {
const {result} = executeProgram(
// language=TypeScript
`
Expand All @@ -15,7 +15,7 @@ test("Can evaluate a CallExpression for a function with variable assignments. #1
square(2);
`,
"square(",
{typescript}
{typescript, useTypeChecker}
);

if (!result.success) t.fail(result.reason.stack);
Expand Down
4 changes: 2 additions & 2 deletions test/await-expression/await-expression.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import test from "ava";
import {executeProgram} from "../setup/execute-program.js";
import {withTypeScript} from "../setup/ts-macro.js";

test("Can evaluate an AwaitExpression #1", withTypeScript, async (t, {typescript}) => {
test("Can evaluate an AwaitExpression #1", withTypeScript, async (t, {typescript, useTypeChecker}) => {
const {result} = executeProgram(
// language=TypeScript
`
Expand All @@ -15,7 +15,7 @@ test("Can evaluate an AwaitExpression #1", withTypeScript, async (t, {typescript
})();
`,
"return await myAsyncFunction()",
{typescript}
{typescript, useTypeChecker}
);

if (!result.success) t.fail(result.reason.stack);
Expand Down
Loading

0 comments on commit e121d66

Please sign in to comment.