md-toy-blog
Version:
Very simple Markdown blog: serves your md as html without fancy databases. You will only spend time writing the actual data.
153 lines (124 loc) • 4.94 kB
TypeScript
type Partial<T> = {
[P in keyof T]?: T[P];
};
type AppConfig = {
compiledUserContentDir: string;
missingRefValueReplacement: string,
mdBlogPostsDir: string;
packageName: string;
previewLength: number;
staticFilesDir: string;
pagesDir: string;
viewTemplatesDir: string;
userCustomConfigPath: string;
userProjectRootDir: string;
};
type AppConfigFactoryParams = AppConfig & {
userCustomConfig: Partial<AppConfig>;
};
type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?:
Required<Pick<T, K>>
& Partial<Record<Exclude<Keys, K>, undefined>>
}[Keys]
// Internal Placeholders
type ActionParamsDefaultRetType = {};
type RouteConfigDefaultAdditionalPropsType = {};
type RouteUserConfigDefaultAdditionalPropsType = {};
type RouteCtorConfigDefaultAdditionalPropsType = {};
type GetControllerActionParamsMethod<T={}> =
(props: { request: HttpRequest; }) => T;
type RouteInterface<A=undefined, U={}> = U & CanResolveRouteInterface & {
controller: RouteControllerCtorConfig<A>;
getControllerActionParams?: GetControllerActionParamsMethod
};
// We need two things:
// 1. A config generated by user passed to DI to create the route
// > ...UserConfig
// 2. A config used by Di to pass to the route constructor
// > ...CtorConfig
// We need two more things:
// a. Path
// b. RegExp
type DiNamedConfig = { name: string; };
type AsInstanceConfig<I> = { instance: I; };
type RouteControllerTypeBothConfig =
DiNamedConfig | AsInstanceConfig<ControllerInterface>;
type ActionParamsObjectType<A> = {
actionParams?: A;
actionParamsGetter?: (props: { request: HttpRequest; }) => A;
}
type RouteControllerWithActionParamsConfig<A=ActionParamsDefaultRetType> =
RequireOnlyOne<ActionParamsObjectType<A>, 'actionParams' | 'actionParamsGetter'>
type RouteControllerGenericConfig<T extends RouteControllerTypeBothConfig, A=ActionParamsDefaultRetType> =
T & RouteControllerWithActionParamsConfig<A>;
type RouteControllerUserConfig<A=ActionParamsDefaultRetType> =
RouteControllerGenericConfig<RouteControllerTypeBothConfig, A>;
type RouteControllerCtorConfig<A=ActionParamsDefaultRetType> =
RouteControllerGenericConfig<AsInstanceConfig<ControllerInterface>, A>;
// 1. ...UserConfig
type RouteUserConfig<E=RouteUserConfigDefaultAdditionalPropsType, A=ActionParamsDefaultRetType> =
AsInstanceConfig<RouteInterface<A>> | (E & DiNamedConfig & {
controller: RouteControllerUserConfig<A>;
});
// 2. ...CtorConfig
type RouteCtorConfig<E=RouteCtorConfigDefaultAdditionalPropsType, A=ActionParamsDefaultRetType> =
E & {
controller: RouteControllerCtorConfig<A>;
};
// Our 4 things
// F : Further props passed to route First level config
// A : ActionParams object
type RouteMatchPathUserConfig<F=RouteConfigDefaultAdditionalPropsType, A=ActionParamsDefaultRetType> =
RouteUserConfig<F & { path: string; }, A>;
type RouteMatchRegExpUserConfig<F=RouteConfigDefaultAdditionalPropsType, A=ActionParamsDefaultRetType> =
RouteUserConfig<F & { pathRegExp: RegExp; }, A>;
type RouteMatchPathCtorConfig<F=RouteConfigDefaultAdditionalPropsType, A=ActionParamsDefaultRetType> =
RouteCtorConfig<F & { paths: string[]; }, A>;
type RouteMatchRegExpCtorConfig<F=RouteConfigDefaultAdditionalPropsType, A=ActionParamsDefaultRetType> =
RouteCtorConfig<F & { pathRegExp: RegExp; }, A>;
type ValidPostSlugListGetterConf = { validPostSlugListGetter: () => string[]; }
type StaticFilePathListGetterConf = { staticFilePathListGetter: () => string[]; }
interface HttpRequest {
url: string;
method: 'GET' | 'POST';
}
interface IsValidRequestInterface {
isValid: (request: HttpRequest) => boolean;
}
interface MatchesRequestRegExpInterface {
matches: (request: HttpRequest, pathRegExp: RegExp) => boolean;
}
interface MatchesRequestPathInterface {
matches: (request: HttpRequest, path: string) => boolean;
}
interface CanResolveRouteInterface {
canResolve: (request: HttpRequest) => boolean;
}
type RegExpRouteSpecConfig<C=any> =
RouteMatchRegExpConfig & MaybeDiControllerConfig<C>;
type RegExpRouteConstructorConfig<C=any> =
RouteMatchRegExpConfig & RouteConstructorControllerConfig<C>;
interface RegExpRouteValidatorConstructorInterface<T> {
new (config: RegExpRouteConstructorConfig): RouteInterface<T>;
}
type PathRouteSpecConfig<C=any> =
RouteMatchPathConfig & MaybeDiControllerConfig<C>;
type PathRouteConstructorConfig<C=any> =
RouteMatchPathConfig & RouteConstructorControllerConfig<C>;
interface PathRouteValidatorConstructorInterface<T> {
new (config: PathRouteConstructorConfig): RouteInterface<T>;
}
interface ResponseInterface {
code: number;
headers: { [k: string]: string; };
body: string;
}
interface ControllerConstructor {
new (config: any): ControllerInterface;
}
interface ControllerInterface<A={}> {
action: (params: A) => Promise<ResponseInterface>;
}