Socket
Socket
Sign inDemoInstall

nestia

Package Overview
Dependencies
Maintainers
1
Versions
222
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nestia - npm Package Compare versions

Comparing version 0.2.3 to 0.3.0-dev.20210923

src/utils/stripJsonComments.ts

4

package.json
{
"name": "nestia",
"version": "0.2.3",
"version": "0.3.0-dev.20210923",
"description": "Automatic SDK and Document generator for the NestJS",

@@ -11,3 +11,3 @@ "main": "src/index.ts",

"dev": "tsc --watch",
"test": "ts-node src/bin/nestia sdk src/test/controllers --out api"
"test": "cd test && bash script.sh"
},

@@ -14,0 +14,0 @@ "repository": {

@@ -9,7 +9,2 @@ # Nestia

## Outline
If you're making a backend server with the **TypeScript** and **NestJS**, you don't need any extra dedication, for delivering Rest API to the client (front-end) developers, like writing `swagger.json` comments.
Just generate a SDK library through the **Nestia** and deliver the SDK library to the client developers. The client developers can call your backend server API just by calling the SDK library functions with *await* symbol, re-using the interfaces what you've defined.
```bash

@@ -20,11 +15,37 @@ npm install --save-dev nestia

If you want to see an example project using the **Nestia**, click below links:
Don't write any `swagger` comment. Just deliver the SDK.
- [Controllers of the NestJS](https://github.surf/samchon/nestia/blob/HEAD/src/test/controllers/base/SaleCommentsController.ts)
- [Structures used in the RestAPI](https://github.surf/samchon/nestia/blob/HEAD/api/structures/sales/articles/ISaleArticle.ts)
- [**SDK generated by the Nestia**](https://github.surf/samchon/nestia/blob/HEAD/api/functional/consumers/sales/reviews/index.ts)
When you're developing a backend server using the `NestJS`, you don't need any extra dedication, for delivering the Rest API to the client developers, like writing the `swagger` comments. You just run this **Nestia** up, then **Nestia** would generate the SDK automatically, by analyzing your controller classes in the compliation and runtime level.
With the automatically generated SDK through this **Nestia**, client developer also does not need any extra work, like reading `swagger` and writing the duplicated interaction code. Client developer only needs to import the SDK and calls matched function with the `await` symbol.
```typescript
import api from "@samchon/bbs-api";
import { IBbsArticle } from "@samchon/bbs-api/lib/structures/bbs/IBbsArticle";
import { IPage } from "@samchon/bbs-api/lib/structures/common/IPage";
export async function test_article_read(connection: api.IConnection): Promise<void>
{
// LIST UP ARTICLE SUMMARIES
const index: IPage<IBbsArticle.ISummary> = await api.functional.bbs.articles.index
(
connection,
"free",
{ limit: 100, page: 1 }
);
// READ AN ARTICLE DETAILY
const article: IBbsArticle = await api.functional.bbs.articles.at
(
connection,
"free",
index.data[0].id
);
console.log(article.title, aritlce.body, article.files);
}
```
## Usage

@@ -34,3 +55,2 @@ ### Installation

npm install --save-dev nestia
npx nestia sdk "src/controllers" --out "src/api"
```

@@ -42,3 +62,3 @@

### CLI options
### SDK generation
```bash

@@ -51,5 +71,8 @@ npx nestia sdk <source_controller_directory> --out <output_sdk_directory>

To generate a SDK library through the **Nestia** is very easy. Just type the `nestia sdk <input> --out <output>` command in the console. If there're multiple source directories containing the NestJS controller classes, type all of them separating by a `space` word.
To generate a SDK library through the **Nestia** is very easy.
Just type the `nestia sdk <input> --out <output>` command in the console. If there're multiple source directories containing the NestJS controller classes, type all of them separating by a `space` word.
Also, when generating a SDK using the cli options, `compilerOptions` would follow the `tsconfig.json`. If no `tsconfig.json` file exists in your project, the configuration would be the `ES5` with `strict` mode. If you want to use different `compilerOptions` with the `tsconfig.json`, you should configure the [nestia.config.ts](#nestiaconfigts).
```bash

@@ -59,5 +82,135 @@ npx nestia install

Also, SDK library generated by the **Nestia** has some dependencies like below. When you type the `nestia install` command in the console, those dependencies would be automatically install and would be enrolled to the `dependencies` field in the `package.json`
### Dependencies
SDK library generated by the **Nestia** has some dependencies like below. When you type the `nestia install` command in the console, those dependencies would be automatically installed and enrolled to the `dependencies` field in the `package.json`
- [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node)
- [node-fetch](https://github.com/node-fetch/node-fetch)
- [node-fetch](https://github.com/node-fetch/node-fetch)
## Advanced
### `NestiaApplication`
```typescript
import tsc from "typescript";
export class NestiaApplication
{
public constructor(config: NestiaApplication.IConfiguration);
public generate(): Promise<void>;
public simulate(): Promise<void>;
}
export namespace NestiaApplication
{
export interface IConfiguration
{
/**
* List of directories containing the NestJS controller classes.
*/
input: string[];
/**
* Output directory that SDK would be placed in.
*/
output: string;
/**
* Compiler options for the TypeScript.
*
* If omitted, the configuration would follow the `tsconfig.json`.
*/
compilerOptions?: tsc.CompilerOptions
}
}
```
### `nestia.config.ts`
### Recommended Structures
## Demonstration
- [Controllers of the NestJS](https://github.surf/samchon/nestia/blob/HEAD/src/test/controllers/base/SaleCommentsController.ts)
- [Structures used in the RestAPI](https://github.surf/samchon/nestia/blob/HEAD/api/structures/sales/articles/ISaleArticle.ts)
- [SDK generated by this **Nestia**](https://github.surf/samchon/nestia/blob/HEAD/api/functional/consumers/sales/reviews/index.ts)
### Controller
If you've decided to adapt this **Nestia** and you want to generate the SDK directly, you don't need any extra work. Just keep you controller class down and do noting. The only one exceptional case that you need an extra dedication is, when you want to explain about the API function to the client developers through the comments.
```typescript
@nest.Controller("consumers/:section/sales/:saleId/questions")
export class ConsumerSaleQuestionsController
{
/**
* Store a new question.
*
* @param request Instance of the Express.Request
* @param section Code of the target section
* @param saleId ID of the target sale
* @param input Content to archive
*
* @return Newly archived question
* @throw 400 bad request error when type of the input data is not valid
* @throw 401 unauthorized error when you've not logged in yet
*/
@nest.Post()
public store
(
@nest.Request() request: express.Request,
@nest.Param("section") section: string,
@nest.Param("saleId") saleId: number,
@nest.Body() input: ISaleQuestion.IStore
): Promise<ISaleQuestion>;
}
```
### SDK
When you run the **Nestia** up using the upper controller class `ConsumerSaleQuestionsController`, the **Nestia** would generate below function for the client developers, by analyzing the `ConsumerSaleQuestionsController` class in the compilation and runtime level.
As you can see, the comments from the `ConsumerSaleQuestionsController.store()` are fully copied to the SDK function. Therefore, if you want to deliver detailed description about the API function, writing the detailed comment would be tne best choice.
```typescript
/**
* Store a new question.
*
* @param connection connection Information of the remote HTTP(s) server with headers (+encryption password)
* @param request Instance of the Express.Request
* @param section Code of the target section
* @param saleId ID of the target sale
* @param input Content to archive
* @return Newly archived question
* @throw 400 bad request error when type of the input data is not valid
* @throw 401 unauthorized error when you've not logged in yet
*
* @nestia Generated by Nestia - https://github.com/samchon/nestia
* @controller ConsumerSaleQuestionsController.store()
* @path POST /consumers/:section/sales/:saleId/questions/
*/
export function store
(
connection: IConnection,
section: string,
saleId: number,
input: store.Input
): Promise<store.Output>
{
return Fetcher.fetch
(
connection,
{
input_encrypted: false,
output_encrypted: false
},
"POST",
`/consumers/${section}/sales/${saleId}/questions/`,
input
);
}
export namespace store
{
export type Input = Primitive<ISaleInquiry.IStore>;
export type Output = Primitive<ISaleInquiry<ISaleArticle.IContent>>;
}
```

@@ -6,38 +6,73 @@ #!/usr/bin/env ts-node-script

import path from "path";
import tsc from "typescript";
import { NestiaApplication } from "../NestiaApplication";
import { Terminal } from "../utils/Terminal";
import { stripJsonComments } from "../utils/stripJsonComments";
import { NestiaApplication } from "../NestiaApplication";
interface ICommand
{
install: boolean;
out: string | null;
}
async function sdk(inputList: string[], command: ICommand): Promise<void>
async function sdk(input: string[], command: ICommand): Promise<void>
{
// VALIDATE OUTPUT
let compilerOptions: tsc.CompilerOptions | undefined = {};
//----
// NESTIA.CONFIG.TS
//----
if (fs.existsSync("nestia.config.ts") === true)
{
const config: NestiaApplication.IConfiguration = await import(path.resolve("nestia.config.ts"));
compilerOptions = config.compilerOptions;
input = config.input;
command.out = config.output;
}
//----
// VALIDATIONS
//----
// CHECK OUTPUT
if (command.out === null)
throw new Error(`Output directory is not specified. Add the "--out <output_directory>" option.`);
// CHECK PARENT DIRECTORY
const parentPath: string = path.resolve(command.out + "/..");
const parentStats: fs.Stats = await fs.promises.stat(parentPath);
if (parentStats.isDirectory() === false)
throw new Error(`Unable to find parent directory of the output path: "${parentPath}".`);
// VALIDATE INPUTS
for (const input of inputList)
// CHECK INPUTS
for (const path of input)
{
const inputStats: fs.Stats = await fs.promises.stat(input);
const inputStats: fs.Stats = await fs.promises.stat(path);
if (inputStats.isDirectory() === false)
throw new Error(`Target "${inputList}" is not a directory.`);
throw new Error(`Target "${path}" is not a directory.`);
}
//----
// GENERATE
// GENERATION
//----
// CALL THE APP.SDK()
const app: NestiaApplication = new NestiaApplication(inputList, command.out);
await app.sdk();
if (fs.existsSync("tsconfig.json") === true)
{
const content: string = await fs.promises.readFile("tsconfig.json", "utf8");
const options: tsc.CompilerOptions = JSON.parse(stripJsonComments(content)).compilerOptions;
compilerOptions = compilerOptions
? { ...options, ...compilerOptions }
: options;
}
// CHECK NESTIA.CONFIG.TS
// CALL THE APP.GENERATE()
const app: NestiaApplication = new NestiaApplication({
output: command.out,
input,
compilerOptions,
});
await app.generate();
}

@@ -62,3 +97,2 @@

out: ["o", "Output path of the SDK files", "string", null],
install: ["i", "Install Dependencies", "boolean", false]
});

@@ -65,0 +99,0 @@

@@ -17,2 +17,3 @@ import type * as tsc from "typescript";

.map(closure => closure(route, query, input))
.filter(str => !!str)
.join("\n");

@@ -47,7 +48,8 @@ }

// RETURNS WITH FINALIZATION
return ""
return "{\n"
+ " return Fetcher.fetch\n"
+ " (\n"
+ fetchArguments.map(param => ` ${param}`).join(",\n") + "\n"
+ " );";
+ " );\n"
+ "}";
}

@@ -147,7 +149,6 @@

+ `${parameters.map(str => ` ${str}`).join(",\n")}\n`
+ ` ): Promise<${output}>\n`
+ "{";
+ ` ): Promise<${output}>`;
}
function tail(route: IRoute, query: IRoute.IParameter | undefined, input: IRoute.IParameter | undefined): string
function tail(route: IRoute, query: IRoute.IParameter | undefined, input: IRoute.IParameter | undefined): string | null
{

@@ -163,6 +164,5 @@ const types: Pair<string, string>[] = [];

if (types.length === 0)
return "}";
return null;
return `}\n`
+ `export namespace ${route.name}\n`
return `export namespace ${route.name}\n`
+ "{\n"

@@ -169,0 +169,0 @@ + (types.map(tuple => ` export type ${tuple.first} = Primitive<${tuple.second}>;`).join("\n")) + "\n"

@@ -18,16 +18,8 @@ import * as fs from "fs";

{
public readonly inputs: string[];
public readonly output: string;
private readonly config_: NestiaApplication.IConfiguration;
private readonly bundle_checker_: Singleton<Promise<(str: string) => boolean>>;
public constructor
(
inputs: string[],
output: string
)
public constructor(config: NestiaApplication.IConfiguration)
{
this.inputs = inputs.map(str => path.resolve(str));
this.output = path.resolve(output);
this.config_ = config;
this.bundle_checker_ = new Singleton(async () =>

@@ -38,3 +30,3 @@ {

{
const relative: string = `${this.output}${path.sep}${file}`;
const relative: string = `${config.output}${path.sep}${file}`;
const stats: fs.Stats = await fs.promises.stat(`${__dirname}${path.sep}bundle${path.sep}${file}`);

@@ -57,9 +49,9 @@

public async sdk(): Promise<void>
public async generate(): Promise<void>
{
// LOAD CONTROLLER FILES
const fileList: string[] = [];
for (const input of this.inputs)
for (const file of this.config_.input.map(str => path.resolve(str)))
{
const found: string[] = await SourceFinder.find(input);
const found: string[] = await SourceFinder.find(file);
const filtered: string[] = await ArrayUtil.asyncFilter(found, file => this.is_not_excluded(file));

@@ -78,3 +70,7 @@

// ANALYZE TYPESCRIPT CODE
const program: tsc.Program = tsc.createProgram(controllerList.map(c => c.file), {});
const program: tsc.Program = tsc.createProgram
(
controllerList.map(c => c.file),
this.config_.compilerOptions || {}
);
const checker: tsc.TypeChecker = program.getTypeChecker();

@@ -93,3 +89,3 @@

// DO GENERATE
await SdkGenerator.generate(this.output, routeList);
await SdkGenerator.generate(this.config_.output, routeList);
}

@@ -99,5 +95,15 @@

{
return file.indexOf(`${this.output}${path.sep}functional`) === -1
return file.indexOf(`${this.config_.output}${path.sep}functional`) === -1
&& (await this.bundle_checker_.get())(file) === false;
}
}
export namespace NestiaApplication
{
export interface IConfiguration
{
input: string[];
output: string;
compilerOptions?: tsc.CompilerOptions;
}
}

@@ -76,3 +76,3 @@ {

},
"include": ["src", "api/structures"]
// "include": ["src", "test"]
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc