ng-openapi 0.0.2 → 0.0.4

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 (4) hide show
  1. package/README.md +52 -32
  2. package/cli.cjs +265 -16
  3. package/index.js +260 -13
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
- # ng-openapi Usage Guide
1
+ # Angular OpenAPI client generator
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/ng-openapi.svg)](https://www.npmjs.com/package/ng-openapi)
4
+ ## 💪 Made with ❤️ by Angular Devs for Angular Devs
5
+
6
+
7
+ ## Quick Start Guide
3
8
  ## Installation
4
9
 
5
10
  ```bash
@@ -84,13 +89,13 @@ ng-openapi -i ./swagger.json -o ./src/api --date-type string
84
89
 
85
90
  ### Optional Fields
86
91
 
87
- - `options.dateType` - How to handle date types: `'string'` or `'Date'` (default: `'Date'`)
88
- - `options.enumStyle` - Enum generation style: `'enum'` or `'union'` (default: `'enum'`)
89
- - `options.generateEnumBasedOnDescription` - Parse enum values from description field (default: `true`)
90
- - `options.generateServices` - Generate Angular services (default: `true`)
91
- - `options.customHeaders` - Headers to add to all HTTP requests
92
- - `options.responseTypeMapping` - Map content types to Angular HttpClient response types
93
- - `options.customizeMethodName` - Function to customize generated method names
92
+ - `dateType` - How to handle date types: `'string'` or `'Date'` (default: `'Date'`)
93
+ - `enumStyle` - Enum generation style: `'enum'` or `'union'` (default: `'enum'`)
94
+ - `generateEnumBasedOnDescription` - Parse enum values from description field (default: `true`)
95
+ - `generateServices` - Generate Angular services (default: `true`)
96
+ - `customHeaders` - Headers to add to all HTTP requests
97
+ - `responseTypeMapping` - Map content types to Angular HttpClient response types
98
+ - `customizeMethodName` - Function to customize generated method names
94
99
  - `compilerOptions` - TypeScript compiler options for code generation
95
100
 
96
101
  ## Generated Files Structure
@@ -104,44 +109,60 @@ output/
104
109
  │ └── *.service.ts # Angular services
105
110
  ├── tokens/
106
111
  │ └── index.ts # Injection tokens
107
- └── utils/
108
- ├── date-transformer.ts # Date transformation interceptor
109
- └── file-download.ts # File download helpers
112
+ ├── utils/
113
+ ├── date-transformer.ts # Date transformation interceptor
114
+ └── file-download.ts # File download helpers
115
+ ├── providers.ts # Provider functions for easy setup
116
+ └── index.ts # Main exports
110
117
  ```
111
118
 
112
119
  ## Angular Integration
113
120
 
114
- ### 1. Configure Base Path
121
+ ### 🚀 Easy Setup (Recommended)
122
+
123
+ The simplest way to integrate ng-openapi is using the provider function:
115
124
 
116
125
  ```typescript
117
- import { BASE_PATH } from './api/tokens';
126
+ // In your app.config.ts
127
+ import { ApplicationConfig } from '@angular/core';
128
+ import { provideNgOpenapi } from './api/providers';
118
129
 
119
- // In your app.config.ts or module
120
130
  export const appConfig: ApplicationConfig = {
121
131
  providers: [
122
- { provide: BASE_PATH, useValue: 'https://api.example.com' },
132
+ // One-line setup with automatic interceptor configuration
133
+ provideNgOpenapi({
134
+ basePath: 'https://api.example.com'
135
+ }),
123
136
  // other providers...
124
137
  ]
125
138
  };
126
139
  ```
127
140
 
128
- ### 2. Add Date Interceptor (if using Date type)
141
+ That's it! This automatically configures:
142
+ - ✅ BASE_PATH token
143
+ - ✅ Date transformation interceptor (if using Date type)
129
144
 
130
- ```typescript
131
- import { DateInterceptor } from './api/utils/date-transformer';
132
- import { HTTP_INTERCEPTORS } from '@angular/common/http';
133
145
 
134
- export const appConfig: ApplicationConfig = {
135
- providers: [
136
- { provide: HTTP_INTERCEPTORS, useClass: DateInterceptor, multi: true },
137
- // other providers...
138
- ]
139
- };
146
+ ### Advanced Provider Options
147
+
148
+ ```typescript
149
+ // Disable date transformation
150
+ provideNgOpenapi({
151
+ basePath: 'https://api.example.com',
152
+ enableDateTransform: false
153
+ });
154
+
155
+ // Async configuration
156
+ provideNgOpenapiAsync({
157
+ basePath: () => import('./config').then(c => c.apiConfig.baseUrl)
158
+ });
140
159
  ```
141
160
 
142
- ### 3. Use Generated Services
161
+ ## Using Generated Services
143
162
 
144
163
  ```typescript
164
+ import { Component, inject } from '@angular/core';
165
+ import { toSignal } from '@angular/core/rxjs-interop';
145
166
  import { UserService } from './api/services';
146
167
  import { User } from './api/models';
147
168
 
@@ -150,19 +171,19 @@ import { User } from './api/models';
150
171
  template: `...`
151
172
  })
152
173
  export class UsersComponent {
153
- users$ = this.userService.getUsers();
154
-
155
- constructor(private userService: UserService) {}
174
+ private readonly userService = inject(UserService);
175
+ readonly users = toSignal(this.userService.getUsers());
156
176
  }
157
177
  ```
158
178
 
159
179
  ## File Download Example
160
180
 
161
181
  ```typescript
182
+ import { Component, inject } from '@angular/core';
162
183
  import { downloadFileOperator } from './api/utils/file-download';
163
184
 
164
185
  export class ReportComponent {
165
- constructor(private reportService: ReportService) {}
186
+ private readonly reportService = inject(ReportService);
166
187
 
167
188
  downloadReport() {
168
189
  this.reportService.getReport('pdf', { reportId: 123 })
@@ -181,8 +202,7 @@ Add these scripts to your `package.json`:
181
202
  ```json
182
203
  {
183
204
  "scripts": {
184
- "generate:api": "ng-openapi -c openapi.config.ts",
185
- "generate:api:watch": "nodemon --watch swagger.json --exec npm run generate:api"
205
+ "generate:api": "ng-openapi -c openapi.config.ts"
186
206
  }
187
207
  }
188
208
  ```
package/cli.cjs CHANGED
@@ -26,7 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
 
27
27
  // src/lib/cli.ts
28
28
  var import_commander = require("commander");
29
- var path6 = __toESM(require("path"));
29
+ var path8 = __toESM(require("path"));
30
30
  var fs4 = __toESM(require("fs"));
31
31
 
32
32
  // src/lib/core/swagger-parser.ts
@@ -85,6 +85,14 @@ var SERVICE_GENERATOR_HEADER_COMMENT = /* @__PURE__ */ __name((controllerName) =
85
85
  * Do not edit this file manually
86
86
  */
87
87
  `, "SERVICE_GENERATOR_HEADER_COMMENT");
88
+ var MAIN_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Entrypoint for the client
89
+ * Do not edit this file manually
90
+ */
91
+ `;
92
+ var PROVIDER_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated provider functions for easy setup
93
+ * Do not edit this file manually
94
+ */
95
+ `;
88
96
 
89
97
  // src/lib/generators/type/type.generator.ts
90
98
  var TypeGenerator = class {
@@ -705,9 +713,53 @@ var DateTransformerGenerator = class {
705
713
  }
706
714
  };
707
715
 
716
+ // src/lib/generators/utility/main-index.generator.ts
717
+ var path4 = __toESM(require("path"));
718
+ var MainIndexGenerator = class {
719
+ static {
720
+ __name(this, "MainIndexGenerator");
721
+ }
722
+ project;
723
+ config;
724
+ constructor(project, config) {
725
+ this.project = project;
726
+ this.config = config;
727
+ }
728
+ generateMainIndex(outputRoot) {
729
+ const indexPath = path4.join(outputRoot, "index.ts");
730
+ const sourceFile = this.project.createSourceFile(indexPath, "", {
731
+ overwrite: true
732
+ });
733
+ sourceFile.insertText(0, MAIN_INDEX_GENERATOR_HEADER_COMMENT);
734
+ sourceFile.addExportDeclaration({
735
+ moduleSpecifier: "./models"
736
+ });
737
+ sourceFile.addExportDeclaration({
738
+ moduleSpecifier: "./tokens"
739
+ });
740
+ sourceFile.addExportDeclaration({
741
+ moduleSpecifier: "./providers"
742
+ });
743
+ if (this.config.options.generateServices !== false) {
744
+ sourceFile.addExportDeclaration({
745
+ moduleSpecifier: "./services"
746
+ });
747
+ }
748
+ if (this.config.options.dateType === "Date") {
749
+ sourceFile.addExportDeclaration({
750
+ moduleSpecifier: "./utils/date-transformer"
751
+ });
752
+ }
753
+ sourceFile.addExportDeclaration({
754
+ moduleSpecifier: "./utils/file-download"
755
+ });
756
+ sourceFile.saveSync();
757
+ }
758
+ };
759
+
708
760
  // src/lib/generators/service/service.generator.ts
709
761
  var import_ts_morph3 = require("ts-morph");
710
- var path4 = __toESM(require("path"));
762
+ var path5 = __toESM(require("path"));
711
763
 
712
764
  // src/lib/utils/string.utils.ts
713
765
  function camelCase(str) {
@@ -1434,7 +1486,7 @@ var ServiceGenerator = class {
1434
1486
  this.methodGenerator = new ServiceMethodGenerator(config);
1435
1487
  }
1436
1488
  generate(outputRoot) {
1437
- const outputDir = path4.join(outputRoot, "services");
1489
+ const outputDir = path5.join(outputRoot, "services");
1438
1490
  const paths = this.extractPaths();
1439
1491
  const controllerGroups = this.groupPathsByController(paths);
1440
1492
  Object.entries(controllerGroups).forEach(([controllerName, operations]) => {
@@ -1444,7 +1496,7 @@ var ServiceGenerator = class {
1444
1496
  extractPaths() {
1445
1497
  const paths = [];
1446
1498
  const swaggerPaths = this.spec.paths || {};
1447
- Object.entries(swaggerPaths).forEach(([path7, pathItem]) => {
1499
+ Object.entries(swaggerPaths).forEach(([path9, pathItem]) => {
1448
1500
  const methods = [
1449
1501
  "get",
1450
1502
  "post",
@@ -1458,7 +1510,7 @@ var ServiceGenerator = class {
1458
1510
  if (pathItem[method]) {
1459
1511
  const operation = pathItem[method];
1460
1512
  paths.push({
1461
- path: path7,
1513
+ path: path9,
1462
1514
  method: method.toUpperCase(),
1463
1515
  operationId: operation.operationId,
1464
1516
  summary: operation.summary,
@@ -1490,12 +1542,12 @@ var ServiceGenerator = class {
1490
1542
  }
1491
1543
  groupPathsByController(paths) {
1492
1544
  const groups = {};
1493
- paths.forEach((path7) => {
1545
+ paths.forEach((path9) => {
1494
1546
  let controllerName = "Default";
1495
- if (path7.tags && path7.tags.length > 0) {
1496
- controllerName = path7.tags[0];
1547
+ if (path9.tags && path9.tags.length > 0) {
1548
+ controllerName = path9.tags[0];
1497
1549
  } else {
1498
- const pathParts = path7.path.split("/").filter((p) => p && !p.startsWith("{"));
1550
+ const pathParts = path9.path.split("/").filter((p) => p && !p.startsWith("{"));
1499
1551
  if (pathParts.length > 1) {
1500
1552
  controllerName = pascalCase(pathParts[1]);
1501
1553
  }
@@ -1504,13 +1556,13 @@ var ServiceGenerator = class {
1504
1556
  if (!groups[controllerName]) {
1505
1557
  groups[controllerName] = [];
1506
1558
  }
1507
- groups[controllerName].push(path7);
1559
+ groups[controllerName].push(path9);
1508
1560
  });
1509
1561
  return groups;
1510
1562
  }
1511
1563
  generateServiceFile(controllerName, operations, outputDir) {
1512
1564
  const fileName = `${kebabCase(controllerName)}.service.ts`;
1513
- const filePath = path4.join(outputDir, fileName);
1565
+ const filePath = path5.join(outputDir, fileName);
1514
1566
  const sourceFile = this.project.createSourceFile(filePath, "", {
1515
1567
  overwrite: true
1516
1568
  });
@@ -1667,7 +1719,7 @@ var ServiceGenerator = class {
1667
1719
 
1668
1720
  // src/lib/generators/service/service-index.generator.ts
1669
1721
  var fs2 = __toESM(require("fs"));
1670
- var path5 = __toESM(require("path"));
1722
+ var path6 = __toESM(require("path"));
1671
1723
  var ServiceIndexGenerator = class {
1672
1724
  static {
1673
1725
  __name(this, "ServiceIndexGenerator");
@@ -1677,8 +1729,8 @@ var ServiceIndexGenerator = class {
1677
1729
  this.project = project;
1678
1730
  }
1679
1731
  generateIndex(outputRoot) {
1680
- const servicesDir = path5.join(outputRoot, "services");
1681
- const indexPath = path5.join(servicesDir, "index.ts");
1732
+ const servicesDir = path6.join(outputRoot, "services");
1733
+ const indexPath = path6.join(servicesDir, "index.ts");
1682
1734
  const sourceFile = this.project.createSourceFile(indexPath, "", {
1683
1735
  overwrite: true
1684
1736
  });
@@ -1697,6 +1749,198 @@ var ServiceIndexGenerator = class {
1697
1749
  }
1698
1750
  };
1699
1751
 
1752
+ // src/lib/generators/utility/provider.generator.ts
1753
+ var path7 = __toESM(require("path"));
1754
+ var ProviderGenerator = class {
1755
+ static {
1756
+ __name(this, "ProviderGenerator");
1757
+ }
1758
+ project;
1759
+ config;
1760
+ constructor(project, config) {
1761
+ this.project = project;
1762
+ this.config = config;
1763
+ }
1764
+ generate(outputDir) {
1765
+ const filePath = path7.join(outputDir, "providers.ts");
1766
+ const sourceFile = this.project.createSourceFile(filePath, "", {
1767
+ overwrite: true
1768
+ });
1769
+ sourceFile.insertText(0, PROVIDER_GENERATOR_HEADER_COMMENT);
1770
+ sourceFile.addImportDeclarations([
1771
+ {
1772
+ namedImports: [
1773
+ "EnvironmentProviders",
1774
+ "Provider",
1775
+ "makeEnvironmentProviders"
1776
+ ],
1777
+ moduleSpecifier: "@angular/core"
1778
+ },
1779
+ {
1780
+ namedImports: [
1781
+ "HTTP_INTERCEPTORS"
1782
+ ],
1783
+ moduleSpecifier: "@angular/common/http"
1784
+ },
1785
+ {
1786
+ namedImports: [
1787
+ "BASE_PATH"
1788
+ ],
1789
+ moduleSpecifier: "./tokens"
1790
+ }
1791
+ ]);
1792
+ if (this.config.options.dateType === "Date") {
1793
+ sourceFile.addImportDeclaration({
1794
+ namedImports: [
1795
+ "DateInterceptor"
1796
+ ],
1797
+ moduleSpecifier: "./utils/date-transformer"
1798
+ });
1799
+ }
1800
+ sourceFile.addInterface({
1801
+ name: "NgOpenapiConfig",
1802
+ isExported: true,
1803
+ docs: [
1804
+ "Configuration options for ng-openapi providers"
1805
+ ],
1806
+ properties: [
1807
+ {
1808
+ name: "basePath",
1809
+ type: "string",
1810
+ docs: [
1811
+ "Base API URL"
1812
+ ]
1813
+ },
1814
+ {
1815
+ name: "enableDateTransform",
1816
+ type: "boolean",
1817
+ hasQuestionToken: true,
1818
+ docs: [
1819
+ "Enable automatic date transformation (default: true)"
1820
+ ]
1821
+ }
1822
+ ]
1823
+ });
1824
+ this.addMainProviderFunction(sourceFile);
1825
+ this.addAsyncProviderFunction(sourceFile);
1826
+ sourceFile.saveSync();
1827
+ }
1828
+ addMainProviderFunction(sourceFile) {
1829
+ const hasDateInterceptor = this.config.options.dateType === "Date";
1830
+ const functionBody = `
1831
+ const providers: Provider[] = [
1832
+ // Base path token
1833
+ {
1834
+ provide: BASE_PATH,
1835
+ useValue: config.basePath
1836
+ }
1837
+ ];
1838
+
1839
+ ${hasDateInterceptor ? `// Add date interceptor if enabled (default: true)
1840
+ if (config.enableDateTransform !== false) {
1841
+ providers.push({
1842
+ provide: HTTP_INTERCEPTORS,
1843
+ useClass: DateInterceptor,
1844
+ multi: true
1845
+ });
1846
+ }` : `// Date transformation not available (dateType: 'string' was used in generation)`}
1847
+
1848
+ return makeEnvironmentProviders(providers);`;
1849
+ sourceFile.addFunction({
1850
+ name: "provideNgOpenapi",
1851
+ isExported: true,
1852
+ docs: [
1853
+ "Provides all necessary configuration for ng-openapi generated services",
1854
+ "",
1855
+ "@example",
1856
+ "```typescript",
1857
+ "// In your app.config.ts",
1858
+ "import { provideNgOpenapi } from './api/providers';",
1859
+ "",
1860
+ "export const appConfig: ApplicationConfig = {",
1861
+ " providers: [",
1862
+ " provideNgOpenapi({",
1863
+ " basePath: 'https://api.example.com'",
1864
+ " }),",
1865
+ " // other providers...",
1866
+ " ]",
1867
+ "};",
1868
+ "```"
1869
+ ],
1870
+ parameters: [
1871
+ {
1872
+ name: "config",
1873
+ type: "NgOpenapiConfig"
1874
+ }
1875
+ ],
1876
+ returnType: "EnvironmentProviders",
1877
+ statements: functionBody
1878
+ });
1879
+ }
1880
+ addAsyncProviderFunction(sourceFile) {
1881
+ const hasDateInterceptor = this.config.options.dateType === "Date";
1882
+ const functionBody = `
1883
+ const providers: Provider[] = [];
1884
+
1885
+ // Handle async base path
1886
+ if (typeof config.basePath === 'string') {
1887
+ providers.push({
1888
+ provide: BASE_PATH,
1889
+ useValue: config.basePath
1890
+ });
1891
+ } else {
1892
+ providers.push({
1893
+ provide: BASE_PATH,
1894
+ useFactory: config.basePath
1895
+ });
1896
+ }
1897
+
1898
+ ${hasDateInterceptor ? `// Add date interceptor if enabled (default: true)
1899
+ if (config.enableDateTransform !== false) {
1900
+ providers.push({
1901
+ provide: HTTP_INTERCEPTORS,
1902
+ useClass: DateInterceptor,
1903
+ multi: true
1904
+ });
1905
+ }` : `// Date transformation not available (dateType: 'string' was used in generation)`}
1906
+
1907
+ return makeEnvironmentProviders(providers);`;
1908
+ sourceFile.addFunction({
1909
+ name: "provideNgOpenapiAsync",
1910
+ isExported: true,
1911
+ docs: [
1912
+ "Alternative function for cases where you need to handle async configuration",
1913
+ "",
1914
+ "@example",
1915
+ "```typescript",
1916
+ "// In your app.config.ts",
1917
+ "import { provideNgOpenapiAsync } from './api/providers';",
1918
+ "",
1919
+ "export const appConfig: ApplicationConfig = {",
1920
+ " providers: [",
1921
+ " provideNgOpenapiAsync({",
1922
+ " basePath: () => import('./config').then(c => c.apiConfig.baseUrl)",
1923
+ " }),",
1924
+ " // other providers...",
1925
+ " ]",
1926
+ "};",
1927
+ "```"
1928
+ ],
1929
+ parameters: [
1930
+ {
1931
+ name: "config",
1932
+ type: `{
1933
+ basePath: string | (() => Promise<string>);
1934
+ enableDateTransform?: boolean;
1935
+ }`
1936
+ }
1937
+ ],
1938
+ returnType: "EnvironmentProviders",
1939
+ statements: functionBody
1940
+ });
1941
+ }
1942
+ };
1943
+
1700
1944
  // src/lib/core/generator.ts
1701
1945
  var fs3 = __toESM(require("fs"));
1702
1946
  async function generateFromConfig(config) {
@@ -1740,6 +1984,11 @@ async function generateFromConfig(config) {
1740
1984
  indexGenerator.generateIndex(outputPath);
1741
1985
  console.log(`\u2705 Angular services generated`);
1742
1986
  }
1987
+ const providerGenerator = new ProviderGenerator(project, config);
1988
+ providerGenerator.generate(outputPath);
1989
+ console.log(`\u2705 Provider functions generated`);
1990
+ const mainIndexGenerator = new MainIndexGenerator(project, config);
1991
+ mainIndexGenerator.generateMainIndex(outputPath);
1743
1992
  console.log("\u{1F389} Generation completed successfully at:", outputPath);
1744
1993
  } catch (error) {
1745
1994
  if (error instanceof Error) {
@@ -1755,7 +2004,7 @@ __name(generateFromConfig, "generateFromConfig");
1755
2004
  // src/lib/cli.ts
1756
2005
  var program = new import_commander.Command();
1757
2006
  async function loadConfigFile(configPath) {
1758
- const resolvedPath = path6.resolve(configPath);
2007
+ const resolvedPath = path8.resolve(configPath);
1759
2008
  if (!fs4.existsSync(resolvedPath)) {
1760
2009
  throw new Error(`Configuration file not found: ${resolvedPath}`);
1761
2010
  }
@@ -1781,7 +2030,7 @@ async function generateFromOptions(options) {
1781
2030
  const config = await loadConfigFile(options.config);
1782
2031
  await generateFromConfig(config);
1783
2032
  } else if (options.input) {
1784
- const inputPath = path6.resolve(options.input);
2033
+ const inputPath = path8.resolve(options.input);
1785
2034
  if (!fs4.existsSync(inputPath)) {
1786
2035
  console.error(`Error: Input file not found: ${inputPath}`);
1787
2036
  process.exit(1);
package/index.js CHANGED
@@ -127,6 +127,14 @@ var SERVICE_GENERATOR_HEADER_COMMENT = /* @__PURE__ */ __name((controllerName) =
127
127
  * Do not edit this file manually
128
128
  */
129
129
  `, "SERVICE_GENERATOR_HEADER_COMMENT");
130
+ var MAIN_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Entrypoint for the client
131
+ * Do not edit this file manually
132
+ */
133
+ `;
134
+ var PROVIDER_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated provider functions for easy setup
135
+ * Do not edit this file manually
136
+ */
137
+ `;
130
138
 
131
139
  // src/lib/generators/type/type.generator.ts
132
140
  var _TypeGenerator = class _TypeGenerator {
@@ -745,9 +753,52 @@ var _DateTransformerGenerator = class _DateTransformerGenerator {
745
753
  __name(_DateTransformerGenerator, "DateTransformerGenerator");
746
754
  var DateTransformerGenerator = _DateTransformerGenerator;
747
755
 
756
+ // src/lib/generators/utility/main-index.generator.ts
757
+ var path4 = __toESM(require("path"));
758
+ var _MainIndexGenerator = class _MainIndexGenerator {
759
+ constructor(project, config) {
760
+ __publicField(this, "project");
761
+ __publicField(this, "config");
762
+ this.project = project;
763
+ this.config = config;
764
+ }
765
+ generateMainIndex(outputRoot) {
766
+ const indexPath = path4.join(outputRoot, "index.ts");
767
+ const sourceFile = this.project.createSourceFile(indexPath, "", {
768
+ overwrite: true
769
+ });
770
+ sourceFile.insertText(0, MAIN_INDEX_GENERATOR_HEADER_COMMENT);
771
+ sourceFile.addExportDeclaration({
772
+ moduleSpecifier: "./models"
773
+ });
774
+ sourceFile.addExportDeclaration({
775
+ moduleSpecifier: "./tokens"
776
+ });
777
+ sourceFile.addExportDeclaration({
778
+ moduleSpecifier: "./providers"
779
+ });
780
+ if (this.config.options.generateServices !== false) {
781
+ sourceFile.addExportDeclaration({
782
+ moduleSpecifier: "./services"
783
+ });
784
+ }
785
+ if (this.config.options.dateType === "Date") {
786
+ sourceFile.addExportDeclaration({
787
+ moduleSpecifier: "./utils/date-transformer"
788
+ });
789
+ }
790
+ sourceFile.addExportDeclaration({
791
+ moduleSpecifier: "./utils/file-download"
792
+ });
793
+ sourceFile.saveSync();
794
+ }
795
+ };
796
+ __name(_MainIndexGenerator, "MainIndexGenerator");
797
+ var MainIndexGenerator = _MainIndexGenerator;
798
+
748
799
  // src/lib/generators/service/service.generator.ts
749
800
  var import_ts_morph3 = require("ts-morph");
750
- var path4 = __toESM(require("path"));
801
+ var path5 = __toESM(require("path"));
751
802
 
752
803
  // src/lib/utils/string.utils.ts
753
804
  function camelCase(str) {
@@ -1478,7 +1529,7 @@ var _ServiceGenerator = class _ServiceGenerator {
1478
1529
  this.methodGenerator = new ServiceMethodGenerator(config);
1479
1530
  }
1480
1531
  generate(outputRoot) {
1481
- const outputDir = path4.join(outputRoot, "services");
1532
+ const outputDir = path5.join(outputRoot, "services");
1482
1533
  const paths = this.extractPaths();
1483
1534
  const controllerGroups = this.groupPathsByController(paths);
1484
1535
  Object.entries(controllerGroups).forEach(([controllerName, operations]) => {
@@ -1488,7 +1539,7 @@ var _ServiceGenerator = class _ServiceGenerator {
1488
1539
  extractPaths() {
1489
1540
  const paths = [];
1490
1541
  const swaggerPaths = this.spec.paths || {};
1491
- Object.entries(swaggerPaths).forEach(([path6, pathItem]) => {
1542
+ Object.entries(swaggerPaths).forEach(([path8, pathItem]) => {
1492
1543
  const methods = [
1493
1544
  "get",
1494
1545
  "post",
@@ -1502,7 +1553,7 @@ var _ServiceGenerator = class _ServiceGenerator {
1502
1553
  if (pathItem[method]) {
1503
1554
  const operation = pathItem[method];
1504
1555
  paths.push({
1505
- path: path6,
1556
+ path: path8,
1506
1557
  method: method.toUpperCase(),
1507
1558
  operationId: operation.operationId,
1508
1559
  summary: operation.summary,
@@ -1534,12 +1585,12 @@ var _ServiceGenerator = class _ServiceGenerator {
1534
1585
  }
1535
1586
  groupPathsByController(paths) {
1536
1587
  const groups = {};
1537
- paths.forEach((path6) => {
1588
+ paths.forEach((path8) => {
1538
1589
  let controllerName = "Default";
1539
- if (path6.tags && path6.tags.length > 0) {
1540
- controllerName = path6.tags[0];
1590
+ if (path8.tags && path8.tags.length > 0) {
1591
+ controllerName = path8.tags[0];
1541
1592
  } else {
1542
- const pathParts = path6.path.split("/").filter((p) => p && !p.startsWith("{"));
1593
+ const pathParts = path8.path.split("/").filter((p) => p && !p.startsWith("{"));
1543
1594
  if (pathParts.length > 1) {
1544
1595
  controllerName = pascalCase(pathParts[1]);
1545
1596
  }
@@ -1548,13 +1599,13 @@ var _ServiceGenerator = class _ServiceGenerator {
1548
1599
  if (!groups[controllerName]) {
1549
1600
  groups[controllerName] = [];
1550
1601
  }
1551
- groups[controllerName].push(path6);
1602
+ groups[controllerName].push(path8);
1552
1603
  });
1553
1604
  return groups;
1554
1605
  }
1555
1606
  generateServiceFile(controllerName, operations, outputDir) {
1556
1607
  const fileName = `${kebabCase(controllerName)}.service.ts`;
1557
- const filePath = path4.join(outputDir, fileName);
1608
+ const filePath = path5.join(outputDir, fileName);
1558
1609
  const sourceFile = this.project.createSourceFile(filePath, "", {
1559
1610
  overwrite: true
1560
1611
  });
@@ -1714,15 +1765,15 @@ var ServiceGenerator = _ServiceGenerator;
1714
1765
 
1715
1766
  // src/lib/generators/service/service-index.generator.ts
1716
1767
  var fs2 = __toESM(require("fs"));
1717
- var path5 = __toESM(require("path"));
1768
+ var path6 = __toESM(require("path"));
1718
1769
  var _ServiceIndexGenerator = class _ServiceIndexGenerator {
1719
1770
  constructor(project) {
1720
1771
  __publicField(this, "project");
1721
1772
  this.project = project;
1722
1773
  }
1723
1774
  generateIndex(outputRoot) {
1724
- const servicesDir = path5.join(outputRoot, "services");
1725
- const indexPath = path5.join(servicesDir, "index.ts");
1775
+ const servicesDir = path6.join(outputRoot, "services");
1776
+ const indexPath = path6.join(servicesDir, "index.ts");
1726
1777
  const sourceFile = this.project.createSourceFile(indexPath, "", {
1727
1778
  overwrite: true
1728
1779
  });
@@ -1743,6 +1794,197 @@ var _ServiceIndexGenerator = class _ServiceIndexGenerator {
1743
1794
  __name(_ServiceIndexGenerator, "ServiceIndexGenerator");
1744
1795
  var ServiceIndexGenerator = _ServiceIndexGenerator;
1745
1796
 
1797
+ // src/lib/generators/utility/provider.generator.ts
1798
+ var path7 = __toESM(require("path"));
1799
+ var _ProviderGenerator = class _ProviderGenerator {
1800
+ constructor(project, config) {
1801
+ __publicField(this, "project");
1802
+ __publicField(this, "config");
1803
+ this.project = project;
1804
+ this.config = config;
1805
+ }
1806
+ generate(outputDir) {
1807
+ const filePath = path7.join(outputDir, "providers.ts");
1808
+ const sourceFile = this.project.createSourceFile(filePath, "", {
1809
+ overwrite: true
1810
+ });
1811
+ sourceFile.insertText(0, PROVIDER_GENERATOR_HEADER_COMMENT);
1812
+ sourceFile.addImportDeclarations([
1813
+ {
1814
+ namedImports: [
1815
+ "EnvironmentProviders",
1816
+ "Provider",
1817
+ "makeEnvironmentProviders"
1818
+ ],
1819
+ moduleSpecifier: "@angular/core"
1820
+ },
1821
+ {
1822
+ namedImports: [
1823
+ "HTTP_INTERCEPTORS"
1824
+ ],
1825
+ moduleSpecifier: "@angular/common/http"
1826
+ },
1827
+ {
1828
+ namedImports: [
1829
+ "BASE_PATH"
1830
+ ],
1831
+ moduleSpecifier: "./tokens"
1832
+ }
1833
+ ]);
1834
+ if (this.config.options.dateType === "Date") {
1835
+ sourceFile.addImportDeclaration({
1836
+ namedImports: [
1837
+ "DateInterceptor"
1838
+ ],
1839
+ moduleSpecifier: "./utils/date-transformer"
1840
+ });
1841
+ }
1842
+ sourceFile.addInterface({
1843
+ name: "NgOpenapiConfig",
1844
+ isExported: true,
1845
+ docs: [
1846
+ "Configuration options for ng-openapi providers"
1847
+ ],
1848
+ properties: [
1849
+ {
1850
+ name: "basePath",
1851
+ type: "string",
1852
+ docs: [
1853
+ "Base API URL"
1854
+ ]
1855
+ },
1856
+ {
1857
+ name: "enableDateTransform",
1858
+ type: "boolean",
1859
+ hasQuestionToken: true,
1860
+ docs: [
1861
+ "Enable automatic date transformation (default: true)"
1862
+ ]
1863
+ }
1864
+ ]
1865
+ });
1866
+ this.addMainProviderFunction(sourceFile);
1867
+ this.addAsyncProviderFunction(sourceFile);
1868
+ sourceFile.saveSync();
1869
+ }
1870
+ addMainProviderFunction(sourceFile) {
1871
+ const hasDateInterceptor = this.config.options.dateType === "Date";
1872
+ const functionBody = `
1873
+ const providers: Provider[] = [
1874
+ // Base path token
1875
+ {
1876
+ provide: BASE_PATH,
1877
+ useValue: config.basePath
1878
+ }
1879
+ ];
1880
+
1881
+ ${hasDateInterceptor ? `// Add date interceptor if enabled (default: true)
1882
+ if (config.enableDateTransform !== false) {
1883
+ providers.push({
1884
+ provide: HTTP_INTERCEPTORS,
1885
+ useClass: DateInterceptor,
1886
+ multi: true
1887
+ });
1888
+ }` : `// Date transformation not available (dateType: 'string' was used in generation)`}
1889
+
1890
+ return makeEnvironmentProviders(providers);`;
1891
+ sourceFile.addFunction({
1892
+ name: "provideNgOpenapi",
1893
+ isExported: true,
1894
+ docs: [
1895
+ "Provides all necessary configuration for ng-openapi generated services",
1896
+ "",
1897
+ "@example",
1898
+ "```typescript",
1899
+ "// In your app.config.ts",
1900
+ "import { provideNgOpenapi } from './api/providers';",
1901
+ "",
1902
+ "export const appConfig: ApplicationConfig = {",
1903
+ " providers: [",
1904
+ " provideNgOpenapi({",
1905
+ " basePath: 'https://api.example.com'",
1906
+ " }),",
1907
+ " // other providers...",
1908
+ " ]",
1909
+ "};",
1910
+ "```"
1911
+ ],
1912
+ parameters: [
1913
+ {
1914
+ name: "config",
1915
+ type: "NgOpenapiConfig"
1916
+ }
1917
+ ],
1918
+ returnType: "EnvironmentProviders",
1919
+ statements: functionBody
1920
+ });
1921
+ }
1922
+ addAsyncProviderFunction(sourceFile) {
1923
+ const hasDateInterceptor = this.config.options.dateType === "Date";
1924
+ const functionBody = `
1925
+ const providers: Provider[] = [];
1926
+
1927
+ // Handle async base path
1928
+ if (typeof config.basePath === 'string') {
1929
+ providers.push({
1930
+ provide: BASE_PATH,
1931
+ useValue: config.basePath
1932
+ });
1933
+ } else {
1934
+ providers.push({
1935
+ provide: BASE_PATH,
1936
+ useFactory: config.basePath
1937
+ });
1938
+ }
1939
+
1940
+ ${hasDateInterceptor ? `// Add date interceptor if enabled (default: true)
1941
+ if (config.enableDateTransform !== false) {
1942
+ providers.push({
1943
+ provide: HTTP_INTERCEPTORS,
1944
+ useClass: DateInterceptor,
1945
+ multi: true
1946
+ });
1947
+ }` : `// Date transformation not available (dateType: 'string' was used in generation)`}
1948
+
1949
+ return makeEnvironmentProviders(providers);`;
1950
+ sourceFile.addFunction({
1951
+ name: "provideNgOpenapiAsync",
1952
+ isExported: true,
1953
+ docs: [
1954
+ "Alternative function for cases where you need to handle async configuration",
1955
+ "",
1956
+ "@example",
1957
+ "```typescript",
1958
+ "// In your app.config.ts",
1959
+ "import { provideNgOpenapiAsync } from './api/providers';",
1960
+ "",
1961
+ "export const appConfig: ApplicationConfig = {",
1962
+ " providers: [",
1963
+ " provideNgOpenapiAsync({",
1964
+ " basePath: () => import('./config').then(c => c.apiConfig.baseUrl)",
1965
+ " }),",
1966
+ " // other providers...",
1967
+ " ]",
1968
+ "};",
1969
+ "```"
1970
+ ],
1971
+ parameters: [
1972
+ {
1973
+ name: "config",
1974
+ type: `{
1975
+ basePath: string | (() => Promise<string>);
1976
+ enableDateTransform?: boolean;
1977
+ }`
1978
+ }
1979
+ ],
1980
+ returnType: "EnvironmentProviders",
1981
+ statements: functionBody
1982
+ });
1983
+ }
1984
+ };
1985
+ __name(_ProviderGenerator, "ProviderGenerator");
1986
+ var ProviderGenerator = _ProviderGenerator;
1987
+
1746
1988
  // src/lib/core/generator.ts
1747
1989
  var fs3 = __toESM(require("fs"));
1748
1990
  function generateFromConfig(config) {
@@ -1787,6 +2029,11 @@ function generateFromConfig(config) {
1787
2029
  indexGenerator.generateIndex(outputPath);
1788
2030
  console.log(`\u2705 Angular services generated`);
1789
2031
  }
2032
+ const providerGenerator = new ProviderGenerator(project, config);
2033
+ providerGenerator.generate(outputPath);
2034
+ console.log(`\u2705 Provider functions generated`);
2035
+ const mainIndexGenerator = new MainIndexGenerator(project, config);
2036
+ mainIndexGenerator.generateMainIndex(outputPath);
1790
2037
  console.log("\u{1F389} Generation completed successfully at:", outputPath);
1791
2038
  } catch (error) {
1792
2039
  if (error instanceof Error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ng-openapi",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Generate Angular services and TypeScript types from OpenAPI/Swagger specifications",
5
5
  "keywords": [
6
6
  "angular",