swagshot 0.1.0

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.
Files changed (68) hide show
  1. package/README.md +182 -0
  2. package/dist/cli/commands/config.d.ts +2 -0
  3. package/dist/cli/commands/config.d.ts.map +1 -0
  4. package/dist/cli/commands/config.js +34 -0
  5. package/dist/cli/commands/config.js.map +1 -0
  6. package/dist/cli/commands/generate.d.ts +9 -0
  7. package/dist/cli/commands/generate.d.ts.map +1 -0
  8. package/dist/cli/commands/generate.js +74 -0
  9. package/dist/cli/commands/generate.js.map +1 -0
  10. package/dist/cli/commands/init.d.ts +4 -0
  11. package/dist/cli/commands/init.d.ts.map +1 -0
  12. package/dist/cli/commands/init.js +104 -0
  13. package/dist/cli/commands/init.js.map +1 -0
  14. package/dist/cli/commands/list.d.ts +6 -0
  15. package/dist/cli/commands/list.d.ts.map +1 -0
  16. package/dist/cli/commands/list.js +26 -0
  17. package/dist/cli/commands/list.js.map +1 -0
  18. package/dist/cli/index.d.ts +3 -0
  19. package/dist/cli/index.d.ts.map +1 -0
  20. package/dist/cli/index.js +40 -0
  21. package/dist/cli/index.js.map +1 -0
  22. package/dist/index.d.ts +3 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +71 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/lib/codeGenerator.d.ts +6 -0
  27. package/dist/lib/codeGenerator.d.ts.map +1 -0
  28. package/dist/lib/codeGenerator.js +331 -0
  29. package/dist/lib/codeGenerator.js.map +1 -0
  30. package/dist/lib/configManager.d.ts +8 -0
  31. package/dist/lib/configManager.d.ts.map +1 -0
  32. package/dist/lib/configManager.js +59 -0
  33. package/dist/lib/configManager.js.map +1 -0
  34. package/dist/lib/detectStructure.d.ts +3 -0
  35. package/dist/lib/detectStructure.d.ts.map +1 -0
  36. package/dist/lib/detectStructure.js +65 -0
  37. package/dist/lib/detectStructure.js.map +1 -0
  38. package/dist/lib/fetchSwagger.d.ts +5 -0
  39. package/dist/lib/fetchSwagger.d.ts.map +1 -0
  40. package/dist/lib/fetchSwagger.js +57 -0
  41. package/dist/lib/fetchSwagger.js.map +1 -0
  42. package/dist/lib/types.d.ts +101 -0
  43. package/dist/lib/types.d.ts.map +1 -0
  44. package/dist/lib/types.js +3 -0
  45. package/dist/lib/types.js.map +1 -0
  46. package/dist/tools/configUpdate.d.ts +2 -0
  47. package/dist/tools/configUpdate.d.ts.map +1 -0
  48. package/dist/tools/configUpdate.js +25 -0
  49. package/dist/tools/configUpdate.js.map +1 -0
  50. package/dist/tools/setup.d.ts +2 -0
  51. package/dist/tools/setup.d.ts.map +1 -0
  52. package/dist/tools/setup.js +63 -0
  53. package/dist/tools/setup.js.map +1 -0
  54. package/dist/tools/sync.d.ts +2 -0
  55. package/dist/tools/sync.d.ts.map +1 -0
  56. package/dist/tools/sync.js +87 -0
  57. package/dist/tools/sync.js.map +1 -0
  58. package/package.json +39 -0
  59. package/src/cli/commands/config.ts +33 -0
  60. package/src/cli/commands/generate.ts +90 -0
  61. package/src/cli/commands/init.ts +76 -0
  62. package/src/cli/commands/list.ts +31 -0
  63. package/src/cli/index.ts +44 -0
  64. package/src/lib/codeGenerator.ts +376 -0
  65. package/src/lib/configManager.ts +54 -0
  66. package/src/lib/detectStructure.ts +66 -0
  67. package/src/lib/fetchSwagger.ts +52 -0
  68. package/src/lib/types.ts +102 -0
@@ -0,0 +1,101 @@
1
+ export interface SwagShotConfig {
2
+ version: string;
3
+ project: {
4
+ root: string;
5
+ apiDir: string;
6
+ typesDir: string;
7
+ hooksDir: string | null;
8
+ outputDir: string;
9
+ };
10
+ style: {
11
+ httpClient: "axios" | "fetch";
12
+ axiosInstance: string | null;
13
+ queryLibrary: "react-query" | "swr" | null;
14
+ namingConvention: "camelCase" | "snake_case";
15
+ };
16
+ swagger?: {
17
+ url: string | null;
18
+ };
19
+ }
20
+ export interface DetectedStructure {
21
+ apiDir: string | null;
22
+ typesDir: string | null;
23
+ hooksDir: string | null;
24
+ httpClient: "axios" | "fetch" | null;
25
+ queryLibrary: "react-query" | "swr" | null;
26
+ axiosInstance: string | null;
27
+ }
28
+ export interface SwaggerSpec {
29
+ openapi?: string;
30
+ swagger?: string;
31
+ info: {
32
+ title: string;
33
+ version: string;
34
+ };
35
+ paths: Record<string, PathItem>;
36
+ components?: {
37
+ schemas?: Record<string, SchemaObject>;
38
+ };
39
+ definitions?: Record<string, SchemaObject>;
40
+ tags?: Array<{
41
+ name: string;
42
+ description?: string;
43
+ }>;
44
+ }
45
+ export interface PathItem {
46
+ get?: Operation;
47
+ post?: Operation;
48
+ put?: Operation;
49
+ patch?: Operation;
50
+ delete?: Operation;
51
+ }
52
+ export interface Operation {
53
+ tags?: string[];
54
+ operationId?: string;
55
+ summary?: string;
56
+ deprecated?: boolean;
57
+ parameters?: Parameter[];
58
+ requestBody?: RequestBody;
59
+ responses: Record<string, Response>;
60
+ }
61
+ export interface Parameter {
62
+ name: string;
63
+ in: "query" | "path" | "header" | "cookie" | "body" | "formData";
64
+ required?: boolean;
65
+ description?: string;
66
+ schema?: SchemaObject;
67
+ type?: string;
68
+ format?: string;
69
+ items?: SchemaObject;
70
+ enum?: unknown[];
71
+ collectionFormat?: string;
72
+ }
73
+ export interface RequestBody {
74
+ required?: boolean;
75
+ content?: Record<string, {
76
+ schema?: SchemaObject;
77
+ }>;
78
+ }
79
+ export interface Response {
80
+ description?: string;
81
+ schema?: SchemaObject;
82
+ content?: Record<string, {
83
+ schema?: SchemaObject;
84
+ }>;
85
+ }
86
+ export interface SchemaObject {
87
+ type?: string;
88
+ format?: string;
89
+ $ref?: string;
90
+ items?: SchemaObject;
91
+ properties?: Record<string, SchemaObject>;
92
+ required?: string[];
93
+ enum?: unknown[];
94
+ description?: string;
95
+ nullable?: boolean;
96
+ allOf?: SchemaObject[];
97
+ oneOf?: SchemaObject[];
98
+ anyOf?: SchemaObject[];
99
+ additionalProperties?: boolean | SchemaObject;
100
+ }
101
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare function handleConfigUpdate(updates: Record<string, string | null>): Promise<string>;
2
+ //# sourceMappingURL=configUpdate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configUpdate.d.ts","sourceRoot":"","sources":["../../src/tools/configUpdate.ts"],"names":[],"mappings":"AAEA,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBhG"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleConfigUpdate = handleConfigUpdate;
4
+ const configManager_js_1 = require("../lib/configManager.js");
5
+ async function handleConfigUpdate(updates) {
6
+ const root = process.cwd();
7
+ try {
8
+ const config = await (0, configManager_js_1.updateConfig)(root, updates);
9
+ return [
10
+ "✅ 설정 업데이트 완료:",
11
+ "```json",
12
+ JSON.stringify(updates, null, 2),
13
+ "```",
14
+ "",
15
+ "현재 설정:",
16
+ "```json",
17
+ JSON.stringify(config, null, 2),
18
+ "```",
19
+ ].join("\n");
20
+ }
21
+ catch (e) {
22
+ return `❌ 설정 업데이트 실패: ${e instanceof Error ? e.message : String(e)}`;
23
+ }
24
+ }
25
+ //# sourceMappingURL=configUpdate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configUpdate.js","sourceRoot":"","sources":["../../src/tools/configUpdate.ts"],"names":[],"mappings":";;AAEA,gDAmBC;AArBD,8DAAuD;AAEhD,KAAK,UAAU,kBAAkB,CAAC,OAAsC;IAC7E,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAE3B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,+BAAY,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACjD,OAAO;YACL,eAAe;YACf,SAAS;YACT,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAChC,KAAK;YACL,EAAE;YACF,QAAQ;YACR,SAAS;YACT,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/B,KAAK;SACN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,iBAAiB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACvE,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function handleSetup(projectRoot?: string): Promise<string>;
2
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/tools/setup.ts"],"names":[],"mappings":"AAKA,wBAAsB,WAAW,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0DvE"}
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.handleSetup = handleSetup;
7
+ const path_1 = __importDefault(require("path"));
8
+ const configManager_js_1 = require("../lib/configManager.js");
9
+ const detectStructure_js_1 = require("../lib/detectStructure.js");
10
+ async function handleSetup(projectRoot) {
11
+ const root = projectRoot ?? process.cwd();
12
+ const existing = await (0, configManager_js_1.readConfig)(root);
13
+ if (existing) {
14
+ return [
15
+ "✅ 이미 설정이 있습니다:",
16
+ "```json",
17
+ JSON.stringify(existing, null, 2),
18
+ "```",
19
+ "",
20
+ "변경하려면 `swagger_config_update` 툴을 사용하거나,",
21
+ "수정할 항목을 알려주세요. 예) 'apiDir를 src/services로 바꿔줘'",
22
+ ].join("\n");
23
+ }
24
+ const detected = await (0, detectStructure_js_1.detectProjectStructure)(root);
25
+ const config = {
26
+ version: "1",
27
+ project: {
28
+ root,
29
+ apiDir: detected.apiDir ?? "src/api",
30
+ typesDir: detected.typesDir ?? "src/types",
31
+ hooksDir: detected.hooksDir,
32
+ outputDir: detected.apiDir ?? "src/api",
33
+ },
34
+ style: {
35
+ httpClient: detected.httpClient ?? "axios",
36
+ axiosInstance: detected.axiosInstance,
37
+ queryLibrary: detected.queryLibrary,
38
+ namingConvention: "camelCase",
39
+ },
40
+ };
41
+ await (0, configManager_js_1.writeConfig)(root, config);
42
+ const lines = [
43
+ "👋 swagshot 초기 설정 완료!",
44
+ "",
45
+ `📂 프로젝트 루트: ${root}`,
46
+ `📁 설정 파일: ${path_1.default.join(root, configManager_js_1.CONFIG_FILE)}`,
47
+ "",
48
+ "감지된 설정:",
49
+ ` • API 함수 폴더: ${config.project.apiDir}${detected.apiDir ? " ✅ 자동 감지" : " ⚠️ 기본값 (수정 가능)"}`,
50
+ ` • 타입 정의 폴더: ${config.project.typesDir}${detected.typesDir ? " ✅ 자동 감지" : " ⚠️ 기본값 (수정 가능)"}`,
51
+ ` • 훅 폴더: ${config.project.hooksDir ?? "없음"}${detected.hooksDir ? " ✅ 자동 감지" : ""}`,
52
+ ` • HTTP 클라이언트: ${config.style.httpClient}${detected.httpClient ? " ✅ 자동 감지" : " ⚠️ 기본값"}`,
53
+ ` • Query 라이브러리: ${config.style.queryLibrary ?? "없음"}${detected.queryLibrary ? " ✅ 자동 감지" : ""}`,
54
+ detected.axiosInstance ? ` • Axios 인스턴스: ${config.style.axiosInstance} ✅ 자동 감지` : "",
55
+ "",
56
+ "잘못된 항목이 있으면 알려주세요.",
57
+ "예) 'typesDir는 src/types/api야'",
58
+ "",
59
+ "이제 `swagger_sync` 툴로 코드를 생성할 수 있습니다!",
60
+ ].filter((l) => l !== undefined);
61
+ return lines.join("\n");
62
+ }
63
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/tools/setup.ts"],"names":[],"mappings":";;;;;AAKA,kCA0DC;AA/DD,gDAAwB;AAExB,8DAA+E;AAC/E,kEAAmE;AAE5D,KAAK,UAAU,WAAW,CAAC,WAAoB;IACpD,MAAM,IAAI,GAAG,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1C,MAAM,QAAQ,GAAG,MAAM,IAAA,6BAAU,EAAC,IAAI,CAAC,CAAC;IAExC,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO;YACL,gBAAgB;YAChB,SAAS;YACT,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YACjC,KAAK;YACL,EAAE;YACF,yCAAyC;YACzC,+CAA+C;SAChD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,IAAA,2CAAsB,EAAC,IAAI,CAAC,CAAC;IAEpD,MAAM,MAAM,GAAmB;QAC7B,OAAO,EAAE,GAAG;QACZ,OAAO,EAAE;YACP,IAAI;YACJ,MAAM,EAAE,QAAQ,CAAC,MAAM,IAAI,SAAS;YACpC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,IAAI,WAAW;YAC1C,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,SAAS,EAAE,QAAQ,CAAC,MAAM,IAAI,SAAS;SACxC;QACD,KAAK,EAAE;YACL,UAAU,EAAE,QAAQ,CAAC,UAAU,IAAI,OAAO;YAC1C,aAAa,EAAE,QAAQ,CAAC,aAAa;YACrC,YAAY,EAAE,QAAQ,CAAC,YAAY;YACnC,gBAAgB,EAAE,WAAW;SAC9B;KACF,CAAC;IAEF,MAAM,IAAA,8BAAW,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAEhC,MAAM,KAAK,GAAG;QACZ,uBAAuB;QACvB,EAAE;QACF,eAAe,IAAI,EAAE;QACrB,aAAa,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,8BAAW,CAAC,EAAE;QAC3C,EAAE;QACF,SAAS;QACT,oBAAoB,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,iBAAiB,EAAE;QAC9F,kBAAkB,MAAM,CAAC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,iBAAiB,EAAE;QAChG,qBAAqB,MAAM,CAAC,OAAO,CAAC,QAAQ,IAAI,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE;QAC5F,mBAAmB,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EAAE;QAC3F,oBAAoB,MAAM,CAAC,KAAK,CAAC,YAAY,IAAI,IAAI,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE;QACjG,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,oBAAoB,MAAM,CAAC,KAAK,CAAC,aAAa,UAAU,CAAC,CAAC,CAAC,EAAE;QACtF,EAAE;QACF,oBAAoB;QACpB,+BAA+B;QAC/B,EAAE;QACF,sCAAsC;KACvC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IAEjC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function handleSync(swaggerUrl: string, tag?: string, includeDeprecated?: boolean): Promise<string>;
2
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/tools/sync.ts"],"names":[],"mappings":"AAMA,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,iBAAiB,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAsF7G"}
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.handleSync = handleSync;
7
+ const path_1 = __importDefault(require("path"));
8
+ const promises_1 = __importDefault(require("fs/promises"));
9
+ const configManager_js_1 = require("../lib/configManager.js");
10
+ const fetchSwagger_js_1 = require("../lib/fetchSwagger.js");
11
+ const codeGenerator_js_1 = require("../lib/codeGenerator.js");
12
+ async function handleSync(swaggerUrl, tag, includeDeprecated = false) {
13
+ const root = process.cwd();
14
+ const config = await (0, configManager_js_1.readConfig)(root);
15
+ if (!config) {
16
+ return [
17
+ "⚠️ 프로젝트 설정이 없습니다.",
18
+ "먼저 `swagger_setup` 툴을 실행해주세요.",
19
+ ].join("\n");
20
+ }
21
+ let spec;
22
+ try {
23
+ spec = await (0, fetchSwagger_js_1.fetchSwaggerSpec)(swaggerUrl);
24
+ }
25
+ catch (e) {
26
+ return `❌ Swagger 스펙을 가져오는 데 실패했습니다.\n${e instanceof Error ? e.message : String(e)}`;
27
+ }
28
+ // If no tag specified, list available tags
29
+ if (!tag) {
30
+ const tags = (0, fetchSwagger_js_1.getControllerTags)(spec);
31
+ if (tags.length === 0) {
32
+ return "❌ Swagger 스펙에 태그가 없습니다.";
33
+ }
34
+ if (tags.length === 1) {
35
+ tag = tags[0];
36
+ }
37
+ else {
38
+ return [
39
+ "📋 사용 가능한 컨트롤러 태그:",
40
+ ...tags.map((t, i) => ` ${i + 1}. ${t}`),
41
+ "",
42
+ "어떤 컨트롤러를 생성할까요?",
43
+ "예) 'payment-controller 생성해줘'",
44
+ "",
45
+ "전체 생성하려면: 'all'",
46
+ ].join("\n");
47
+ }
48
+ }
49
+ // Handle 'all' tag
50
+ const tagsToProcess = tag === "all" ? (0, fetchSwagger_js_1.getControllerTags)(spec) : [tag];
51
+ const writtenFiles = [];
52
+ for (const currentTag of tagsToProcess) {
53
+ const filteredSpec = (0, fetchSwagger_js_1.filterByTag)(spec, currentTag, includeDeprecated);
54
+ const endpointCount = Object.values(filteredSpec.paths).reduce((acc, item) => acc + Object.keys(item).length, 0);
55
+ if (endpointCount === 0) {
56
+ continue;
57
+ }
58
+ // Derive file name from tag
59
+ const baseName = currentTag.replace(/-controller$/, "").replace(/-/g, "-");
60
+ // Generate types
61
+ const typesContent = (0, codeGenerator_js_1.generateTypes)(filteredSpec, currentTag);
62
+ const typesDir = path_1.default.join(root, config.project.typesDir);
63
+ await promises_1.default.mkdir(typesDir, { recursive: true });
64
+ const typesFile = path_1.default.join(typesDir, `${baseName}.ts`);
65
+ await promises_1.default.writeFile(typesFile, typesContent, "utf-8");
66
+ writtenFiles.push(typesFile);
67
+ // Generate API file
68
+ const apiContent = (0, codeGenerator_js_1.generateApiFile)(filteredSpec, currentTag, config);
69
+ const apiDir = path_1.default.join(root, config.project.outputDir);
70
+ await promises_1.default.mkdir(apiDir, { recursive: true });
71
+ const apiFile = path_1.default.join(apiDir, `${baseName}.ts`);
72
+ await promises_1.default.writeFile(apiFile, apiContent, "utf-8");
73
+ writtenFiles.push(apiFile);
74
+ }
75
+ if (writtenFiles.length === 0) {
76
+ return `⚠️ 태그 '${tag}'에 해당하는 엔드포인트를 찾을 수 없습니다.`;
77
+ }
78
+ return [
79
+ `✅ 코드 생성 완료!`,
80
+ `📌 처리된 태그: ${tagsToProcess.join(", ")}`,
81
+ `📁 생성된 파일 (${writtenFiles.length}개):`,
82
+ ...writtenFiles.map((f) => ` - ${path_1.default.relative(root, f)}`),
83
+ "",
84
+ "생성된 코드를 확인하고 필요한 경우 수정해주세요.",
85
+ ].join("\n");
86
+ }
87
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/tools/sync.ts"],"names":[],"mappings":";;;;;AAMA,gCAsFC;AA5FD,gDAAwB;AACxB,2DAA6B;AAC7B,8DAAqD;AACrD,4DAA0F;AAC1F,8DAAyE;AAElE,KAAK,UAAU,UAAU,CAAC,UAAkB,EAAE,GAAY,EAAE,iBAAiB,GAAG,KAAK;IAC1F,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,MAAM,IAAA,6BAAU,EAAC,IAAI,CAAC,CAAC;IAEtC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO;YACL,mBAAmB;YACnB,+BAA+B;SAChC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,IAAI,IAAI,CAAC;IACT,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,IAAA,kCAAgB,EAAC,UAAU,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,iCAAiC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACvF,CAAC;IAED,2CAA2C;IAC3C,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,GAAG,IAAA,mCAAiB,EAAC,IAAI,CAAC,CAAC;QACrC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,yBAAyB,CAAC;QACnC,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;aAAM,CAAC;YACN,OAAO;gBACL,oBAAoB;gBACpB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzC,EAAE;gBACF,iBAAiB;gBACjB,8BAA8B;gBAC9B,EAAE;gBACF,iBAAiB;aAClB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IAED,mBAAmB;IACnB,MAAM,aAAa,GAAG,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,IAAA,mCAAiB,EAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACtE,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,KAAK,MAAM,UAAU,IAAI,aAAa,EAAE,CAAC;QACvC,MAAM,YAAY,GAAG,IAAA,6BAAW,EAAC,IAAI,EAAE,UAAU,EAAE,iBAAiB,CAAC,CAAC;QACtE,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM,CAC5D,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,EAC7C,CAAC,CACF,CAAC;QAEF,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;YACxB,SAAS;QACX,CAAC;QAED,4BAA4B;QAC5B,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAE3E,iBAAiB;QACjB,MAAM,YAAY,GAAG,IAAA,gCAAa,EAAC,YAAY,EAAE,UAAU,CAAC,CAAC;QAC7D,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1D,MAAM,kBAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,QAAQ,KAAK,CAAC,CAAC;QACxD,MAAM,kBAAE,CAAC,SAAS,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;QACrD,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAE7B,oBAAoB;QACpB,MAAM,UAAU,GAAG,IAAA,kCAAe,EAAC,YAAY,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACzD,MAAM,kBAAE,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,cAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,QAAQ,KAAK,CAAC,CAAC;QACpD,MAAM,kBAAE,CAAC,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;QACjD,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,UAAU,GAAG,2BAA2B,CAAC;IAClD,CAAC;IAED,OAAO;QACL,aAAa;QACb,cAAc,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACxC,cAAc,YAAY,CAAC,MAAM,KAAK;QACtC,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,cAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAC3D,EAAE;QACF,6BAA6B;KAC9B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "swagshot",
3
+ "version": "0.1.0",
4
+ "description": "Swagger-powered API & type generator",
5
+ "main": "dist/cli/index.js",
6
+ "bin": {
7
+ "swagshot": "dist/cli/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "ts-node src/cli/index.ts",
12
+ "start": "node dist/cli/index.js",
13
+ "prepare": "npm run build"
14
+ },
15
+ "dependencies": {
16
+ "axios": "^1.6.0",
17
+ "commander": "^12.0.0",
18
+ "zod": "^3.22.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^20.0.0",
22
+ "ts-node": "^10.9.0",
23
+ "typescript": "^5.3.0"
24
+ },
25
+ "files": [
26
+ "dist/",
27
+ "src/"
28
+ ],
29
+ "keywords": ["swagger", "openapi", "codegen", "typescript", "cli"],
30
+ "license": "MIT",
31
+ "engines": {
32
+ "node": ">=18"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/Jyophie/swagshot.git"
37
+ },
38
+ "homepage": "https://github.com/Jyophie/swagshot"
39
+ }
@@ -0,0 +1,33 @@
1
+ import { readConfig, writeConfig } from "../../lib/configManager";
2
+
3
+ export async function configCommand(action: string, key: string, value: string) {
4
+ if (action !== "set") {
5
+ console.error(`❌ 알 수 없는 액션: ${action}. 사용 가능: set`);
6
+ process.exit(1);
7
+ }
8
+
9
+ const root = process.cwd();
10
+ const config = await readConfig(root);
11
+
12
+ if (!config) {
13
+ console.error("❌ 설정 파일이 없습니다. 먼저 실행하세요: swagshot init");
14
+ process.exit(1);
15
+ }
16
+
17
+ const parsedValue = value === "null" ? null : value;
18
+
19
+ if (key in config.project) {
20
+ (config.project as Record<string, unknown>)[key] = parsedValue;
21
+ } else if (key in config.style) {
22
+ (config.style as Record<string, unknown>)[key] = parsedValue;
23
+ } else if (key === "swaggerUrl" || key === "url") {
24
+ config.swagger = { ...config.swagger, url: parsedValue };
25
+ } else {
26
+ console.error(`❌ 알 수 없는 설정 키: ${key}`);
27
+ console.log("사용 가능한 키: apiDir, typesDir, hooksDir, outputDir, httpClient, axiosInstance, queryLibrary, swaggerUrl");
28
+ process.exit(1);
29
+ }
30
+
31
+ await writeConfig(root, config);
32
+ console.log(`✅ ${key} = ${value}`);
33
+ }
@@ -0,0 +1,90 @@
1
+ import path from "path";
2
+ import fs from "fs/promises";
3
+ import { readConfig } from "../../lib/configManager";
4
+ import { fetchSwaggerSpec, filterByTag, getControllerTags } from "../../lib/fetchSwagger";
5
+ import { generateTypes, generateApiFile } from "../../lib/codeGenerator";
6
+
7
+ interface GenerateOptions {
8
+ url: string;
9
+ tag?: string;
10
+ all?: boolean;
11
+ includeDeprecated?: boolean;
12
+ }
13
+
14
+ export async function generateCommand(options: GenerateOptions) {
15
+ const root = process.cwd();
16
+ const config = await readConfig(root);
17
+
18
+ if (!config) {
19
+ console.error("❌ 설정 파일이 없습니다. 먼저 실행하세요: swagshot init");
20
+ process.exit(1);
21
+ }
22
+
23
+ console.log(`\n📡 Swagger 스펙 가져오는 중... ${options.url}`);
24
+
25
+ let spec;
26
+ try {
27
+ spec = await fetchSwaggerSpec(options.url);
28
+ } catch (e) {
29
+ console.error(`❌ Swagger 스펙을 가져오지 못했습니다.\n${e instanceof Error ? e.message : String(e)}`);
30
+ process.exit(1);
31
+ }
32
+
33
+ const allTags = getControllerTags(spec);
34
+
35
+ // 태그 미지정 시 목록 출력 후 종료
36
+ if (!options.tag && !options.all) {
37
+ console.log("\n📋 사용 가능한 컨트롤러 태그:");
38
+ allTags.forEach((t, i) => console.log(` ${i + 1}. ${t}`));
39
+ console.log("\n사용법:");
40
+ console.log(` swagshot generate --url "${options.url}" --tag <태그명>`);
41
+ console.log(` swagshot generate --url "${options.url}" --all`);
42
+ return;
43
+ }
44
+
45
+ const tagsToProcess = options.all ? allTags : [options.tag!];
46
+ const writtenFiles: string[] = [];
47
+
48
+ for (const tag of tagsToProcess) {
49
+ if (!allTags.includes(tag)) {
50
+ console.error(`❌ 태그를 찾을 수 없습니다: "${tag}"`);
51
+ console.log("사용 가능한 태그:", allTags.join(", "));
52
+ process.exit(1);
53
+ }
54
+
55
+ const filteredSpec = filterByTag(spec, tag, options.includeDeprecated ?? false);
56
+ const endpointCount = Object.values(filteredSpec.paths).reduce(
57
+ (acc, item) => acc + Object.keys(item).length, 0
58
+ );
59
+
60
+ if (endpointCount === 0) continue;
61
+
62
+ const baseName = tag.replace(/-controller$/, "");
63
+
64
+ // types 파일 생성
65
+ const typesContent = generateTypes(filteredSpec, tag);
66
+ const typesDir = path.join(root, config.project.typesDir);
67
+ await fs.mkdir(typesDir, { recursive: true });
68
+ const typesFile = path.join(typesDir, `${baseName}.ts`);
69
+ await fs.writeFile(typesFile, typesContent, "utf-8");
70
+ writtenFiles.push(typesFile);
71
+
72
+ // api 파일 생성
73
+ const apiContent = generateApiFile(filteredSpec, tag, config);
74
+ const apiDir = path.join(root, config.project.outputDir);
75
+ await fs.mkdir(apiDir, { recursive: true });
76
+ const apiFile = path.join(apiDir, `${baseName}.ts`);
77
+ await fs.writeFile(apiFile, apiContent, "utf-8");
78
+ writtenFiles.push(apiFile);
79
+
80
+ console.log(`\n✅ ${tag} (${endpointCount}개 엔드포인트)`);
81
+ }
82
+
83
+ if (writtenFiles.length === 0) {
84
+ console.log("⚠️ 생성된 파일이 없습니다.");
85
+ return;
86
+ }
87
+
88
+ console.log(`\n📁 생성된 파일 (${writtenFiles.length}개):`);
89
+ writtenFiles.forEach(f => console.log(` - ${path.relative(root, f)}`));
90
+ }
@@ -0,0 +1,76 @@
1
+ import path from "path";
2
+ import * as readline from "readline/promises";
3
+ import { stdin as input, stdout as output } from "process";
4
+ import { SwagShotConfig } from "../../lib/types";
5
+ import { readConfig, writeConfig, CONFIG_FILE } from "../../lib/configManager";
6
+ import { detectProjectStructure } from "../../lib/detectStructure";
7
+
8
+ export async function initCommand(options: { root?: string }) {
9
+ const root = path.resolve(options.root ?? process.cwd());
10
+ const existing = await readConfig(root);
11
+
12
+ if (existing) {
13
+ console.log("\n✅ 이미 설정 파일이 있습니다:");
14
+ console.log(JSON.stringify(existing, null, 2));
15
+ console.log("\n변경하려면: swagshot config set <key> <value>");
16
+ return;
17
+ }
18
+
19
+ console.log("\n👋 swagshot 초기 설정을 시작합니다.");
20
+ console.log(`📂 프로젝트 루트: ${root}\n`);
21
+
22
+ const detected = await detectProjectStructure(root);
23
+
24
+ const rl = readline.createInterface({ input, output });
25
+
26
+ async function ask(question: string, defaultValue: string): Promise<string> {
27
+ const answer = await rl.question(`${question} (기본값: ${defaultValue}): `);
28
+ return answer.trim() || defaultValue;
29
+ }
30
+
31
+ console.log("자동 감지 결과를 확인해주세요. Enter를 누르면 기본값 사용:\n");
32
+
33
+ const swaggerUrl = await ask("Swagger JSON URL", "");
34
+ const apiDir = await ask("API 함수 폴더", detected.apiDir ?? "src/api");
35
+ const typesDir = await ask("타입 정의 폴더", detected.typesDir ?? "src/types");
36
+ const hooksDir = await ask("훅 폴더 (없으면 빈칸)", detected.hooksDir ?? "");
37
+ const httpClient = await ask("HTTP 클라이언트 (axios/fetch)", detected.httpClient ?? "axios");
38
+ const axiosInstance = httpClient === "axios"
39
+ ? await ask("axios 인스턴스 파일 경로 (없으면 빈칸)", detected.axiosInstance ?? "")
40
+ : "";
41
+ const queryLibrary = await ask("Query 라이브러리 (react-query/swr/없으면 빈칸)", detected.queryLibrary ?? "");
42
+
43
+ rl.close();
44
+
45
+ const config: SwagShotConfig = {
46
+ version: "1",
47
+ project: {
48
+ root,
49
+ apiDir,
50
+ typesDir,
51
+ hooksDir: hooksDir || null,
52
+ outputDir: apiDir,
53
+ },
54
+ style: {
55
+ httpClient: httpClient as "axios" | "fetch",
56
+ axiosInstance: axiosInstance || null,
57
+ queryLibrary: (queryLibrary || null) as "react-query" | "swr" | null,
58
+ namingConvention: "camelCase",
59
+ },
60
+ swagger: {
61
+ url: swaggerUrl || null,
62
+ },
63
+ };
64
+
65
+ await writeConfig(root, config);
66
+
67
+ console.log(`\n✅ 설정 완료! ${path.join(root, CONFIG_FILE)} 저장됨`);
68
+ console.log("\n이제 코드를 생성할 수 있습니다:");
69
+ if (swaggerUrl) {
70
+ console.log(` swagshot list --url "${swaggerUrl}"`);
71
+ console.log(` swagshot generate --url "${swaggerUrl}" --tag <컨트롤러명>`);
72
+ } else {
73
+ console.log(` swagshot list --url <swagger-json-url>`);
74
+ console.log(` swagshot generate --url <swagger-json-url> --tag <컨트롤러명>`);
75
+ }
76
+ }
@@ -0,0 +1,31 @@
1
+ import { fetchSwaggerSpec, getControllerTags } from "../../lib/fetchSwagger";
2
+
3
+ interface ListOptions {
4
+ url: string;
5
+ }
6
+
7
+ export async function listCommand(options: ListOptions) {
8
+ console.log(`\n📡 Swagger 스펙 가져오는 중... ${options.url}`);
9
+
10
+ let spec;
11
+ try {
12
+ spec = await fetchSwaggerSpec(options.url);
13
+ } catch (e) {
14
+ console.error(`❌ Swagger 스펙을 가져오지 못했습니다.\n${e instanceof Error ? e.message : String(e)}`);
15
+ process.exit(1);
16
+ }
17
+
18
+ const tags = getControllerTags(spec);
19
+
20
+ if (tags.length === 0) {
21
+ console.log("❌ 태그가 없습니다.");
22
+ return;
23
+ }
24
+
25
+ console.log(`\n📋 컨트롤러 태그 목록 (${tags.length}개):\n`);
26
+ tags.forEach((t, i) => console.log(` ${i + 1}. ${t}`));
27
+
28
+ console.log("\n사용법:");
29
+ console.log(` swagshot generate --url "${options.url}" --tag <태그명>`);
30
+ console.log(` swagshot generate --url "${options.url}" --all`);
31
+ }
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { initCommand } from "./commands/init";
4
+ import { generateCommand } from "./commands/generate";
5
+ import { listCommand } from "./commands/list";
6
+ import { configCommand } from "./commands/config";
7
+
8
+ const program = new Command();
9
+
10
+ program
11
+ .name("swagshot")
12
+ .description("Swagger-powered API & type generator")
13
+ .version("0.1.0");
14
+
15
+ program
16
+ .command("init")
17
+ .description("프로젝트 초기 설정 (.swagshot.json 생성)")
18
+ .option("--root <path>", "프로젝트 루트 경로 (기본값: 현재 디렉토리)")
19
+ .action(initCommand);
20
+
21
+ program
22
+ .command("generate")
23
+ .description("Swagger 스펙으로 API 함수와 TypeScript 타입 생성")
24
+ .requiredOption("--url <url>", "Swagger JSON URL 또는 파일 경로")
25
+ .option("--tag <tag>", "생성할 컨트롤러 태그 (없으면 목록 표시)")
26
+ .option("--all", "모든 컨트롤러 생성")
27
+ .option("--include-deprecated", "deprecated 엔드포인트 포함")
28
+ .action(generateCommand);
29
+
30
+ program
31
+ .command("list")
32
+ .description("Swagger 스펙의 컨트롤러 태그 목록 조회")
33
+ .requiredOption("--url <url>", "Swagger JSON URL 또는 파일 경로")
34
+ .action(listCommand);
35
+
36
+ program
37
+ .command("config")
38
+ .description("프로젝트 설정 변경")
39
+ .argument("<action>", "set")
40
+ .argument("<key>", "설정 키 (예: apiDir, typesDir, httpClient)")
41
+ .argument("<value>", "설정 값")
42
+ .action(configCommand);
43
+
44
+ program.parse();