@geekmidas/cli 1.2.1 → 1.2.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/CHANGELOG.md +12 -0
- package/dist/index.cjs +110 -182
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +111 -183
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-DrbBWq0s.mjs → openapi-NthphEWK.mjs} +3 -4
- package/dist/{openapi-DrbBWq0s.mjs.map → openapi-NthphEWK.mjs.map} +1 -1
- package/dist/{openapi-BZP8jkI4.cjs → openapi-ZhO7wwya.cjs} +2 -9
- package/dist/{openapi-BZP8jkI4.cjs.map → openapi-ZhO7wwya.cjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.mts.map +1 -1
- package/dist/openapi.mjs +1 -1
- package/package.json +4 -4
- package/src/__tests__/openapi.spec.ts +385 -5
- package/src/dev/index.ts +71 -107
- package/src/generators/OpenApiTsGenerator.ts +0 -1
- package/src/init/generators/ui.ts +20 -20
- package/src/openapi.ts +2 -1
- package/src/workspace/__tests__/client-generator.spec.ts +472 -19
- package/src/workspace/client-generator.ts +139 -199
package/dist/openapi.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
require('./workspace-BMJE18LV.cjs');
|
|
3
3
|
require('./config-Bayob8pB.cjs');
|
|
4
|
-
const require_openapi = require('./openapi-
|
|
4
|
+
const require_openapi = require('./openapi-ZhO7wwya.cjs');
|
|
5
5
|
|
|
6
6
|
exports.OPENAPI_OUTPUT_PATH = require_openapi.OPENAPI_OUTPUT_PATH;
|
|
7
7
|
exports.generateOpenApi = require_openapi.generateOpenApi;
|
package/dist/openapi.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.d.cts","names":[],"sources":["../src/openapi.ts"],"sourcesContent":[],"mappings":";;;;UASU,cAAA;;AAFiD;AAS3D;AAKA;;AACS,cANI,mBAAA,GAMJ,mBAAA;;AACO;
|
|
1
|
+
{"version":3,"file":"openapi.d.cts","names":[],"sources":["../src/openapi.ts"],"sourcesContent":[],"mappings":";;;;UASU,cAAA;;AAFiD;AAS3D;AAKA;;AACS,cANI,mBAAA,GAMJ,mBAAA;;AACO;AA6BhB;AAAqC,iBA/BrB,oBAAA,CA+BqB,MAAA,EA9B5B,SA8B4B,CAAA,EA7BlC,aA6BkC,GAAA;EAAA,OAC5B,EAAA,OAAA;CAAS;AAER;AAkCV;;;AAEG,iBAvCmB,eAAA,CAuCnB,MAAA,EAtCM,SAsCN,EAAA,QAAA,EAAA;EAAO,MAAA,CAAA,EAAA,OAAA;IApCP;;;;iBAkCmB,cAAA,WACZ,iBACP"}
|
package/dist/openapi.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.d.mts","names":[],"sources":["../src/openapi.ts"],"sourcesContent":[],"mappings":";;;;UASU,cAAA;;AAFiD;AAS3D;AAKA;;AACS,cANI,mBAAA,GAMJ,mBAAA;;AACO;
|
|
1
|
+
{"version":3,"file":"openapi.d.mts","names":[],"sources":["../src/openapi.ts"],"sourcesContent":[],"mappings":";;;;UASU,cAAA;;AAFiD;AAS3D;AAKA;;AACS,cANI,mBAAA,GAMJ,mBAAA;;AACO;AA6BhB;AAAqC,iBA/BrB,oBAAA,CA+BqB,MAAA,EA9B5B,SA8B4B,CAAA,EA7BlC,aA6BkC,GAAA;EAAA,OAC5B,EAAA,OAAA;CAAS;AAER;AAkCV;;;AAEG,iBAvCmB,eAAA,CAuCnB,MAAA,EAtCM,SAsCN,EAAA,QAAA,EAAA;EAAO,MAAA,CAAA,EAAA,OAAA;IApCP;;;;iBAkCmB,cAAA,WACZ,iBACP"}
|
package/dist/openapi.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
import "./workspace-CASoZOjs.mjs";
|
|
3
3
|
import "./config-BQ4a36Rq.mjs";
|
|
4
|
-
import { OPENAPI_OUTPUT_PATH, generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi-
|
|
4
|
+
import { OPENAPI_OUTPUT_PATH, generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi-NthphEWK.mjs";
|
|
5
5
|
|
|
6
6
|
export { OPENAPI_OUTPUT_PATH, generateOpenApi, openapiCommand, resolveOpenApiConfig };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geekmidas/cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
"prompts": "~2.4.2",
|
|
55
55
|
"tsx": "~4.20.3",
|
|
56
56
|
"@geekmidas/constructs": "~1.0.0",
|
|
57
|
-
"@geekmidas/errors": "~1.0.0",
|
|
58
57
|
"@geekmidas/envkit": "~1.0.0",
|
|
59
|
-
"@geekmidas/
|
|
60
|
-
"@geekmidas/logger": "~1.0.0"
|
|
58
|
+
"@geekmidas/errors": "~1.0.0",
|
|
59
|
+
"@geekmidas/logger": "~1.0.0",
|
|
60
|
+
"@geekmidas/schema": "~1.0.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@types/lodash.kebabcase": "^4.1.9",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, realpathSync } from 'node:fs';
|
|
2
|
-
import { readFile, rm } from 'node:fs/promises';
|
|
2
|
+
import { mkdir, readFile, rm } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
5
|
import {
|
|
@@ -38,9 +38,9 @@ describe('resolveOpenApiConfig', () => {
|
|
|
38
38
|
});
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
it('should return
|
|
41
|
+
it('should return enabled by default when openapi is undefined', () => {
|
|
42
42
|
const result = resolveOpenApiConfig({ ...baseConfig });
|
|
43
|
-
expect(result.enabled).toBe(
|
|
43
|
+
expect(result.enabled).toBe(true);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
it('should use custom config values when provided', () => {
|
|
@@ -111,9 +111,9 @@ describe('generateOpenApi', () => {
|
|
|
111
111
|
expect(result).toBeNull();
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
-
it('should return null when
|
|
114
|
+
it('should return null when no endpoints are found', async () => {
|
|
115
115
|
const config: GkmConfig = {
|
|
116
|
-
routes: './src/endpoints/**/*.ts',
|
|
116
|
+
routes: './src/endpoints/**/*.ts', // Path doesn't exist
|
|
117
117
|
envParser: './src/config/env#envParser',
|
|
118
118
|
logger: './src/config/logger#logger',
|
|
119
119
|
};
|
|
@@ -468,3 +468,383 @@ export const complexEndpoint = e
|
|
|
468
468
|
expect(content).toContain('export interface paths');
|
|
469
469
|
});
|
|
470
470
|
});
|
|
471
|
+
|
|
472
|
+
describe('openapiCommand - workspace mode', () => {
|
|
473
|
+
let tempDir: string;
|
|
474
|
+
const originalCwd = process.cwd();
|
|
475
|
+
|
|
476
|
+
beforeEach(async () => {
|
|
477
|
+
tempDir = realpathSync(await createTempDir('openapi-workspace-'));
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
afterEach(async () => {
|
|
481
|
+
process.chdir(originalCwd);
|
|
482
|
+
await cleanupDir(tempDir);
|
|
483
|
+
vi.restoreAllMocks();
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('should generate OpenAPI for backend app in workspace', async () => {
|
|
487
|
+
// Create workspace structure
|
|
488
|
+
const apiDir = join(tempDir, 'apps/api');
|
|
489
|
+
await mkdir(apiDir, { recursive: true });
|
|
490
|
+
|
|
491
|
+
// Create endpoint in backend app
|
|
492
|
+
await createMockEndpointFile(
|
|
493
|
+
apiDir,
|
|
494
|
+
'src/endpoints/users.ts',
|
|
495
|
+
'getUsers',
|
|
496
|
+
'/users',
|
|
497
|
+
'GET',
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
// Create workspace config (gkm.config.json)
|
|
501
|
+
await createTestFile(
|
|
502
|
+
tempDir,
|
|
503
|
+
'gkm.config.json',
|
|
504
|
+
JSON.stringify({
|
|
505
|
+
name: 'test-workspace',
|
|
506
|
+
apps: {
|
|
507
|
+
api: {
|
|
508
|
+
type: 'backend',
|
|
509
|
+
path: 'apps/api',
|
|
510
|
+
port: 3000,
|
|
511
|
+
routes: './src/endpoints/**/*.ts',
|
|
512
|
+
openapi: { enabled: true },
|
|
513
|
+
},
|
|
514
|
+
},
|
|
515
|
+
}),
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
process.chdir(tempDir);
|
|
519
|
+
const consoleSpy = vi.spyOn(console, 'log');
|
|
520
|
+
|
|
521
|
+
await openapiCommand({ cwd: tempDir });
|
|
522
|
+
|
|
523
|
+
// Should generate OpenAPI in the backend app's .gkm folder
|
|
524
|
+
const outputPath = join(apiDir, OPENAPI_OUTPUT_PATH);
|
|
525
|
+
expect(existsSync(outputPath)).toBe(true);
|
|
526
|
+
|
|
527
|
+
const content = await readFile(outputPath, 'utf-8');
|
|
528
|
+
expect(content).toContain('export interface paths');
|
|
529
|
+
expect(content).toContain("'/users'");
|
|
530
|
+
|
|
531
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
532
|
+
expect.stringContaining('[api] Generated OpenAPI'),
|
|
533
|
+
);
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
it('should copy OpenAPI to frontend app with client.output', async () => {
|
|
537
|
+
// Create workspace structure
|
|
538
|
+
const apiDir = join(tempDir, 'apps/api');
|
|
539
|
+
const webDir = join(tempDir, 'apps/web');
|
|
540
|
+
await mkdir(apiDir, { recursive: true });
|
|
541
|
+
await mkdir(webDir, { recursive: true });
|
|
542
|
+
|
|
543
|
+
// Create endpoint in backend app
|
|
544
|
+
await createMockEndpointFile(
|
|
545
|
+
apiDir,
|
|
546
|
+
'src/endpoints/users.ts',
|
|
547
|
+
'getUsers',
|
|
548
|
+
'/users',
|
|
549
|
+
'GET',
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
// Create workspace config with frontend that depends on backend
|
|
553
|
+
await createTestFile(
|
|
554
|
+
tempDir,
|
|
555
|
+
'gkm.config.json',
|
|
556
|
+
JSON.stringify({
|
|
557
|
+
name: 'test-workspace',
|
|
558
|
+
apps: {
|
|
559
|
+
api: {
|
|
560
|
+
type: 'backend',
|
|
561
|
+
path: 'apps/api',
|
|
562
|
+
port: 3000,
|
|
563
|
+
routes: './src/endpoints/**/*.ts',
|
|
564
|
+
openapi: { enabled: true },
|
|
565
|
+
},
|
|
566
|
+
web: {
|
|
567
|
+
type: 'frontend',
|
|
568
|
+
framework: 'nextjs',
|
|
569
|
+
path: 'apps/web',
|
|
570
|
+
port: 3001,
|
|
571
|
+
dependencies: ['api'],
|
|
572
|
+
client: {
|
|
573
|
+
output: './src/api',
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
}),
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
process.chdir(tempDir);
|
|
581
|
+
const consoleSpy = vi.spyOn(console, 'log');
|
|
582
|
+
|
|
583
|
+
await openapiCommand({ cwd: tempDir });
|
|
584
|
+
|
|
585
|
+
// Should generate OpenAPI in backend app
|
|
586
|
+
const backendOutput = join(apiDir, OPENAPI_OUTPUT_PATH);
|
|
587
|
+
expect(existsSync(backendOutput)).toBe(true);
|
|
588
|
+
|
|
589
|
+
// Should copy to frontend app's client output path
|
|
590
|
+
const frontendOutput = join(webDir, 'src/api/openapi.ts');
|
|
591
|
+
expect(existsSync(frontendOutput)).toBe(true);
|
|
592
|
+
|
|
593
|
+
// Content should be the same
|
|
594
|
+
const backendContent = await readFile(backendOutput, 'utf-8');
|
|
595
|
+
const frontendContent = await readFile(frontendOutput, 'utf-8');
|
|
596
|
+
expect(frontendContent).toBe(backendContent);
|
|
597
|
+
|
|
598
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
599
|
+
expect.stringContaining('[web] ./src/api/openapi.ts'),
|
|
600
|
+
);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
it('should only copy to frontend apps that depend on the backend', async () => {
|
|
604
|
+
// Create workspace structure with multiple apps
|
|
605
|
+
const apiDir = join(tempDir, 'apps/api');
|
|
606
|
+
const authDir = join(tempDir, 'apps/auth');
|
|
607
|
+
const webDir = join(tempDir, 'apps/web');
|
|
608
|
+
const adminDir = join(tempDir, 'apps/admin');
|
|
609
|
+
await mkdir(apiDir, { recursive: true });
|
|
610
|
+
await mkdir(authDir, { recursive: true });
|
|
611
|
+
await mkdir(webDir, { recursive: true });
|
|
612
|
+
await mkdir(adminDir, { recursive: true });
|
|
613
|
+
|
|
614
|
+
// Create endpoints
|
|
615
|
+
await createMockEndpointFile(
|
|
616
|
+
apiDir,
|
|
617
|
+
'src/endpoints/users.ts',
|
|
618
|
+
'getUsers',
|
|
619
|
+
'/users',
|
|
620
|
+
'GET',
|
|
621
|
+
);
|
|
622
|
+
await createMockEndpointFile(
|
|
623
|
+
authDir,
|
|
624
|
+
'src/endpoints/login.ts',
|
|
625
|
+
'login',
|
|
626
|
+
'/login',
|
|
627
|
+
'POST',
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
// Create workspace config
|
|
631
|
+
await createTestFile(
|
|
632
|
+
tempDir,
|
|
633
|
+
'gkm.config.json',
|
|
634
|
+
JSON.stringify({
|
|
635
|
+
name: 'test-workspace',
|
|
636
|
+
apps: {
|
|
637
|
+
api: {
|
|
638
|
+
type: 'backend',
|
|
639
|
+
path: 'apps/api',
|
|
640
|
+
port: 3000,
|
|
641
|
+
routes: './src/endpoints/**/*.ts',
|
|
642
|
+
openapi: { enabled: true },
|
|
643
|
+
},
|
|
644
|
+
auth: {
|
|
645
|
+
type: 'backend',
|
|
646
|
+
path: 'apps/auth',
|
|
647
|
+
port: 3001,
|
|
648
|
+
routes: './src/endpoints/**/*.ts',
|
|
649
|
+
openapi: { enabled: true },
|
|
650
|
+
},
|
|
651
|
+
web: {
|
|
652
|
+
type: 'frontend',
|
|
653
|
+
framework: 'nextjs',
|
|
654
|
+
path: 'apps/web',
|
|
655
|
+
port: 3002,
|
|
656
|
+
dependencies: ['api'], // Only depends on api
|
|
657
|
+
client: {
|
|
658
|
+
output: './src/api',
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
admin: {
|
|
662
|
+
type: 'frontend',
|
|
663
|
+
framework: 'nextjs',
|
|
664
|
+
path: 'apps/admin',
|
|
665
|
+
port: 3003,
|
|
666
|
+
dependencies: ['auth'], // Only depends on auth
|
|
667
|
+
client: {
|
|
668
|
+
output: './src/client',
|
|
669
|
+
},
|
|
670
|
+
},
|
|
671
|
+
},
|
|
672
|
+
}),
|
|
673
|
+
);
|
|
674
|
+
|
|
675
|
+
process.chdir(tempDir);
|
|
676
|
+
|
|
677
|
+
await openapiCommand({ cwd: tempDir });
|
|
678
|
+
|
|
679
|
+
// Web should have api's OpenAPI (not auth's)
|
|
680
|
+
const webApiOutput = join(webDir, 'src/api/openapi.ts');
|
|
681
|
+
expect(existsSync(webApiOutput)).toBe(true);
|
|
682
|
+
const webContent = await readFile(webApiOutput, 'utf-8');
|
|
683
|
+
expect(webContent).toContain("'/users'");
|
|
684
|
+
expect(webContent).not.toContain("'/login'");
|
|
685
|
+
|
|
686
|
+
// Admin should have auth's OpenAPI (not api's)
|
|
687
|
+
const adminClientOutput = join(adminDir, 'src/client/openapi.ts');
|
|
688
|
+
expect(existsSync(adminClientOutput)).toBe(true);
|
|
689
|
+
const adminContent = await readFile(adminClientOutput, 'utf-8');
|
|
690
|
+
expect(adminContent).toContain("'/login'");
|
|
691
|
+
expect(adminContent).not.toContain("'/users'");
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
it('should not copy to frontend with empty dependencies array', async () => {
|
|
695
|
+
// Create workspace structure
|
|
696
|
+
const apiDir = join(tempDir, 'apps/api');
|
|
697
|
+
const webDir = join(tempDir, 'apps/web');
|
|
698
|
+
await mkdir(apiDir, { recursive: true });
|
|
699
|
+
await mkdir(webDir, { recursive: true });
|
|
700
|
+
|
|
701
|
+
await createMockEndpointFile(
|
|
702
|
+
apiDir,
|
|
703
|
+
'src/endpoints/users.ts',
|
|
704
|
+
'getUsers',
|
|
705
|
+
'/users',
|
|
706
|
+
'GET',
|
|
707
|
+
);
|
|
708
|
+
|
|
709
|
+
// Frontend with empty dependencies array (depends on nothing)
|
|
710
|
+
await createTestFile(
|
|
711
|
+
tempDir,
|
|
712
|
+
'gkm.config.json',
|
|
713
|
+
JSON.stringify({
|
|
714
|
+
name: 'test-workspace',
|
|
715
|
+
apps: {
|
|
716
|
+
api: {
|
|
717
|
+
type: 'backend',
|
|
718
|
+
path: 'apps/api',
|
|
719
|
+
port: 3000,
|
|
720
|
+
routes: './src/endpoints/**/*.ts',
|
|
721
|
+
openapi: { enabled: true },
|
|
722
|
+
},
|
|
723
|
+
web: {
|
|
724
|
+
type: 'frontend',
|
|
725
|
+
framework: 'nextjs',
|
|
726
|
+
path: 'apps/web',
|
|
727
|
+
port: 3001,
|
|
728
|
+
dependencies: [], // Empty array means depends on nothing
|
|
729
|
+
client: {
|
|
730
|
+
output: './src/api',
|
|
731
|
+
},
|
|
732
|
+
},
|
|
733
|
+
},
|
|
734
|
+
}),
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
process.chdir(tempDir);
|
|
738
|
+
|
|
739
|
+
await openapiCommand({ cwd: tempDir });
|
|
740
|
+
|
|
741
|
+
// Should NOT copy to frontend since it has no dependencies
|
|
742
|
+
const frontendOutput = join(webDir, 'src/api/openapi.ts');
|
|
743
|
+
expect(existsSync(frontendOutput)).toBe(false);
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
it('should skip frontend apps without client.output', async () => {
|
|
747
|
+
const apiDir = join(tempDir, 'apps/api');
|
|
748
|
+
const webDir = join(tempDir, 'apps/web');
|
|
749
|
+
await mkdir(apiDir, { recursive: true });
|
|
750
|
+
await mkdir(webDir, { recursive: true });
|
|
751
|
+
|
|
752
|
+
await createMockEndpointFile(
|
|
753
|
+
apiDir,
|
|
754
|
+
'src/endpoints/users.ts',
|
|
755
|
+
'getUsers',
|
|
756
|
+
'/users',
|
|
757
|
+
'GET',
|
|
758
|
+
);
|
|
759
|
+
|
|
760
|
+
// Frontend without client.output
|
|
761
|
+
await createTestFile(
|
|
762
|
+
tempDir,
|
|
763
|
+
'gkm.config.json',
|
|
764
|
+
JSON.stringify({
|
|
765
|
+
name: 'test-workspace',
|
|
766
|
+
apps: {
|
|
767
|
+
api: {
|
|
768
|
+
type: 'backend',
|
|
769
|
+
path: 'apps/api',
|
|
770
|
+
port: 3000,
|
|
771
|
+
routes: './src/endpoints/**/*.ts',
|
|
772
|
+
openapi: { enabled: true },
|
|
773
|
+
},
|
|
774
|
+
web: {
|
|
775
|
+
type: 'frontend',
|
|
776
|
+
framework: 'nextjs',
|
|
777
|
+
path: 'apps/web',
|
|
778
|
+
port: 3001,
|
|
779
|
+
dependencies: ['api'],
|
|
780
|
+
// No client.output configured
|
|
781
|
+
},
|
|
782
|
+
},
|
|
783
|
+
}),
|
|
784
|
+
);
|
|
785
|
+
|
|
786
|
+
process.chdir(tempDir);
|
|
787
|
+
|
|
788
|
+
await openapiCommand({ cwd: tempDir });
|
|
789
|
+
|
|
790
|
+
// Backend should still have OpenAPI generated
|
|
791
|
+
expect(existsSync(join(apiDir, OPENAPI_OUTPUT_PATH))).toBe(true);
|
|
792
|
+
|
|
793
|
+
// But no files should be created in frontend
|
|
794
|
+
expect(existsSync(join(webDir, 'src/api/openapi.ts'))).toBe(false);
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
it('should handle nested client.output paths', async () => {
|
|
798
|
+
const apiDir = join(tempDir, 'apps/api');
|
|
799
|
+
const webDir = join(tempDir, 'apps/web');
|
|
800
|
+
await mkdir(apiDir, { recursive: true });
|
|
801
|
+
await mkdir(webDir, { recursive: true });
|
|
802
|
+
|
|
803
|
+
await createMockEndpointFile(
|
|
804
|
+
apiDir,
|
|
805
|
+
'src/endpoints/users.ts',
|
|
806
|
+
'getUsers',
|
|
807
|
+
'/users',
|
|
808
|
+
'GET',
|
|
809
|
+
);
|
|
810
|
+
|
|
811
|
+
// Deeply nested client output path
|
|
812
|
+
await createTestFile(
|
|
813
|
+
tempDir,
|
|
814
|
+
'gkm.config.json',
|
|
815
|
+
JSON.stringify({
|
|
816
|
+
name: 'test-workspace',
|
|
817
|
+
apps: {
|
|
818
|
+
api: {
|
|
819
|
+
type: 'backend',
|
|
820
|
+
path: 'apps/api',
|
|
821
|
+
port: 3000,
|
|
822
|
+
routes: './src/endpoints/**/*.ts',
|
|
823
|
+
openapi: { enabled: true },
|
|
824
|
+
},
|
|
825
|
+
web: {
|
|
826
|
+
type: 'frontend',
|
|
827
|
+
framework: 'nextjs',
|
|
828
|
+
path: 'apps/web',
|
|
829
|
+
port: 3001,
|
|
830
|
+
dependencies: ['api'],
|
|
831
|
+
client: {
|
|
832
|
+
output: './src/lib/api/generated',
|
|
833
|
+
},
|
|
834
|
+
},
|
|
835
|
+
},
|
|
836
|
+
}),
|
|
837
|
+
);
|
|
838
|
+
|
|
839
|
+
process.chdir(tempDir);
|
|
840
|
+
|
|
841
|
+
await openapiCommand({ cwd: tempDir });
|
|
842
|
+
|
|
843
|
+
// Should create nested directories and file
|
|
844
|
+
const frontendOutput = join(webDir, 'src/lib/api/generated/openapi.ts');
|
|
845
|
+
expect(existsSync(frontendOutput)).toBe(true);
|
|
846
|
+
|
|
847
|
+
const content = await readFile(frontendOutput, 'utf-8');
|
|
848
|
+
expect(content).toContain('export interface paths');
|
|
849
|
+
});
|
|
850
|
+
});
|