mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
187 lines
5.4 KiB
TypeScript
187 lines
5.4 KiB
TypeScript
import {
|
|
INestApplicationContext,
|
|
InjectionToken,
|
|
Logger,
|
|
} from '@nestjs/common';
|
|
import { ApplicationConfig } from '../application-config';
|
|
import { ModuleRef, NestContainer } from '../injector';
|
|
import { InternalCoreModule } from '../injector/internal-core-module/internal-core-module';
|
|
import { Module } from '../injector/module';
|
|
import {
|
|
DebugReplFn,
|
|
GetReplFn,
|
|
HelpReplFn,
|
|
MethodsReplFn,
|
|
ResolveReplFn,
|
|
SelectReplFn,
|
|
} from './native-functions';
|
|
import { ReplFunction } from './repl-function';
|
|
import type { ReplFunctionClass } from './repl.interfaces';
|
|
|
|
type ModuleKey = string;
|
|
export type ModuleDebugEntry = {
|
|
controllers: Record<string, InjectionToken>;
|
|
providers: Record<string, InjectionToken>;
|
|
};
|
|
|
|
type ReplScope = Record<string, any>;
|
|
|
|
export class ReplContext {
|
|
public readonly logger = new Logger(ReplContext.name);
|
|
public debugRegistry: Record<ModuleKey, ModuleDebugEntry> = {};
|
|
public readonly globalScope: ReplScope = Object.create(null);
|
|
public readonly nativeFunctions = new Map<
|
|
string,
|
|
InstanceType<ReplFunctionClass>
|
|
>();
|
|
private readonly container: NestContainer;
|
|
|
|
constructor(
|
|
public readonly app: INestApplicationContext,
|
|
nativeFunctionsClassRefs?: ReplFunctionClass[],
|
|
) {
|
|
this.container = (app as any).container; // Using `any` because `app.container` is not public.
|
|
|
|
this.initializeContext();
|
|
this.initializeNativeFunctions(nativeFunctionsClassRefs || []);
|
|
}
|
|
|
|
public writeToStdout(text: string) {
|
|
process.stdout.write(text);
|
|
}
|
|
|
|
private initializeContext() {
|
|
const modules = this.container.getModules();
|
|
|
|
modules.forEach(moduleRef => {
|
|
let moduleName = moduleRef.metatype.name;
|
|
if (moduleName === InternalCoreModule.name) {
|
|
return;
|
|
}
|
|
if (this.globalScope[moduleName]) {
|
|
moduleName += ` (${moduleRef.token})`;
|
|
}
|
|
|
|
this.introspectCollection(moduleRef, moduleName, 'providers');
|
|
this.introspectCollection(moduleRef, moduleName, 'controllers');
|
|
|
|
// For in REPL auto-complete functionality
|
|
Object.defineProperty(this.globalScope, moduleName, {
|
|
value: moduleRef.metatype,
|
|
configurable: false,
|
|
enumerable: true,
|
|
});
|
|
});
|
|
}
|
|
|
|
private introspectCollection(
|
|
moduleRef: Module,
|
|
moduleKey: ModuleKey,
|
|
collection: keyof ModuleDebugEntry,
|
|
) {
|
|
const moduleDebugEntry = {};
|
|
moduleRef[collection].forEach(({ token }) => {
|
|
const stringifiedToken = this.stringifyToken(token);
|
|
if (
|
|
stringifiedToken === ApplicationConfig.name ||
|
|
stringifiedToken === moduleRef.metatype.name
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if (!this.globalScope[stringifiedToken]) {
|
|
// For in REPL auto-complete functionality
|
|
Object.defineProperty(this.globalScope, stringifiedToken, {
|
|
value: token,
|
|
configurable: false,
|
|
enumerable: true,
|
|
});
|
|
}
|
|
|
|
if (stringifiedToken === ModuleRef.name) {
|
|
return;
|
|
}
|
|
|
|
moduleDebugEntry[stringifiedToken] = token;
|
|
});
|
|
|
|
this.debugRegistry[moduleKey] = {
|
|
...this.debugRegistry?.[moduleKey],
|
|
[collection]: moduleDebugEntry,
|
|
};
|
|
}
|
|
|
|
private stringifyToken(token: unknown): string {
|
|
return typeof token !== 'string'
|
|
? typeof token === 'function'
|
|
? token.name
|
|
: (token?.toString() as string)
|
|
: `"${token}"`;
|
|
}
|
|
|
|
private addNativeFunction(
|
|
NativeFunctionRef: ReplFunctionClass,
|
|
): InstanceType<ReplFunctionClass>[] {
|
|
const nativeFunction = new NativeFunctionRef(this);
|
|
const nativeFunctions = [nativeFunction];
|
|
|
|
this.nativeFunctions.set(nativeFunction.fnDefinition.name, nativeFunction);
|
|
|
|
nativeFunction.fnDefinition.aliases?.forEach(aliasName => {
|
|
const aliasNativeFunction: InstanceType<ReplFunctionClass> =
|
|
Object.create(nativeFunction);
|
|
aliasNativeFunction.fnDefinition = {
|
|
name: aliasName,
|
|
description: aliasNativeFunction.fnDefinition.description,
|
|
signature: aliasNativeFunction.fnDefinition.signature,
|
|
};
|
|
this.nativeFunctions.set(aliasName, aliasNativeFunction);
|
|
nativeFunctions.push(aliasNativeFunction);
|
|
});
|
|
|
|
return nativeFunctions;
|
|
}
|
|
|
|
private registerFunctionIntoGlobalScope(
|
|
nativeFunction: InstanceType<ReplFunctionClass>,
|
|
) {
|
|
// Bind the method to REPL's context:
|
|
this.globalScope[nativeFunction.fnDefinition.name] =
|
|
nativeFunction.action.bind(nativeFunction);
|
|
|
|
// Load the help trigger as a `help` getter on each native function:
|
|
const functionBoundRef: ReplFunction['action'] =
|
|
this.globalScope[nativeFunction.fnDefinition.name];
|
|
Object.defineProperty(functionBoundRef, 'help', {
|
|
enumerable: false,
|
|
configurable: false,
|
|
get: () =>
|
|
// Dynamically builds the help message as will unlikely to be called
|
|
// several times.
|
|
this.writeToStdout(nativeFunction.makeHelpMessage()),
|
|
});
|
|
}
|
|
|
|
private initializeNativeFunctions(
|
|
nativeFunctionsClassRefs: ReplFunctionClass[],
|
|
): void {
|
|
const builtInFunctionsClassRefs: ReplFunctionClass[] = [
|
|
HelpReplFn,
|
|
GetReplFn,
|
|
ResolveReplFn,
|
|
SelectReplFn,
|
|
DebugReplFn,
|
|
MethodsReplFn,
|
|
];
|
|
|
|
builtInFunctionsClassRefs
|
|
.concat(nativeFunctionsClassRefs)
|
|
.forEach(NativeFunction => {
|
|
const nativeFunctions = this.addNativeFunction(NativeFunction);
|
|
nativeFunctions.forEach(nativeFunction => {
|
|
this.registerFunctionIntoGlobalScope(nativeFunction);
|
|
});
|
|
});
|
|
}
|
|
}
|