Skip to content

Commit 2cea844

Browse files
authored
chore: move load() __filename and __dirname handling to shell-api (#710)
This will make it easier to implement these features in a fully equivalent way in Compass/VSCode, without having to duplicate this particular bit of code (and not risk divergence, e.g. if we should ever decide to use a separate stack for storing filename info).
1 parent f5c2c85 commit 2cea844

File tree

5 files changed

+58
-23
lines changed

5 files changed

+58
-23
lines changed

packages/cli-repl/src/mongosh-repl.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ import { MongoshCommandFailed, MongoshInternalError, MongoshWarning } from '@mon
33
import { changeHistory } from '@mongosh/history';
44
import i18n from '@mongosh/i18n';
55
import type { ServiceProvider } from '@mongosh/service-provider-core';
6-
import { EvaluationListener, ShellCliOptions, ShellInternalState } from '@mongosh/shell-api';
6+
import { EvaluationListener, ShellCliOptions, ShellInternalState, OnLoadResult } from '@mongosh/shell-api';
77
import { ShellEvaluator, ShellResult } from '@mongosh/shell-evaluator';
88
import type { MongoshBus, UserConfig } from '@mongosh/types';
99
import askpassword from 'askpassword';
1010
import { Console } from 'console';
1111
import { once } from 'events';
12-
import path from 'path';
1312
import prettyRepl from 'pretty-repl';
1413
import { ReplOptions, REPLServer } from 'repl';
1514
import type { Readable, Writable } from 'stream';
@@ -318,27 +317,17 @@ class MongoshNodeRepl implements EvaluationListener {
318317
}
319318
}
320319

321-
async onLoad(filename: string): Promise<void> {
320+
async onLoad(filename: string): Promise<OnLoadResult> {
322321
const repl = this.runtimeState().repl;
323322
const {
324323
contents,
325324
absolutePath
326325
} = await this.ioProvider.readFileUTF8(filename);
327326

328-
const previousFilename = repl.context.__filename;
329-
repl.context.__filename = absolutePath;
330-
repl.context.__dirname = path.dirname(absolutePath);
331-
try {
332-
await promisify(repl.eval.bind(repl))(contents, repl.context, filename);
333-
} finally {
334-
if (previousFilename) {
335-
repl.context.__filename = previousFilename;
336-
repl.context.__dirname = path.dirname(previousFilename);
337-
} else {
338-
delete repl.context.__filename;
339-
delete repl.context.__dirname;
340-
}
341-
}
327+
return {
328+
resolvedFilename: absolutePath,
329+
evaluate: () => promisify(repl.eval.bind(repl))(contents, repl.context, absolutePath)
330+
};
342331
}
343332

344333
/**

packages/shell-api/src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Database from './database';
55
import Explainable from './explainable';
66
import ExplainableCursor from './explainable-cursor';
77
import Help, { HelpProperties } from './help';
8-
import ShellInternalState, { EvaluationListener, ShellCliOptions } from './shell-internal-state';
8+
import ShellInternalState, { EvaluationListener, ShellCliOptions, OnLoadResult } from './shell-internal-state';
99
import toIterator from './toIterator';
1010
import Shard from './shard';
1111
import ReplicaSet from './replica-set';
@@ -62,5 +62,6 @@ export {
6262
getShellApiType,
6363
ShellResult,
6464
ShellCliOptions,
65-
TypeSignature
65+
TypeSignature,
66+
OnLoadResult
6667
};

packages/shell-api/src/shell-api.spec.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,9 +570,22 @@ describe('ShellApi', () => {
570570
});
571571
describe('load', () => {
572572
it('asks the evaluation listener to load a file', async() => {
573-
evaluationListener.onLoad.resolves();
573+
evaluationListener.onLoad.callsFake(async(filename: string) => {
574+
expect(filename).to.equal('abc.js');
575+
expect(internalState.context.__filename).to.equal(undefined);
576+
expect(internalState.context.__dirname).to.equal(undefined);
577+
return {
578+
resolvedFilename: '/resolved/abc.js',
579+
evaluate: async() => {
580+
expect(internalState.context.__filename).to.equal('/resolved/abc.js');
581+
expect(internalState.context.__dirname).to.equal('/resolved');
582+
}
583+
};
584+
});
574585
await internalState.context.load('abc.js');
575-
expect(evaluationListener.onLoad).to.have.been.calledWith('abc.js');
586+
expect(evaluationListener.onLoad).to.have.callCount(1);
587+
expect(internalState.context.__filename).to.equal(undefined);
588+
expect(internalState.context.__dirname).to.equal(undefined);
576589
});
577590
});
578591
for (const cmd of ['print', 'printjson']) {

packages/shell-api/src/shell-api.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { CommonErrors, MongoshUnimplementedError, MongoshInternalError } from '@
2020
import { DBQuery } from './deprecated';
2121
import { promisify } from 'util';
2222
import { ClientSideFieldLevelEncryptionOptions } from './field-level-encryption';
23+
import { dirname } from 'path';
2324

2425
@shellApiClassDefault
2526
@hasAsyncChild
@@ -109,7 +110,25 @@ export default class ShellApi extends ShellApiClass {
109110
CommonErrors.NotImplemented
110111
);
111112
}
112-
await this.internalState.evaluationListener.onLoad(filename);
113+
const {
114+
resolvedFilename, evaluate
115+
} = await this.internalState.evaluationListener.onLoad(filename);
116+
117+
const context = this.internalState.context;
118+
const previousFilename = context.__filename;
119+
context.__filename = resolvedFilename;
120+
context.__dirname = dirname(resolvedFilename);
121+
try {
122+
await evaluate();
123+
} finally {
124+
if (previousFilename) {
125+
context.__filename = previousFilename;
126+
context.__dirname = dirname(previousFilename);
127+
} else {
128+
delete context.__filename;
129+
delete context.__dirname;
130+
}
131+
}
113132
return true;
114133
}
115134

packages/shell-api/src/shell-internal-state.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@ export interface AutocompleteParameters {
3535
getCollectionCompletionsForCurrentDb: (collName: string) => Promise<string[]>;
3636
}
3737

38+
export interface OnLoadResult {
39+
/**
40+
* The absolute path of the file that should be load()ed.
41+
*/
42+
resolvedFilename: string;
43+
44+
/**
45+
* The actual steps that are needed to evaluate the load()ed file.
46+
* For the duration of this call, __filename and __dirname are set as expected.
47+
*/
48+
evaluate(): Promise<void>;
49+
}
50+
3851
export interface EvaluationListener {
3952
/**
4053
* Called when print() or printjson() is run from the shell.
@@ -66,7 +79,7 @@ export interface EvaluationListener {
6679
/**
6780
* Called when load() is used in the shell.
6881
*/
69-
onLoad?: (filename: string) => Promise<void>;
82+
onLoad?: (filename: string) => Promise<OnLoadResult> | OnLoadResult;
7083
}
7184

7285
/**

0 commit comments

Comments
 (0)