Skip to content

Commit

Permalink
feat(playground): add support local require VSCODE-468 (#718)
Browse files Browse the repository at this point in the history
* support local require when running playground

* pr feedback

* add test for local require
  • Loading branch information
mabaasit authored Apr 17, 2024
1 parent 3bc8428 commit 0390097
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/editors/playgroundController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ export default class PlaygroundController {
result = await this._languageServerController.evaluate({
codeToEvaluate,
connectionId,
filePath: vscode.window.activeTextEditor?.document.uri.fsPath,
});
} catch (error) {
const msg =
Expand Down
1 change: 1 addition & 0 deletions src/language/languageServerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ export default class LanguageServerController {
): Promise<ShellEvaluateResult> {
log.info('Running a playground...', {
connectionId: playgroundExecuteParameters.connectionId,
filePath: playgroundExecuteParameters.filePath,
inputLength: playgroundExecuteParameters.codeToEvaluate.length,
});
this._isExecutingInProgress = true;
Expand Down
1 change: 1 addition & 0 deletions src/language/mongoDBService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ export default class MongoDBService {
name: ServerCommands.EXECUTE_CODE_FROM_PLAYGROUND,
data: {
codeToEvaluate: params.codeToEvaluate,
filePath: params.filePath,
connectionString: this.connectionString,
connectionOptions: this.connectionOptions,
},
Expand Down
40 changes: 30 additions & 10 deletions src/language/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,25 @@ const getLanguage = (evaluationResult: EvaluationResult) => {
return 'plaintext';
};

type ExecuteCodeOptions = {
codeToEvaluate: string;
connectionString: string;
connectionOptions: MongoClientOptions;
filePath?: string;
};

/**
* Execute code from a playground.
*/
const execute = async (
codeToEvaluate: string,
connectionString: string,
connectionOptions: MongoClientOptions
): Promise<{ data?: ShellEvaluateResult; error?: any }> => {
const execute = async ({
codeToEvaluate,
connectionString,
connectionOptions,
filePath,
}: ExecuteCodeOptions): Promise<{
data?: ShellEvaluateResult;
error?: any;
}> => {
const serviceProvider = await CliServiceProvider.connect(
connectionString,
connectionOptions
Expand All @@ -67,6 +78,19 @@ const execute = async (
},
});

// In order to support local require directly from the file where code lives, we can not wrap the
// whole code in a function for two reasons:
// 1. We need to return the response and can not simply add return. And
// 2. We can not use eval to evaluate the *codeToEvaluate* as mongosh async-rewriter can’t see into the eval.
// We are also not directly concatenating the require with the code either due to "use strict"
if (filePath) {
await runtime.evaluate(`(function () {
globalThis.require = require('module').createRequire(${JSON.stringify(
filePath
)});
} ())`);
}

// Evaluate a playground content.
const { source, type, printable } = await runtime.evaluate(codeToEvaluate);
const namespace =
Expand Down Expand Up @@ -94,11 +118,7 @@ const handleMessageFromParentPort = async ({ name, data }): Promise<void> => {
if (name === ServerCommands.EXECUTE_CODE_FROM_PLAYGROUND) {
parentPort?.postMessage({
name: ServerCommands.CODE_EXECUTION_RESULT,
payload: await execute(
data.codeToEvaluate,
data.connectionString,
data.connectionOptions
),
payload: await execute(data),
});
}
};
Expand Down
43 changes: 41 additions & 2 deletions src/test/suite/language/mongoDBService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
import type { CompletionItem } from 'vscode-languageclient/node';
import chai from 'chai';
import { createConnection } from 'vscode-languageserver/node';
import fs from 'fs';
import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import { TextDocument } from 'vscode-languageserver-textdocument';
import type { Db } from 'mongodb';
Expand Down Expand Up @@ -45,7 +46,7 @@ suite('MongoDBService Test Suite', () => {
'dist',
languageServerWorkerFileName
);
await fs.promises.stat(languageServerModuleBundlePath);
await fs.stat(languageServerModuleBundlePath);
});

suite('Extension path', () => {
Expand Down Expand Up @@ -3008,6 +3009,44 @@ suite('MongoDBService Test Suite', () => {
expect(result).to.deep.equal(expectedResult);
});
});

suite('evaluate allows to import local files', function () {
let tmpDir: string;
beforeEach(async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'local-import'));
await fs.writeFile(
path.join(tmpDir, 'utils.js'),
`module.exports.add = function (a, b) {
return a + b;
};
`
);
});
afterEach(async () => {
await fs.rm(tmpDir, { recursive: true });
});
test('evaluate allows to import file', async () => {
const source = new CancellationTokenSource();
const result = await testMongoDBService.evaluate(
{
connectionId: 'pineapple',
codeToEvaluate: 'const { add } = require("./utils.js"); add(1, 2);',
filePath: path.join(tmpDir, 'utils.js'),
},
source.token
);
const expectedResult = {
result: {
namespace: null,
type: 'number',
content: 3,
language: 'plaintext',
},
};

expect(result).to.deep.equal(expectedResult);
});
});
});

suite('Export to language mode', function () {
Expand Down
1 change: 1 addition & 0 deletions src/types/playgroundType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export type ShellEvaluateResult =
export type PlaygroundEvaluateParams = {
codeToEvaluate: string;
connectionId: string;
filePath?: string;
};

export interface ExportToLanguageAddons {
Expand Down

0 comments on commit 0390097

Please sign in to comment.