Skip to content

Commit

Permalink
Merge pull request #985 from samchon/feature/multiple
Browse files Browse the repository at this point in the history
Allow multiple configurations in a file.
  • Loading branch information
samchon authored Aug 14, 2024
2 parents b207205 + 7857e4b commit b1b111c
Show file tree
Hide file tree
Showing 204 changed files with 2,702 additions and 387 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "@nestia/station",
"version": "3.11.0-dev.20240814",
"version": "3.11.0-dev.20240814-3",
"description": "Nestia station",
"scripts": {
"build": "node build/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nestia",
"version": "5.7.0",
"version": "5.7.0-dev.20240814",
"description": "Nestia CLI tool",
"main": "bin/index.js",
"bin": {
Expand Down
6 changes: 3 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/core",
"version": "3.11.0-dev.20240814",
"version": "3.11.0-dev.20240814-3",
"description": "Super-fast validation decorators of NestJS",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -36,7 +36,7 @@
},
"homepage": "https://nestia.io",
"dependencies": {
"@nestia/fetcher": "../fetcher/nestia-fetcher-3.11.0-dev.20240814.tgz",
"@nestia/fetcher": "^3.11.0-dev.20240814-3",
"@nestjs/common": ">=7.0.1",
"@nestjs/core": ">=7.0.1",
"@samchon/openapi": "^0.4.5",
Expand All @@ -53,7 +53,7 @@
"ws": "^7.5.3"
},
"peerDependencies": {
"@nestia/fetcher": ">=3.11.0-dev.20240814",
"@nestia/fetcher": ">=3.11.0-dev.20240814-3",
"@nestjs/common": ">=7.0.1",
"@nestjs/core": ">=7.0.1",
"reflect-metadata": ">=0.1.12",
Expand Down
2 changes: 1 addition & 1 deletion packages/fetcher/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/fetcher",
"version": "3.11.0-dev.20240814",
"version": "3.11.0-dev.20240814-3",
"description": "Fetcher library of Nestia SDK",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
10 changes: 5 additions & 5 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/sdk",
"version": "3.11.0-dev.20240814",
"version": "3.11.0-dev.20240814-3",
"description": "Nestia SDK and Swagger generator",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -32,8 +32,8 @@
},
"homepage": "https://nestia.io",
"dependencies": {
"@nestia/core": "../core/nestia-core-3.11.0-dev.20240814.tgz",
"@nestia/fetcher": "../fetcher/nestia-fetcher-3.11.0-dev.20240814.tgz",
"@nestia/core": "^3.11.0-dev.20240814-3",
"@nestia/fetcher": "^3.11.0-dev.20240814-3",
"@samchon/openapi": "^0.4.5",
"@wrtnio/openai-function-schema": "^0.2.3",
"cli": "^1.0.1",
Expand All @@ -48,8 +48,8 @@
"typia": "^6.8.0"
},
"peerDependencies": {
"@nestia/core": ">=3.11.0-dev.20240814",
"@nestia/fetcher": ">=3.11.0-dev.20240814",
"@nestia/core": ">=3.11.0-dev.20240814-3",
"@nestia/fetcher": ">=3.11.0-dev.20240814-3",
"@nestjs/common": ">=7.0.1",
"@nestjs/core": ">=7.0.1",
"reflect-metadata": ">=0.1.12",
Expand Down
26 changes: 18 additions & 8 deletions packages/sdk/src/NestiaSdkApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,22 @@ export class NestiaSdkApplication {
!this.config.swagger?.output &&
!this.config.openai?.output
)
throw new Error("Error on NestiaApplication.all(): nothing to generate.");
throw new Error(
[
"Error on NestiaApplication.all(): nothing to generate, configure at least one property of below:",
"",
" - INestiaConfig.output",
" - INestiaConfig.swagger.output",
" - INestiaConfig.openai.output",
].join("\n"),
);
print_title("Nestia All Generator");
await this.generate({
generate: async (app) => {
if (this.config.output) await SdkGenerator.generate(app);
if (this.config.e2e) await E2eGenerator.generate(app);
if (this.config.output) {
await SdkGenerator.generate(app);
if (this.config.e2e) await E2eGenerator.generate(app);
}
if (this.config.swagger) await SwaggerGenerator.generate(app);
if (this.config.openai) await OpenAiGenerator.generate(app);
},
Expand All @@ -48,11 +58,11 @@ export class NestiaSdkApplication {
public async e2e(): Promise<void> {
if (!this.config.output)
throw new Error(
"Error on NestiaApplication.e2e(): output path of SDK is not specified.",
"Error on NestiaApplication.e2e(): configure INestiaConfig.output property.",
);
else if (!this.config.e2e)
throw new Error(
"Error on NestiaApplication.e2e(): output path of e2e test files is not specified.",
"Error on NestiaApplication.e2e(): configure INestiaConfig.e2e property.",
);

const validate =
Expand Down Expand Up @@ -80,7 +90,7 @@ export class NestiaSdkApplication {
public async sdk(): Promise<void> {
if (!this.config.output)
throw new Error(
"Error on NestiaApplication.sdk(): output path is not specified.",
"Error on NestiaApplication.sdk(): configure INestiaConfig.output property.",
);

const parent: string = path.resolve(this.config.output + "/..");
Expand All @@ -100,7 +110,7 @@ export class NestiaSdkApplication {
public async swagger(): Promise<void> {
if (!this.config.swagger?.output)
throw new Error(
`Error on NestiaApplication.swagger(): output path of the "swagger.json" is not specified.`,
`Error on NestiaApplication.swagger(): configure INestiaConfig.swagger property.`,
);

const parsed: path.ParsedPath = path.parse(this.config.swagger.output);
Expand All @@ -122,7 +132,7 @@ export class NestiaSdkApplication {
public async openai(): Promise<void> {
if (!this.config.openai?.output)
throw new Error(
`Error on NestiaApplication.openai(): output path of the "openai.json" is not specified.`,
`Error on NestiaApplication.openai(): configure INestiaConfig.openai property.`,
);

const parsed: path.ParsedPath = path.parse(this.config.openai.output);
Expand Down
17 changes: 10 additions & 7 deletions packages/sdk/src/executable/internal/NestiaConfigLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ export namespace NestiaConfigLoader {
);
};

export const config = async (
export const configurations = async (
file: string,
compilerOptions: Record<string, any>,
): Promise<INestiaConfig> => {
): Promise<INestiaConfig[]> => {
if (fs.existsSync(path.resolve(file)) === false)
throw new Error(`Unable to find "${file}" file.`);

Expand All @@ -59,16 +59,19 @@ export namespace NestiaConfigLoader {
: undefined,
});

const loaded: INestiaConfig & { default?: INestiaConfig } = await import(
path.resolve(file)
);
const config: INestiaConfig =
const loaded: (INestiaConfig | INestiaConfig[]) & {
default?: INestiaConfig | INestiaConfig[];
} = await import(path.resolve(file));
const instance: INestiaConfig | INestiaConfig[] =
typeof loaded?.default === "object" && loaded.default !== null
? loaded.default
: loaded;
const configurations: INestiaConfig[] = Array.isArray(instance)
? instance
: [instance];

try {
return typia.assert(config);
return typia.assert(configurations);
} catch (exp) {
if (typia.is<typia.TypeGuardError>(exp))
exp.message = `invalid "${file}" data.`;
Expand Down
94 changes: 73 additions & 21 deletions packages/sdk/src/executable/internal/NestiaSdkCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,59 @@ import { NestiaSdkApplication } from "../../NestiaSdkApplication";
import { NestiaConfigLoader } from "./NestiaConfigLoader";

export namespace NestiaSdkCommand {
export const sdk = () => main((app) => app.sdk());
export const swagger = () => main((app) => app.swagger());
export const openai = () => main((app) => app.openai());
export const e2e = () => main((app) => app.e2e());
export const all = () => main((app) => app.all());
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.",
});
export const openai = () =>
main({
title: "OpenAI Function Calling Schema",
generate: (app) => app.openai(),
validate: (config) => !!config.openai?.output,
solution: "configure INestiaConfig.openai property.",
});
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 laest one property of below:",
"",
" - INestiaConfig.output",
" - INestiaConfig.swagger.output",
" - INestiaConfig.openai.output",
].join("\n"),
});

const main = async (task: (app: NestiaSdkApplication) => Promise<void>) => {
await _Generate(task);
};

const _Generate = async (
task: (app: NestiaSdkApplication) => Promise<void>,
) => {
const main = async (props: {
title: string;
solution: string;
generate: (app: NestiaSdkApplication) => Promise<void>;
validate: (config: INestiaConfig) => boolean;
}) => {
// LOAD CONFIG INFO
const command: ts.ParsedCommandLine =
await NestiaConfigLoader.compilerOptions(
Expand All @@ -26,17 +66,29 @@ export namespace NestiaSdkCommand {
extension: "json",
}) ?? "tsconfig.json",
);
const config: INestiaConfig = await NestiaConfigLoader.config(
getFileArgument({
type: "config",
extension: "ts",
}) ?? "nestia.config.ts",
command.raw.compilerOptions,
);
const configurations: INestiaConfig[] =
await NestiaConfigLoader.configurations(
getFileArgument({
type: "config",
extension: "ts",
}) ?? "nestia.config.ts",
command.raw.compilerOptions,
);

// GENERATE
const app: NestiaSdkApplication = new NestiaSdkApplication(config);
await task(app);
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);
}
};

const getFileArgument = (props: {
Expand Down
31 changes: 0 additions & 31 deletions packages/sdk/src/generates/E2eGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import fs from "fs";
import path from "path";

import { ConfigAnalyzer } from "../analyses/ConfigAnalyzer";
import { INestiaProject } from "../structures/INestiaProject";
import { ITypedApplication } from "../structures/ITypedApplication";
import { E2eFileProgrammer } from "./internal/E2eFileProgrammer";

Expand All @@ -17,9 +15,6 @@ export namespace E2eGenerator {
await mkdir(path.join(output, "features", "api"));
await mkdir(path.join(output, "features", "api", "automated"));

// GENERATE TEST INDEX FILE
await index(app.project)(path.join(app.project.config.e2e!, "index.ts"));

// GENERATE EACH TEST FILES
for (const route of app.routes)
if (route.protocol === "http")
Expand All @@ -28,32 +23,6 @@ export namespace E2eGenerator {
current: path.join(output, "features", "api", "automated"),
})(route);
};

const index =
(project: INestiaProject) =>
async (output: string): Promise<void> => {
if (fs.existsSync(output)) return;

const location: string = path.join(
__dirname,
"..",
"..",
"assets",
"bundle",
"e2e",
"index.ts",
);
const content: string = await fs.promises.readFile(location, "utf8");

await fs.promises.writeFile(
output,
content.replace(
"${input}",
JSON.stringify(await ConfigAnalyzer.input(project.config)),
),
"utf8",
);
};
}

const mkdir = async (location: string): Promise<void> => {
Expand Down
22 changes: 11 additions & 11 deletions test/executable/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ const feature = (name) => {
try {
TestValidator.error("compile error")(() => {
cp.execSync("npx tsc", { stdio: "ignore" });
generate("swagger");
generate("sdk");
generate("all");
});
throw new Error("compile error must be occured.");
} catch {
Expand All @@ -52,14 +51,15 @@ const feature = (name) => {
if (name.includes("distribute"))
cp.execSync(`npx rimraf packages/api`, { stdio: "ignore" });

const config = fs.readFileSync(`${featureDirectory(name)}/${file}`, "utf8");
{
const lines = config.split("\r\n").join("\n").split("\n");
if (lines.some((l) => l.startsWith(` output:`))) generate("sdk");
}
for (const kind of ["swagger", "openai", "e2e"])
if (config.includes(`${kind}:`)) generate(kind);
cp.execSync("npx tsc", { stdio: "ignore" });
if (name === "all") {
const config = fs.readFileSync(`${featureDirectory(name)}/${file}`, "utf8");
{
const lines = config.split("\r\n").join("\n").split("\n");
if (lines.some((l) => l.startsWith(` output:`))) generate("sdk");
}
for (const kind of ["swagger", "openai", "e2e"])
if (config.includes(`${kind}:`)) generate(kind);
} else generate("all");

// RUN TEST AUTOMATION PROGRAM
if (fs.existsSync("src/test")) {
Expand All @@ -70,7 +70,7 @@ const feature = (name) => {
return;
} catch {}
test();
}
} else cp.execSync("npx tsc", { stdio: "ignore" });
};

const main = async () => {
Expand Down
Loading

0 comments on commit b1b111c

Please sign in to comment.