UNPKG

ts5deco-express-controller

Version:

TypeScript 5 Modern Decorator Express Controller Framework

291 lines (273 loc) 13.2 kB
#!/usr/bin/env node #!/usr/bin/env node "use strict";var E=Object.create;var u=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var k=Object.getPrototypeOf,A=Object.prototype.hasOwnProperty;var S=(t,e)=>()=>(t&&(e=t(t=0)),e);var $=(t,e)=>{for(var s in e)u(t,s,{get:e[s],enumerable:!0})},O=(t,e,s,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of I(e))!A.call(t,o)&&o!==s&&u(t,o,{get:()=>e[o],enumerable:!(a=U(e,o))||a.enumerable});return t};var h=(t,e,s)=>(s=t!=null?E(k(t)):{},O(e||!t||!t.__esModule?u(s,"default",{value:t,enumerable:!0}):s,t));var T={};$(T,{initProject:()=>D});async function D(t="."){let e=c.default.resolve(t),s=c.default.join(e,"api");await l.default.mkdir(s,{recursive:!0}),await l.default.writeFile(c.default.join(s,"openapi.yaml"),`openapi: 3.0.0 info: title: My API version: 1.0.0 description: API for my application servers: - url: http://localhost:3000/api description: Local development server paths: /users: get: summary: Get all users operationId: getUsers parameters: - name: page in: query schema: type: integer default: 1 - name: limit in: query schema: type: integer default: 10 responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/PaginatedUserResponse' '500': description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' post: summary: Create a new user operationId: createUser requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserRequest' responses: '201': description: User created successfully content: application/json: schema: $ref: '#/components/schemas/User' '400': description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' /users/{id}: get: summary: Get a user by ID operationId: getUserById parameters: - name: id in: path required: true schema: type: string responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/User' '404': description: User not found content: application/json: schema: $ref: '#/components/schemas/Error' components: schemas: User: type: object required: - id - name - email properties: id: type: string example: "123" name: type: string example: "John Doe" email: type: string format: email example: "john@example.com" createdAt: type: string format: date-time updatedAt: type: string format: date-time CreateUserRequest: type: object required: - name - email - password properties: name: type: string example: "John Doe" email: type: string format: email example: "john@example.com" password: type: string format: password minLength: 8 Error: type: object required: - error - message properties: error: type: string example: "NOT_FOUND" message: type: string example: "The requested resource was not found" code: type: string example: "USER_NOT_FOUND" statusCode: type: integer example: 404 PaginatedUserResponse: type: object required: - data - total - page - limit - hasNext - hasPrev properties: data: type: array items: $ref: '#/components/schemas/User' total: type: integer example: 100 page: type: integer example: 1 limit: type: integer example: 10 hasNext: type: boolean example: true hasPrev: type: boolean example: false `);let o=c.default.join(e,"package.json"),n=!1;try{let r=JSON.parse(await l.default.readFile(o,"utf8"));r.scripts||(r.scripts={}),r.scripts["generate:types"]||(r.scripts["generate:types"]="ts5deco-express-controller generate",n=!0),r.scripts.generate||(r.scripts.generate="npm run generate:types",n=!0),n&&(await l.default.writeFile(o,JSON.stringify(r,null,2)),console.log("\u2705 Updated package.json with recommended scripts"))}catch{console.log("\u{1F4A1} Recommended package.json scripts:"),console.log(' "generate:types": "ts5deco-express-controller generate"'),console.log(' "generate": "npm run generate:types"')}try{let r=c.default.join(e,".gitignore"),d="";try{d=await l.default.readFile(r,"utf8")}catch{}d.includes("src/types/generated/")||(d+=` # Generated OpenAPI types src/types/generated/ `,await l.default.writeFile(r,d),console.log("\u2705 Updated .gitignore to exclude generated types"))}catch{console.log("\u{1F4A1} Recommended .gitignore addition:"),console.log(" src/types/generated/")}console.log(` \u{1F4C1} Created files:`),console.log(` ${c.default.relative(process.cwd(),c.default.join(s,"openapi.yaml"))}`)}var l,c,w=S(()=>{"use strict";l=h(require("fs/promises")),c=h(require("path"))});var m=require("commander");var p=h(require("fs/promises")),i=h(require("path")),f=require("child_process");async function x(t){let{input:e,outputDir:s,apiTypesPath:a,utilsPath:o,generateAliases:n=!0,generateUtils:r=!0,openapiTypescriptOptions:d=[]}=t;try{await p.default.access(e)}catch{throw new Error(`OpenAPI specification file not found: ${e}`)}await p.default.mkdir(s,{recursive:!0});let y=i.default.join(s,"api.d.ts");console.log(`\u{1F4C4} Processing ${e}...`);let j=["npx openapi-typescript",`"${e}"`,"-o",`"${y}"`,...d].join(" ");try{(0,f.execSync)(j,{stdio:"inherit"}),console.log(`\u2705 Generated types: ${y}`)}catch(g){throw new Error(`Failed to generate types: ${g instanceof Error?g.message:String(g)}`)}let v=await R(y);r&&o&&(await b(o,y),console.log(`\u2705 Generated utility types: ${o}`)),n&&a&&(await q(a,v,o),console.log(`\u2705 Generated API type aliases: ${a}`))}async function R(t){try{let s=(await p.default.readFile(t,"utf8")).match(/schemas:\s*{([^}]+)}/s);if(!s||!s[1])return[];let a=s[1],o=[],n=/(\w+):\s*{/g,r;for(;(r=n.exec(a))!==null;)r[1]&&o.push(r[1]);return o}catch(e){return console.warn("Warning: Could not extract schema names:",e instanceof Error?e.message:String(e)),[]}}async function b(t,e){let a=`/** * OpenAPI \uD0C0\uC785 \uC720\uD2F8\uB9AC\uD2F0 * openapi-typescript\uB85C \uC0DD\uC131\uB41C \uD0C0\uC785\uB4E4\uC744 \uC27D\uAC8C \uC0AC\uC6A9\uD558\uAE30 \uC704\uD55C \uD5EC\uD37C\uB4E4 */ import type { paths, components } from './${i.default.relative(i.default.dirname(t),e).replace(/\.d\.ts$/,"").replace(/\\/g,"/")}'; /** * OpenAPI \uC2A4\uD0A4\uB9C8\uC5D0\uC11C \uD0C0\uC785\uC744 \uCD94\uCD9C\uD558\uB294 \uD5EC\uD37C \uD0C0\uC785 * @example * \`\`\`typescript * type User = ExtractSchema<'User'>; * type ErrorResponse = ExtractSchema<'Error'>; * \`\`\` */ export type ExtractSchema<T extends keyof components['schemas']> = components['schemas'][T]; /** * OpenAPI \uACBD\uB85C\uC5D0\uC11C \uC751\uB2F5 \uD0C0\uC785\uC744 \uCD94\uCD9C\uD558\uB294 \uD5EC\uD37C \uD0C0\uC785 * @example * \`\`\`typescript * type GetUserResponse = ExtractResponse<'/users/{id}', 'get'>; * type CreateUserResponse = ExtractResponse<'/users', 'post', 201>; * \`\`\` */ export type ExtractResponse< TPath extends keyof paths, TMethod extends keyof paths[TPath], TStatus extends number = 200 > = TPath extends keyof paths ? TMethod extends keyof paths[TPath] ? paths[TPath][TMethod] extends { responses: any } ? TStatus extends keyof paths[TPath][TMethod]['responses'] ? paths[TPath][TMethod]['responses'][TStatus] extends { content: { 'application/json': infer T } } ? T : never : never : never : never : never; /** * OpenAPI \uACBD\uB85C\uC5D0\uC11C \uC694\uCCAD \uBCF8\uBB38 \uD0C0\uC785\uC744 \uCD94\uCD9C\uD558\uB294 \uD5EC\uD37C \uD0C0\uC785 */ export type ExtractRequestBody< TPath extends keyof paths, TMethod extends keyof paths[TPath] > = TPath extends keyof paths ? TMethod extends keyof paths[TPath] ? paths[TPath][TMethod] extends { requestBody: { content: { 'application/json': infer T } } } ? T : never : never : never; /** * OpenAPI \uACBD\uB85C\uC5D0\uC11C \uD30C\uB77C\uBBF8\uD130 \uD0C0\uC785\uC744 \uCD94\uCD9C\uD558\uB294 \uD5EC\uD37C \uD0C0\uC785 */ export type ExtractParameters< TPath extends keyof paths, TMethod extends keyof paths[TPath] > = TPath extends keyof paths ? TMethod extends keyof paths[TPath] ? paths[TPath][TMethod] extends { parameters: infer P } ? P : never : never : never; // \uC6D0\uBCF8 \uD0C0\uC785\uB4E4\uB3C4 re-export export type { paths, components }; `;await p.default.mkdir(i.default.dirname(t),{recursive:!0}),await p.default.writeFile(t,a)}async function q(t,e,s){let o=`/** * API \uD0C0\uC785 \uBCC4\uCE6D * OpenAPI \uC2A4\uD399\uC5D0\uC11C \uC0DD\uC131\uB41C \uD0C0\uC785\uB4E4\uC744 \uC27D\uAC8C \uC0AC\uC6A9\uD558\uAE30 \uC704\uD55C \uBCC4\uCE6D \uC815\uC758 * * \uC774 \uD30C\uC77C\uC740 \uC790\uB3D9 \uC0DD\uC131\uB429\uB2C8\uB2E4. \uD544\uC694\uC5D0 \uB530\uB77C \uC218\uC815\uD558\uC5EC \uC0AC\uC6A9\uD558\uC138\uC694. */ ${s?`import type { ExtractSchema } from './${i.default.relative(i.default.dirname(t),s).replace(/\.ts$/,"").replace(/\\/g,"/")}';`:""} /** * \uC77C\uBC18\uC801\uC73C\uB85C \uC0AC\uC6A9\uB418\uB294 \uD0C0\uC785 \uBCC4\uCE6D\uB4E4 * \uD544\uC694\uC5D0 \uB530\uB77C \uCD94\uAC00\uD558\uAC70\uB098 \uC218\uC815\uD558\uC138\uC694 */ ${e.map(n=>{let r=M(n);return r?`export type ${r} = ExtractSchema<'${n}'>;`:`// export type ${n} = ExtractSchema<'${n}'>;`}).join(` `)} /** * \uBAA8\uB4E0 \uC2A4\uD0A4\uB9C8 \uD0C0\uC785\uB4E4 (\uCC38\uACE0\uC6A9) * \uC544\uB798 \uD0C0\uC785\uB4E4\uC744 \uC704\uC640 \uAC19\uC774 \uBCC4\uCE6D\uC73C\uB85C \uB9CC\uB4E4\uC5B4 \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4: * ${e.map(n=>` * - ${n}`).join(` `)} */ `;await p.default.mkdir(i.default.dirname(t),{recursive:!0}),await p.default.writeFile(t,o)}function M(t){return{User:"User",Error:"ErrorResponse",CreateUserRequest:"CreateUserRequest",UpdateUserRequest:"UpdateUserRequest",PaginatedUserResponse:"PaginatedResponse",ApiError:"ApiError",ValidationError:"ValidationError"}[t]||null}var P="0.1.0";m.program.name("ts5deco-express-controller").description("TypeScript 5 Modern Decorator Express Controller Framework CLI").version(P);m.program.command("generate").description("Generate TypeScript types from OpenAPI specification").option("-i, --input <path>","OpenAPI specification file path","./api/openapi.yaml").option("-o, --output <path>","Output directory for generated types","./src/types/generated").option("--api-types <path>","Output path for API type aliases","./src/types/api.ts").option("--utils <path>","Output path for utility types","./src/types/openapi-utils.ts").option("--no-aliases","Skip generating type aliases").option("--no-utils","Skip generating utility types").action(async t=>{try{console.log(`\u{1F680} Generating TypeScript types from OpenAPI specification... `),await x({input:t.input,outputDir:t.output,apiTypesPath:t.apiTypes,utilsPath:t.utils,generateAliases:t.aliases,generateUtils:t.utils}),console.log("\u2728 Type generation completed successfully!")}catch(e){console.error("\u274C Error generating types:",e instanceof Error?e.message:String(e)),process.exit(1)}});m.program.command("init").description("Initialize a new project with example OpenAPI specification").option("-d, --dir <path>","Target directory",".").action(async t=>{try{console.log(`\u{1F527} Initializing project with OpenAPI configuration... `);let{initProject:e}=await Promise.resolve().then(()=>(w(),T));await e(t.dir),console.log("\u2728 Project initialized successfully!"),console.log(` Next steps:`),console.log("1. Edit api/openapi.yaml to match your API"),console.log("2. Run: npx ts5deco-express-controller generate"),console.log("3. Import types in your controllers")}catch(e){console.error("\u274C Error initializing project:",e instanceof Error?e.message:String(e)),process.exit(1)}});m.program.parse(); //# sourceMappingURL=index.js.map