@nestia/sdk
Version:
Nestia SDK and Swagger generator
128 lines (117 loc) • 4.09 kB
text/typescript
import { INestiaConfig } from "../../INestiaConfig";
import { NestiaSdkApplication } from "../../NestiaSdkApplication";
import { NestiaConfigLoader } from "./NestiaConfigLoader";
import { NestiaSdkWatcher } from "./NestiaSdkWatcher";
export namespace NestiaSdkCommand {
export const sdk = () =>
main({
title: "SDK library",
generate: (app) => app.sdk(),
validate: (config) => !!config.output,
solution: "configure INestiaConfig.output property.",
});
export const swagger = () =>
main({
title: "Swagger Document",
generate: (app) => app.swagger(),
validate: (config) => !!config.swagger?.output,
solution: "configure INestiaConfig.swagger property.",
watch: hasFlagArgument("watch"),
});
export const e2e = () =>
main({
title: "E2E Functions",
generate: (app) => app.e2e(),
validate: (config) => !!config.e2e,
solution: [
"configure two properties:",
"",
" - INestiaConfig.output",
" - INestiaConfig.e2e",
].join("\n"),
});
export const all = () =>
main({
title: "everything",
generate: (app) => app.all(),
validate: () => true,
solution: [
"configure at least one property of below:",
"",
" - INestiaConfig.output",
" - INestiaConfig.swagger.output",
].join("\n"),
});
const main = async (props: {
title: string;
solution: string;
generate: (app: NestiaSdkApplication) => Promise<void>;
validate: (config: INestiaConfig) => boolean;
watch?: boolean;
}) => {
// LOAD CONFIG INFO
const project: string =
getFileArgument({
type: "project",
extension: "json",
}) ?? "tsconfig.json";
process.env.NESTIA_PROJECT = project;
const configFile: string =
getFileArgument({
type: "config",
extension: "ts",
}) ?? "nestia.config.ts";
const configurations = async (): Promise<INestiaConfig[]> => {
const command: NestiaConfigLoader.ICompilerOptions =
await NestiaConfigLoader.compilerOptions(project);
return NestiaConfigLoader.configurations(
configFile,
command.raw.compilerOptions ?? {},
);
};
const generate = async (configurations: INestiaConfig[]): Promise<void> => {
if (
configurations.length > 1 &&
configurations.some(props.validate) === false
)
throw new Error(
`Every configurations are invalid to generate ${props.title}, ${props.solution}`,
);
for (const config of configurations) {
if (configurations.length > 1 && props.validate(config) === false)
continue;
const app: NestiaSdkApplication = new NestiaSdkApplication(config);
await props.generate(app);
}
};
if (props.watch === true)
return NestiaSdkWatcher.watch({
configFile,
configurations,
generate,
projectFile: project,
});
await generate(await configurations());
};
const getFileArgument = (props: {
type: string;
extension: string;
}): string | null => {
const argv: string[] = process.argv.slice(3);
const index: number = argv.findIndex((str) => str === `--${props.type}`);
if (index === -1) return null;
// Bound the VALUE position, not the whole argument list. The two only
// coincide when the flag is the sole argument, so any preceding token used
// to send `undefined` into endsWith() and report a TypeError instead of this
// diagnostic. A following flag is a missing value too, not a badly named
// file.
const file: string | undefined = argv[index + 1];
if (file === undefined || file.startsWith("--"))
throw new Error(`${props.type} file must be provided`);
if (file.endsWith(props.extension) === false)
throw new Error(`${props.type} file must be ${props.extension} file`);
return file;
};
const hasFlagArgument = (type: string): boolean =>
process.argv.slice(3).some((str) => str === `--${type}`);
}