next-openapi-gen 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-openapi-gen",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Automatically generate OpenAPI documentation for Next.js API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,11 +0,0 @@
1
- import "swagger-ui-react/swagger-ui.css";
2
- import dynamic from "next/dynamic";
3
- const SwaggerUI = dynamic(() => import("swagger-ui-react"), {
4
- ssr: false,
5
- loading: () => <p>Loading Component...</p>,
6
- });
7
- export default async function ApiDocsPage() {
8
- return (<section>
9
- <SwaggerUI url="/swagger.json"/>
10
- </section>);
11
- }
File without changes
package/dist/cli/init.js DELETED
File without changes
File without changes
package/dist/init.js DELETED
@@ -1,30 +0,0 @@
1
- import path from "path";
2
- import fse from "fs-extra";
3
- import ora from "ora";
4
- // Definicja szablonu dla pliku OpenAPI
5
- const openApiTemplate = {
6
- openapi: "3.0.0",
7
- info: {
8
- title: "API Documentation",
9
- version: "1.0.0",
10
- description: "This is the OpenAPI specification for your project.",
11
- },
12
- servers: [
13
- {
14
- url: "http://localhost:3000",
15
- description: "Local development server",
16
- },
17
- ],
18
- paths: {},
19
- };
20
- export const init = async () => {
21
- const spinner = ora("Initializing project with OpenAPI template...\n").start();
22
- try {
23
- const outputPath = path.join(process.cwd(), "next.openapi.json");
24
- await fse.writeJson(outputPath, openApiTemplate, { spaces: 2 });
25
- spinner.succeed(`OpenAPI template created successfully at ${outputPath}`);
26
- }
27
- catch (error) {
28
- spinner.fail(`Failed to initialize project: ${error.message}`);
29
- }
30
- };
@@ -1,95 +0,0 @@
1
- // import path from "path";
2
- // import fs from "fs-extra";
3
- // // Definicja typu dla konfiguracji OpenAPI
4
- // interface OpenApiConfig {
5
- // openapi: string;
6
- // info: {
7
- // title: string;
8
- // description: string;
9
- // version: string;
10
- // };
11
- // servers: { url: string; description: string }[];
12
- // paths: Record<string, any>;
13
- // components: {
14
- // schemas: Record<string, any>;
15
- // };
16
- // }
17
- // // Domyślne opcje OpenAPI
18
- // const defaultOpenApiConfig: OpenApiConfig = {
19
- // openapi: "3.0.0",
20
- // info: {
21
- // title: "Next.js API",
22
- // description: "This is the API documentation for the Next.js application.",
23
- // version: "1.0.0",
24
- // },
25
- // servers: [
26
- // {
27
- // url: "http://localhost:3000",
28
- // description: "Local server",
29
- // },
30
- // ],
31
- // paths: {},
32
- // components: {
33
- // schemas: {},
34
- // },
35
- // };
36
- // export function generateOpenApiSpec(): void {
37
- // const projectRoot = process.cwd();
38
- // // Ścieżki do API, schematów i modeli
39
- // const apiDir = path.join(projectRoot, "pages/api");
40
- // const schemaDir = path.join(projectRoot, "schemas");
41
- // const modelsDir = path.join(projectRoot, "models");
42
- // // Generowanie pliku next.openapi.json
43
- // const openApiPath = path.join(projectRoot, "next.openapi.json");
44
- // // Generowanie paths i schemas na podstawie plików z katalogów
45
- // const openApiSpec: OpenApiConfig = {
46
- // ...defaultOpenApiConfig,
47
- // paths: generatePathsFromApiDir(apiDir),
48
- // components: {
49
- // schemas: generateSchemasFromDir(schemaDir),
50
- // },
51
- // };
52
- // // Zapisanie pliku next.openapi.json
53
- // fs.writeJsonSync(openApiPath, openApiSpec, { spaces: 2 });
54
- // console.log(`OpenAPI spec generated at: ${openApiPath}`);
55
- // }
56
- // // Funkcja generująca ścieżki na podstawie katalogu API
57
- // function generatePathsFromApiDir(apiDir: string): Record<string, any> {
58
- // const paths: Record<string, any> = {};
59
- // if (fs.existsSync(apiDir)) {
60
- // const files = fs.readdirSync(apiDir);
61
- // files.forEach((file) => {
62
- // const filePath = path.join(apiDir, file);
63
- // if (fs.statSync(filePath).isFile() && file.endsWith(".ts")) {
64
- // const route = `/api/${file.replace(".ts", "")}`;
65
- // paths[route] = {
66
- // get: {
67
- // summary: `Get ${route}`,
68
- // responses: {
69
- // 200: {
70
- // description: "Successful response",
71
- // },
72
- // },
73
- // },
74
- // };
75
- // }
76
- // });
77
- // }
78
- // return paths;
79
- // }
80
- // // Funkcja generująca schematy na podstawie katalogu schematów
81
- // function generateSchemasFromDir(schemaDir: string): Record<string, any> {
82
- // const schemas: Record<string, any> = {};
83
- // if (fs.existsSync(schemaDir)) {
84
- // const files = fs.readdirSync(schemaDir);
85
- // files.forEach((file) => {
86
- // const schemaPath = path.join(schemaDir, file);
87
- // if (fs.statSync(schemaPath).isFile() && file.endsWith(".json")) {
88
- // const schema = fs.readJsonSync(schemaPath);
89
- // const schemaName = file.replace(".json", "");
90
- // schemas[schemaName] = schema;
91
- // }
92
- // });
93
- // }
94
- // return schemas;
95
- // }
@@ -1,201 +0,0 @@
1
- import { parse } from "@babel/parser";
2
- import traverse from "@babel/traverse";
3
- import * as t from "@babel/types";
4
- import fs from "fs";
5
- import path from "path";
6
- import ora from "ora";
7
-
8
- const apiDir = path.resolve("./src/app/api");
9
- const schemaDir = path.resolve("./src/types/schemas");
10
- const swaggerPaths = {};
11
-
12
- function extractSchemaFromZod(schemaNode) {
13
- const properties = {};
14
- schemaNode.arguments.forEach((arg) => {
15
- if (t.isObjectExpression(arg)) {
16
- arg.properties.forEach((prop) => {
17
- if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
18
- // @ts-ignore
19
- const type = prop.value.callee.property.name;
20
- properties[prop.key.name] = { type };
21
- }
22
- });
23
- }
24
- });
25
- return { type: "object", properties };
26
- }
27
-
28
- function findSchemaDefinition(schemaName) {
29
- let schemaNode = null;
30
-
31
- function processSchemaFile(filePath) {
32
- const content = fs.readFileSync(filePath, "utf-8");
33
- const ast = parse(content, {
34
- sourceType: "module",
35
- plugins: ["typescript"],
36
- });
37
-
38
- traverse.default(ast, {
39
- VariableDeclarator(path) {
40
- if (t.isIdentifier(path.node.id, { name: schemaName })) {
41
- if (t.isCallExpression(path.node.init)) {
42
- schemaNode = path.node.init;
43
- }
44
- }
45
- },
46
- });
47
- }
48
-
49
- function scanSchemaDir(dir) {
50
- const files = fs.readdirSync(dir);
51
- files.forEach((file) => {
52
- const filePath = path.join(dir, file);
53
- const stat = fs.statSync(filePath);
54
- if (stat.isDirectory()) {
55
- scanSchemaDir(filePath);
56
- } else if (file.endsWith(".ts")) {
57
- processSchemaFile(filePath);
58
- }
59
- });
60
- }
61
-
62
- scanSchemaDir(schemaDir);
63
- return schemaNode;
64
- }
65
-
66
- function processFile(filePath) {
67
- const content = fs.readFileSync(filePath, "utf-8");
68
- const ast = parse(content, { sourceType: "module", plugins: ["typescript"] });
69
-
70
- traverse.default(ast, {
71
- ExportNamedDeclaration(path) {
72
- const declaration = path.node.declaration;
73
-
74
- if (t.isVariableDeclaration(declaration)) {
75
- declaration.declarations.forEach((decl) => {
76
- if (t.isVariableDeclarator(decl) && t.isIdentifier(decl.id)) {
77
- const varName = decl.id.name;
78
- let schema = {};
79
- let options: any = {};
80
-
81
- if (
82
- varName === "POST" ||
83
- varName === "GET" ||
84
- varName === "PUT" ||
85
- varName === "PATCH" ||
86
- varName === "DELETE"
87
- ) {
88
- const handler = decl.init;
89
-
90
- // Handle schema definition
91
- if (
92
- fs.existsSync(schemaDir) &&
93
- t.isCallExpression(handler) &&
94
- t.isIdentifier(handler.callee, { name: "withAPI" })
95
- ) {
96
- const [schemaIdentifier] = handler.arguments;
97
-
98
- if (t.isIdentifier(schemaIdentifier)) {
99
- const schemaNode = findSchemaDefinition(
100
- schemaIdentifier.name
101
- );
102
-
103
- const optionsNode = schemaNode.arguments[0];
104
-
105
- if (schemaNode) {
106
- // @TODO: add z.array tracking
107
- if (t.isObjectExpression(optionsNode)) {
108
- schema = extractSchemaFromZod(schemaNode);
109
-
110
- options = optionsNode.properties.reduce((acc, prop) => {
111
- if (
112
- t.isObjectProperty(prop) &&
113
- t.isIdentifier(prop.key)
114
- ) {
115
- // @ts-ignore
116
- acc[prop.key.name] = prop.value.callee.property.name;
117
- }
118
- return acc;
119
- }, {});
120
- }
121
- }
122
- }
123
- }
124
-
125
- const method = varName.toLowerCase();
126
- const routePath = filePath
127
- .replace(apiDir, "")
128
- .replace("route.ts", "")
129
- .replaceAll("\\", "/")
130
- .replace(/\/$/, "");
131
-
132
- const rootPath = routePath.split("/")[1];
133
-
134
- if (!swaggerPaths[routePath]) {
135
- swaggerPaths[routePath] = {};
136
- }
137
-
138
- swaggerPaths[routePath][method] = {
139
- operationId: options.opId,
140
- description: options.desc,
141
- tags: [rootPath],
142
- requestBody: {
143
- content: {
144
- "application/json": {
145
- schema,
146
- },
147
- },
148
- },
149
- responses: {
150
- 200: {
151
- description: "Successful response",
152
- content: {
153
- "application/json": {
154
- schema: options.res,
155
- },
156
- },
157
- },
158
- 400: {
159
- description: "Validation error",
160
- },
161
- 500: {
162
- description: "Server error",
163
- },
164
- },
165
- };
166
- }
167
- }
168
- });
169
- }
170
- },
171
- });
172
- }
173
-
174
- function scanDir(dir) {
175
- const files = fs.readdirSync(dir);
176
- files.forEach((file) => {
177
- const filePath = path.join(dir, file);
178
- const stat = fs.statSync(filePath);
179
- if (stat.isDirectory()) {
180
- scanDir(filePath);
181
- } else if (file.endsWith(".ts")) {
182
- processFile(filePath);
183
- }
184
- });
185
- }
186
-
187
- export async function generateOpenapiSpec() {
188
- const spinner = ora("Generating openapi specification...\n").start();
189
-
190
- scanDir(apiDir);
191
-
192
- const openapiPath = path.resolve("./next.openapi.json");
193
- const openapiSpec = JSON.parse(fs.readFileSync(openapiPath, "utf-8"));
194
-
195
- openapiSpec.paths = swaggerPaths;
196
-
197
- const outputPath = path.resolve(openapiSpec.outputPath);
198
- fs.writeFileSync(outputPath, JSON.stringify(openapiSpec, null, 2));
199
-
200
- spinner.succeed(`Swagger spec generated at ${outputPath}`);
201
- }
@@ -1,110 +0,0 @@
1
- import path from "path";
2
- import fse from "fs-extra";
3
- import fs from "fs";
4
- import ora from "ora";
5
- import { exec } from "child_process";
6
- import util from "util";
7
-
8
- const execPromise = util.promisify(exec);
9
-
10
- const spinner = ora("Initializing project with OpenAPI template...\n");
11
-
12
- const openApiTemplate = {
13
- openapi: "3.0.0",
14
- info: {
15
- title: "API Documentation",
16
- version: "1.0.0",
17
- description: "This is the OpenAPI specification for your project.",
18
- },
19
- servers: [
20
- {
21
- url: "http://localhost:3000",
22
- description: "Local development server",
23
- },
24
- ],
25
- paths: {},
26
- apiPath: "./src/app/api",
27
- docsUrl: "api-docs",
28
- ui: "swagger",
29
- outputPath: "./public/swagger.json",
30
- };
31
-
32
- const getPackageManager = async () => {
33
- if (fs.existsSync(path.join(process.cwd(), "yarn.lock"))) {
34
- return "yarn";
35
- }
36
- if (fs.existsSync(path.join(process.cwd(), "pnpm-lock.yaml"))) {
37
- return "pnpm";
38
- }
39
- return "npm";
40
- };
41
-
42
- async function createDocsPage() {
43
- const paths = ["app", "api-docs"];
44
- const srcPath = path.join(process.cwd(), "src");
45
-
46
- if (fs.existsSync(srcPath)) {
47
- paths.unshift("src");
48
- }
49
-
50
- const docsDir = path.join(process.cwd(), ...paths);
51
- await fs.promises.mkdir(docsDir, { recursive: true });
52
-
53
- const swaggerComponent = `
54
- import "swagger-ui-react/swagger-ui.css";
55
-
56
- import dynamic from "next/dynamic";
57
-
58
- const SwaggerUI = dynamic(() => import("swagger-ui-react"), {
59
- ssr: false,
60
- loading: () => <p>Loading Component...</p>,
61
- });
62
-
63
- export default async function ApiDocsPage() {
64
- return (
65
- <section>
66
- <SwaggerUI url="/swagger.json" />
67
- </section>
68
- );
69
- }
70
- `;
71
-
72
- const componentPath = path.join(docsDir, "page.tsx");
73
- await fs.promises.writeFile(componentPath, swaggerComponent.trim());
74
- spinner.succeed(`Created ${paths.join("/")}/page.tsx for Swagger UI.`);
75
- }
76
-
77
- async function installSwagger() {
78
- const packageManager = await getPackageManager();
79
- const installCmd = `${packageManager} ${
80
- packageManager === "npm" ? "install" : "add"
81
- }`;
82
-
83
- spinner.succeed("Installing swagger-ui-react...");
84
- const resp = await execPromise(`${installCmd} swagger-ui-react`);
85
- spinner.succeed("Successfully installed swagger-ui-react.");
86
- }
87
-
88
- function extendOpenApiTemplate(spec, options) {
89
- spec.ui = options.ui ?? spec.ui;
90
- spec.docsUrl = options.docsUrl ?? spec.docsUrl;
91
- }
92
-
93
- export async function init(ui: string, docsUrl: string) {
94
- spinner.start();
95
-
96
- try {
97
- const outputPath = path.join(process.cwd(), "next.openapi.json");
98
- extendOpenApiTemplate(openApiTemplate, { docsUrl, ui });
99
-
100
- await fse.writeJson(outputPath, openApiTemplate, { spaces: 2 });
101
- spinner.succeed(`Created OpenAPI template in next.openapi.json`);
102
-
103
- if (ui === "swagger") {
104
- createDocsPage();
105
- installSwagger();
106
- }
107
- } catch (error) {
108
- spinner.fail(`Failed to initialize project: ${error.message}`);
109
- }
110
- }
package/src/index.ts DELETED
@@ -1,27 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from "commander";
4
-
5
- import { init } from "./commands/init.js";
6
- import { generateOpenapiSpec } from "./commands/generate-openapi-spec.js";
7
-
8
- const program = new Command();
9
-
10
- program
11
- .name("next-openapi-gen")
12
- .version("0.0.1")
13
- .description(
14
- "Super fast and easy way to generate OpenAPI documentation for Next.js"
15
- );
16
-
17
- program
18
- .command("init <ui> <docs-url>")
19
- .description("Initialize a openapi specification")
20
- .action(init);
21
-
22
- program
23
- .command("generate")
24
- .description("Generate a specification based on api routes")
25
- .action(generateOpenapiSpec);
26
-
27
- program.parse(process.argv);
package/todo.md DELETED
@@ -1,12 +0,0 @@
1
- next-openapi-gen
2
- next-apidoc-gen
3
-
4
-
5
- OpenAPI generator for Next.js
6
-
7
- npx create-email init my-project
8
- npx create-email generate component
9
-
10
-
11
- node ./dist/index.js init swagger api-docs
12
- node ./dist/index.js generate
package/tsconfig.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "esnext",
4
- "lib": ["es2022"],
5
- // "target": "ESNext",
6
- "module": "esnext",
7
- "outDir": "./dist",
8
- "rootDir": "./src",
9
- "esModuleInterop": true,
10
- "moduleResolution": "node",
11
- "strict": false,
12
- "types": ["node"],
13
- "skipLibCheck": true
14
- },
15
- "include": ["src"],
16
- "exclude": ["dist", "build", "node_modules"]
17
- }