Skip to content

Commit

Permalink
feat: api doc
Browse files Browse the repository at this point in the history
  • Loading branch information
RulerChen committed Jun 16, 2024
1 parent 3e5c06e commit 4067774
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 57 deletions.
6 changes: 5 additions & 1 deletion packages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import printProjectName from './scripts/printProjectName.js';
import printTemplate from './scripts/printTemplate.js';
import printLinter from './scripts/printLinter.js';
import printUnitTest from './scripts/printUnitTest.js';
import printDocs from './scripts/printDocs.js';
import printProjectManager from './scripts/printProjectManager.js';
import createProject from './scripts/createProject.js';
import isProjectExist from './utils/isProjectExist.js';
Expand All @@ -25,10 +26,13 @@ main(async (program) => {
let template = program.opts()['template'] || (await printTemplate());
let linter = program.opts()['linter'] || (await printLinter());
let unitTest = program.opts()['unitTest'] || (await printUnitTest());
let apiDoc = program.opts()['apiDoc'] || (await printDocs());
let projectManager = program.opts()['manager'] || (await printProjectManager());

// console.log(template, linter, unitTest, apiDoc, projectManager);

try {
await createProject({ projectName, template, projectManager, linter, unitTest });
await createProject({ projectName, template, projectManager, linter, unitTest, apiDoc });
} catch (error) {
console.log(error);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gen-express-cli",
"version": "1.0.3",
"version": "1.0.4",
"description": "A simple express cli to generate express app simply by running a command",
"main": "index.js",
"type": "module",
Expand Down
14 changes: 14 additions & 0 deletions packages/scripts/createDoc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import fsPromises from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';

export async function createDoc(projectName, template) {
const SOURCE_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), '../', `templates/swagger/${template}`);
const TARGET_PATH = path.join(process.cwd(), projectName);

try {
await fsPromises.cp(SOURCE_PATH, TARGET_PATH, { recursive: true, force: true });
} catch (error) {
throw error;
}
}
79 changes: 38 additions & 41 deletions packages/scripts/createPackageJson.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,13 @@ import fsPromises from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';

export async function createPackageJson(projectName, template, linter, unitTest) {
export async function createPackageJson(projectName, template, linter, unitTest, apiDoc) {
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const packageJsonTemplate = JSON.parse(await fsPromises.readFile(path.resolve(__dirname, `../templates/package.json`), 'utf-8'));

const packageJson = {
...packageJsonTemplate,
name: projectName,
scripts: {},
dependencies: {
...packageJsonTemplate.dependencies,
},
devDependencies: {
...packageJsonTemplate.devDependencies,
},
};

if (template === 'javascript') {
Expand All @@ -24,58 +17,62 @@ export async function createPackageJson(projectName, template, linter, unitTest)
packageJson.scripts['start'] = 'cross-env NODE_ENV=production node ./src/index.js';
if (linter) {
packageJson.scripts['lint'] = 'eslint ./src/**/*';
packageJson.devDependencies['eslint'] = '~8.56.0';
packageJson.devDependencies['eslint-config-standard'] = '~17.1.0';
packageJson.devDependencies['eslint-plugin-import'] = '~2.29.1';
packageJson.devDependencies['eslint-plugin-prettier'] = '~5.1.3';
packageJson.devDependencies['eslint-config-prettier'] = '~9.1.0';
packageJson.devDependencies['prettier'] = '~3.2.5';
packageJson.scripts['format'] = 'prettier --write ./**/*.{js,json}';
packageJson.devDependencies['eslint'] = '^8';
packageJson.devDependencies['eslint-config-standard'] = '^17';
packageJson.devDependencies['eslint-plugin-import'] = '^2';
packageJson.devDependencies['eslint-plugin-prettier'] = '^5';
packageJson.devDependencies['eslint-config-prettier'] = '^9';
packageJson.devDependencies['prettier'] = '^3';
}
if (unitTest === 'jest') {
if (linter) packageJson.devDependencies['eslint-plugin-jest'] = '~27.6.3';
if (linter) packageJson.devDependencies['eslint-plugin-jest'] = '^27';
packageJson.scripts['test'] =
'jest --coverage=true -w=1 --forceExit --detectOpenHandles --watchAll=false --testPathPattern=src/__tests__';
packageJson.devDependencies['jest'] = '~29.7.0';
packageJson.devDependencies['jest'] = '^29';
}
if (e2eTest === 'supertest') {
packageJson.scripts['test:e2e'] = 'node --experimental-vm-modules node_modules/jest/bin/jest.js --testPathPattern=src/e2e';
packageJson.devDependencies['supertest'] = '~6.3.4';
if (apiDoc) {
packageJson.scripts['swagger'] = 'node swagger.js';
packageJson.devDependencies['swagger-autogen'] = '^2';
packageJson.devDependencies['swagger-ui-express'] = '^5';
}
}

if (template === 'typescript') {
packageJson.scripts['dev'] = 'cross-env NODE_ENV=development nodemon --exec ts-node ./src/index.ts';
packageJson.scripts['build'] = 'tsc';
packageJson.scripts['start'] = 'cross-env NODE_ENV=production node ./build/index.js';
packageJson.devDependencies['typescript'] = '~5.3.3';
packageJson.devDependencies['ts-node'] = '~10.9.2';
packageJson.devDependencies['@types/cors'] = '~2.8.17';
packageJson.devDependencies['@types/express'] = '~4.17.21';
packageJson.devDependencies['@types/node'] = '~20.11.16';
packageJson.devDependencies['typescript'] = '^5';
packageJson.devDependencies['ts-node'] = '^10';
packageJson.devDependencies['@types/cors'] = '^2';
packageJson.devDependencies['@types/express'] = '^4';
packageJson.devDependencies['@types/node'] = '^20';
if (linter) {
packageJson.scripts['lint'] = 'eslint ./src/**/*';
packageJson.devDependencies['eslint'] = '~8.56.0';
packageJson.devDependencies['@typescript-eslint/eslint-plugin'] = '~6.20.0';
packageJson.devDependencies['eslint-config-prettier'] = '~9.1.0';
packageJson.devDependencies['eslint-config-standard-with-typescript'] = '~43.0.1';
packageJson.devDependencies['eslint-plugin-import'] = '~2.29.1';
packageJson.devDependencies['eslint-plugin-n'] = '~16.6.2';
packageJson.devDependencies['eslint-plugin-prettier'] = '~5.1.3';
packageJson.devDependencies['eslint-plugin-promise'] = '~6.1.1';
packageJson.devDependencies['prettier'] = '~3.2.5';
packageJson.scripts['format'] = 'prettier --write ./**/*.{ts,json}';
packageJson.devDependencies['eslint'] = '^8';
packageJson.devDependencies['@typescript-eslint/eslint-plugin'] = '^6';
packageJson.devDependencies['eslint-config-prettier'] = '^9';
packageJson.devDependencies['eslint-config-standard-with-typescript'] = '^43';
packageJson.devDependencies['eslint-plugin-import'] = '^2';
packageJson.devDependencies['eslint-plugin-n'] = '^16';
packageJson.devDependencies['eslint-plugin-prettier'] = '^5';
packageJson.devDependencies['eslint-plugin-promise'] = '^6';
packageJson.devDependencies['prettier'] = '^3';
}
if (unitTest === 'jest') {
if (linter) packageJson.devDependencies['eslint-plugin-jest'] = '~27.6.3';
if (linter) packageJson.devDependencies['eslint-plugin-jest'] = '^27';
packageJson.scripts['test'] =
'jest --coverage=true -w=1 --forceExit --detectOpenHandles --watchAll=false --testPathPattern=src/__tests__';
packageJson.devDependencies['jest'] = '~29.7.0';
packageJson.devDependencies['ts-jest'] = '~29.1.2';
packageJson.devDependencies['@types/jest'] = '~29.5.12';
packageJson.devDependencies['jest'] = '^29';
packageJson.devDependencies['ts-jest'] = '^29';
packageJson.devDependencies['@types/jest'] = '^29';
}
if (e2eTest === 'supertest') {
packageJson.scripts['test:e2e'] = 'node --experimental-vm-modules node_modules/jest/bin/jest.js --testPathPattern=src/e2e';
packageJson.devDependencies['supertest'] = '~6.3.4';
packageJson.devDependencies['@types/supertest'] = '~6.0.2';
if (apiDoc) {
packageJson.scripts['swagger'] = 'ts-node swagger.ts';
packageJson.devDependencies['swagger-autogen'] = '^2';
packageJson.devDependencies['swagger-ui-express'] = '^5';
packageJson.devDependencies['@types/swagger-ui-express'] = '^4';
}
}

Expand Down
17 changes: 11 additions & 6 deletions packages/scripts/createProject.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,43 @@ import { createLinter } from './createLinter.js';
import { createPackageJson } from './createPackageJson.js';
import { createTsConfig } from './createTsConfig.js';
import { createUnitTest } from './createUnitTest.js';
import { createDoc } from './createDoc.js';

import { runCommand } from '../utils/exec.js';

async function createStructure({ projectName, template, linter, unitTest }) {
async function createStructure({ projectName, template, linter, unitTest, apiDoc }) {
try {
await createBase(projectName, template);
await createPackageJson(projectName, template, linter, unitTest);
await createPackageJson(projectName, template, linter, unitTest, apiDoc);
if (linter) await createLinter(projectName, template, unitTest);
if (template === 'typescript') await createTsConfig(projectName, unitTest);
if (unitTest === 'jest') await createUnitTest(projectName, template, unitTest);
if (apiDoc) await createDoc(projectName, template);
} catch (error) {
throw error;
}
}

async function installDependencies(projectName, projectManager) {
async function installDependencies(projectName, projectManager, linter) {
if (projectManager === 'npm') {
await runCommand(`cd ${projectName} && npm install && cd ..`);
if (linter) await runCommand(`cd ${projectName} && npm run format && cd ..`);
} else if (projectManager === 'yarn') {
await runCommand(`cd ${projectName} && yarn && cd ..`);
if (linter) await runCommand(`cd ${projectName} && yarn format && cd ..`);
} else if (projectManager === 'pnpm') {
await runCommand(`cd ${projectName} && pnpm install && cd ..`);
if (linter) await runCommand(`cd ${projectName} && pnpm run format && cd ..`);
}
}

export default async function createProject({ projectName, template, projectManager, linter, unitTest }) {
export default async function createProject({ projectName, template, projectManager, linter, unitTest, apiDoc }) {
const spinner = createSpinner('Creating project...');
try {
spinner.start();

await createStructure({ projectName, template, linter, unitTest });
await installDependencies(projectName, projectManager);
await createStructure({ projectName, template, linter, unitTest, apiDoc });
await installDependencies(projectName, projectManager, linter);

spinner.success({ text: 'Project created successfully' });
} catch (error) {
Expand Down
3 changes: 2 additions & 1 deletion packages/scripts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ export default function main(callback) {
.version(APP_VERSION, '-v, --version', 'output the current version')
.arguments('[project-name]', 'project name')
.addOption(new Option('-t, --template <template-name>', 'choose express template').choices(Object.values(TEMPLATES)))
.addOption(new Option('-l, --linter <linter-name>', 'choose linter').choices(['eslint', 'none']))
.addOption(new Option('-l, --linter <linter-name>', 'choose linter').default(false))
.addOption(new Option('-u, --unit-test <unit-test-name>', 'choose unit test').choices(Object.values(UNIT_TEST)))
.addOption(new Option('-a, --api-doc', 'use swagger for API documentation').default(false))
.action(() => callback(program));

program.parse(process.argv);
Expand Down
12 changes: 12 additions & 0 deletions packages/scripts/printDocs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import inquirer from 'inquirer';

export default async function printDocs() {
const iq = await inquirer.prompt({
name: 'apiDoc',
type: 'confirm',
message: 'Do you want to use swagger for API documentation?',
default: false,
});

return iq.apiDoc;
}
2 changes: 1 addition & 1 deletion packages/scripts/printUnitTest.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import inquirer from 'inquirer';
import { UNIT_TEST } from '../variables/templates.js';

export default async function printProjectManager() {
export default async function printUnitTest() {
const iq = await inquirer.prompt({
name: 'unitTest',
type: 'list',
Expand Down
10 changes: 5 additions & 5 deletions packages/templates/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
"author": "",
"license": "ISC",
"dependencies": {
"cors": "~2.8.5",
"dotenv": "~16.4.1",
"express": "~4.18.2"
"cors": "^2",
"dotenv": "^16",
"express": "^4"
},
"devDependencies": {
"cross-env": "~7.0.3",
"nodemon": "~3.0.3"
"cross-env": "^7",
"nodemon": "^3"
}
}
26 changes: 26 additions & 0 deletions packages/templates/swagger/javascript/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import express from 'express';
import dotenv from 'dotenv';
import cors from 'cors';

import swaggerUi from 'swagger-ui-express';
import swaggerDocument from '../apidoc.json';

import routes from './routes/index';

dotenv.config();

const app = express();
const PORT = process.env.PORT ?? 8000;

app.use(cors());
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.use(`/api`, routes);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

export default app;
17 changes: 17 additions & 0 deletions packages/templates/swagger/javascript/swagger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import swaggerAutogen from 'swagger-autogen';

const doc = {
info: {
title: 'Gen Express CLI',
description: 'Gen Express CLI API Documentation',
},
host: 'localhost:8000/api',
};

const outputFile = './apidoc.json';
const routes = ['./src/routes/index.js'];

/* NOTE: If you are using the express Router, you must pass in the 'routes' only the
root file where the route starts, such as index.js, app.js, routes.js, etc ... */

swaggerAutogen()(outputFile, routes, doc);
27 changes: 27 additions & 0 deletions packages/templates/swagger/typescript/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import express from 'express';
import dotenv from 'dotenv';
import cors from 'cors';

import swaggerUi from 'swagger-ui-express';
import swaggerDocument from '../apidoc.json';

import routes from './routes/index';

dotenv.config();

const app = express();
const PORT = process.env.PORT ?? 8000;

app.use(cors());
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.use(`/api`, routes);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

export default app;
18 changes: 18 additions & 0 deletions packages/templates/swagger/typescript/swagger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import swaggerAutogen from 'swagger-autogen';

const doc = {
info: {
title: 'Gen Express CLI',
description: 'Gen Express CLI API Documentation',
},
host: 'localhost:8000/api',
};

const outputFile = './apidoc.json';
const routes = ['./src/routes/index.ts'];

/* NOTE: If you are using the express Router, you must pass in the 'routes' only the
root file where the route starts, such as index.js, app.js, routes.js, etc ... */

// eslint-disable-next-line @typescript-eslint/no-floating-promises
swaggerAutogen()(outputFile, routes, doc);
3 changes: 2 additions & 1 deletion packages/templates/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
"target": "es6",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"resolveJsonModule": true,
"baseUrl": "src",
"outDir": "build",
"sourceMap": true,
"strict": true,
"esModuleInterop": true,
"types": ["node"]
},
"include": ["process.env.d.ts", "./**/*.ts"],
"include": ["process.env.d.ts", "./**/*.ts", "swagger/javascript/swagger.js"],
"exclude": ["node_modules"]
}

0 comments on commit 4067774

Please sign in to comment.