Skip to content

Commit

Permalink
feature(type-compiler): stop using TypeChecker entirely and allow loa…
Browse files Browse the repository at this point in the history
…der-pattern, to better support webpack/esbuild/SWC.

chore(type): moved tests files, since all will container reflection now.
feature: started using .js file extension in imports to support esm.
feature(rpc): added RpcWebSocketServer
feature(http): support functional route definitions (alternative to classes).
feature(event): support functional event listeners (alternative to classes).
breaking(http): renamed Router to HttpRouter.
breaking(rpc): renamed TcpRpcServer = > RpcTcpServer, TcpRpcClientAdapter => RpcTcpClientAdapter, NetTcpRpcClientAdapter => RpcNetTcpClientAdapter
  • Loading branch information
marcj committed Jun 12, 2022
1 parent bb4794d commit 3093949
Show file tree
Hide file tree
Showing 136 changed files with 3,737 additions and 1,077 deletions.
2 changes: 1 addition & 1 deletion jest-resolver.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const resolve = require('enhanced-resolve');
const resolve2 = require('resolve');
// const resolve2 = require('resolve');

/**
* This custom jest resolver makes sure symlinks are not followed, so preserveSymlinks=true.
Expand Down
60 changes: 30 additions & 30 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion packages/angular-universal/src/listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export class AngularUniversalListener {
}

event.routeFound(
new RouteConfig('angular', ['GET'], event.url, { controller: AngularUniversalListener, module: this.module, methodName: 'render' }),
new RouteConfig('angular', ['GET'], event.url, { type: 'controller', controller: AngularUniversalListener, module: this.module, methodName: 'render' }),
() => [event.url]
);
}
Expand Down
18 changes: 9 additions & 9 deletions packages/api-console-module/src/controller.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { ApiAction, ApiConsoleApi, ApiDocument, ApiEntryPoints, ApiRoute, ApiRouteResponse } from '@deepkit/api-console-api';
import { getActions, rpc, RpcKernel } from '@deepkit/rpc';
import { HttpRouteFilter, HttpRouterFilterResolver, parseRouteControllerAction } from '@deepkit/http';
import { ClassType, getClassName } from '@deepkit/core';
import { ClassType, getClassName, isClass } from '@deepkit/core';
import { Config } from './module.config';
import { readFile } from 'fs/promises';
import { ReflectionClass, ReflectionKind, serializeType, Type, TypeClass, TypeObjectLiteral, TypePropertySignature } from '@deepkit/type';

class ControllerNameGenerator {
controllers = new Map<ClassType, string>();
controllers = new Map<ClassType | Function, string>();
controllerNames = new Set<string>();

getName(controller: ClassType): string {
getName(controller: ClassType | Function): string {
let controllerName = this.controllers.get(controller);
if (!controllerName) {
controllerName = getClassName(controller);
controllerName = isClass(controller) ? getClassName(controller) : controller.name;
let candidate = controllerName;
let i = 2;
while (this.controllerNames.has(candidate)) {
Expand Down Expand Up @@ -83,7 +83,7 @@ export class ApiConsoleController implements ApiConsoleApi {

try {
//todo: Collection, SubjectEntity, Observable get pretty big
rpcAction.methodType = serializeType(reflectionMethod.method);
rpcAction.methodType = serializeType(reflectionMethod.type);
} catch (error: any) {
console.log(`Could not serialize result type of ${of}: ${error.message}`);
}
Expand All @@ -103,12 +103,12 @@ export class ApiConsoleController implements ApiConsoleApi {
for (const route of this.filterResolver.resolve(this.filter.model)) {
if (route.internal) continue;

const controllerName = nameGenerator.getName(route.action.controller);
const controllerName = nameGenerator.getName( route.action.type === 'controller' ? route.action.controller : route.action.fn);

const routeD = new ApiRoute(
route.getFullPath(), route.httpMethods,
controllerName,
route.action.methodName,
route.action.type === 'controller' ? route.action.methodName : '',
route.description,
route.groups,
route.category,
Expand Down Expand Up @@ -167,9 +167,9 @@ export class ApiConsoleController implements ApiConsoleApi {
}
}

const reflectionMethod = ReflectionClass.from(route.action.controller).getMethod(route.action.methodName);
const fn = route.getReflectionFunction();
routeD.resultType = serializeType(fn.getReturnType());

routeD.resultType = serializeType(reflectionMethod.getReturnType());
if (urlType.types.length) routeD.urlType = serializeType(urlType);
if (queryType.types.length) routeD.queryType = serializeType(queryType);
routes.push(routeD);
Expand Down
18 changes: 16 additions & 2 deletions packages/app/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ExitError } from '@oclif/errors';
import { buildOclifCommand } from './command';
import { EnvConfiguration } from './configuration';
import { ReflectionClass, ReflectionKind } from '@deepkit/type';
import { EventDispatcher, EventListener, EventListenerCallback, EventOfEventToken, EventToken } from '@deepkit/event';

export function setPartialConfig(target: { [name: string]: any }, partial: { [name: string]: any }, incomingPath: string = '') {
for (const i in partial) {
Expand Down Expand Up @@ -148,7 +149,7 @@ class EnvConfigLoader {
}
}

export class RootAppModule<T> extends AppModule<T> {
export class RootAppModule<T extends RootModuleDefinition> extends AppModule<T> {
}

/**
Expand Down Expand Up @@ -176,10 +177,13 @@ export class App<T extends RootModuleDefinition> {
this.serviceContainer = serviceContainer || new ServiceContainer(this.appModule);
}

static fromModule<T>(module: AppModule<T>): App<T> {
static fromModule<T extends RootModuleDefinition>(module: AppModule<T>): App<T> {
return new App({} as T, undefined, module);
}

/**
* Allows to change the module after the configuration has been loaded, right before the application bootstraps.
*/
setup(...args: Parameters<this['appModule']['setup']>): this {
this.serviceContainer.appModule = (this.serviceContainer.appModule.setup as any)(...args as any[]);
return this;
Expand All @@ -195,6 +199,16 @@ export class App<T extends RootModuleDefinition> {
return this;
}

listen<T extends EventToken<any>, DEPS extends any[]>(eventToken: T, callback: EventListenerCallback<T['event']>, order: number = 0): this {
const listener: EventListener<any> = { callback, order, eventToken };
this.appModule.listeners.push(listener);
return this;
}

public async dispatch<T extends EventToken<any>>(eventToken: T, event: EventOfEventToken<T>, injector?: InjectorContext): Promise<void> {
return await this.get(EventDispatcher).dispatch(eventToken, event, injector);
}

/**
* Loads environment variables and optionally reads from .env files in order to find matching configuration options
* in your application and modules in order to set their values.
Expand Down
32 changes: 18 additions & 14 deletions packages/app/src/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
ReflectionParameter,
ReflectionProperty,
validate,
ValidationErrorItem
ValidationError
} from '@deepkit/type';
import { Command as OclifCommandBase } from '@oclif/command';
import { Command as OclifCommand } from '@oclif/config';
Expand Down Expand Up @@ -61,7 +61,7 @@ function getProperty(classType: ClassType, ref: {property: string; parameterInde

class ArgDefinition {
isFlag: boolean = false;
multiple: boolean = false;
description: string = '';
hidden: boolean = false;
char: string = '';
property!: string;
Expand All @@ -88,16 +88,15 @@ export class ArgDecorator implements PropertyApiTypeInterface<ArgDefinition> {
cli.addArg(this.t)(classType);
}

get multiple() {
this.t.multiple = true;
return;
}

get hidden() {
this.t.hidden = true;
return;
}

description(description: string) {
this.t.description = description;
}

char(char: string) {
this.t.char = char;
}
Expand Down Expand Up @@ -154,16 +153,19 @@ export function buildOclifCommand(name: string, injector: InjectorContext, class

const options = {
name: propertySchema.name,
description: 'todo',
description: t.description,
hidden: t.hidden,
required: !(propertySchema.isOptional() || propertySchema.hasDefault()),
multiple: t.multiple,
multiple: propertySchema.getType().kind === ReflectionKind.array,
default: propertySchema.getDefaultValue(),
};

//todo, add `parse(i)` and make sure type is correct depending on t.propertySchema.type
if (t.isFlag) {
oclifFlags[propertySchema.name] = propertySchema.type.kind === ReflectionKind.boolean ? flags.boolean(options) : flags.string(options);
if (t.char) {
oclifFlags[propertySchema.name].char = t.char as any;
}
} else {
oclifArgs.push(options);
}
Expand All @@ -188,9 +190,9 @@ export function buildOclifCommand(name: string, injector: InjectorContext, class
const methodArgs: any[] = [];

for (const property of argDefinitions!.args) {
try {
const propertySchema = getProperty(classType, property);
const propertySchema = getProperty(classType, property);

try {
const v = converters.get(propertySchema)!(args[propertySchema.name] ?? flags[propertySchema.name]);
if (propertySchema instanceof ReflectionParameter) {
methodArgs.push(v);
Expand All @@ -200,11 +202,13 @@ export function buildOclifCommand(name: string, injector: InjectorContext, class
}
}
} catch (e) {
if (e instanceof ValidationErrorItem) {
console.log(`Validation error in ${e.path}: ${e.message} [${e.code}]`);
if (e instanceof ValidationError) {
for (const item of e.errors) {
console.error(`Validation error in ${propertySchema.name + (item.path ? '.' + item.path : '')}: ${item.message} [${item.code}]`);
}
return 8;
}
console.log(e);
console.error(e);
return 8;
}
}
Expand Down
8 changes: 4 additions & 4 deletions packages/app/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ export interface ModuleDefinition {
exports?: ExportType[];

/**
* Module bootstrap class. This class is instantiated on bootstrap and can
* setup various injected services. A more flexible alternative is to use .setup() with compiler passes.
* Module bootstrap class|function.
* This class is instantiated or function executed on bootstrap and can set up various injected services.
*/
bootstrap?: ClassType;
bootstrap?: ClassType | Function;

/**
* Configuration definition.
Expand Down Expand Up @@ -257,7 +257,7 @@ export class AppModule<T extends RootModuleDefinition, C extends ExtractClassTyp
* This is also after `processController` and `processProvider` have been called and the full
* final module tree is known. Adding now new providers or modules doesn't have any effect.
*
* Last chance to setup the injector context, via this.setupProvider().
* Last chance to set up the injector context, via this.setupProvider().
*/
postProcess() {

Expand Down
10 changes: 6 additions & 4 deletions packages/app/src/service-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* You should have received a copy of the MIT License along with this program.
*/

import { ClassType, getClassName, isClass } from '@deepkit/core';
import { ClassType, getClassName, isClass, isFunction } from '@deepkit/core';
import { EventDispatcher } from '@deepkit/event';
import { AppModule, ConfigurationInvalidError, MiddlewareConfig, ModuleDefinition } from './module';
import { Injector, InjectorContext, InjectorModule, isProvided, ProviderWithScope, resolveToken, Token } from '@deepkit/injector';
Expand Down Expand Up @@ -194,14 +194,16 @@ export class ServiceContainer {
protected processModule(
module: AppModule<ModuleDefinition>
): void {
if (module.injector) throw new Error(`Module ${getClassName(module)}.${module.name} was already imported. Can not re-use module instances.`);
if (module.injector) {
throw new Error(`Module ${getClassName(module)} (id=${module.name}) was already imported. Can not re-use module instances.`);
}

const providers = module.getProviders();
const controllers = module.getControllers();
const listeners = module.getListeners();
const middlewares = module.getMiddlewares();

if (module.options.bootstrap && !module.isProvided(module.options.bootstrap)) {
if (module.options.bootstrap && !isFunction(module.options.bootstrap) && !module.isProvided(module.options.bootstrap)) {
providers.push(module.options.bootstrap);
}

Expand All @@ -224,7 +226,7 @@ export class ServiceContainer {
providers.unshift({ provide: listener });
this.eventDispatcher.registerListener(listener, module);
} else {
this.eventDispatcher.add(listener.eventToken, { fn: listener.callback, order: listener.order, module: listener.module });
this.eventDispatcher.add(listener.eventToken, { fn: listener.callback, order: listener.order, module: listener.module || module });
}
}

Expand Down

0 comments on commit 3093949

Please sign in to comment.