ng-openapi 0.0.22 → 0.0.23

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/cli.cjs +263 -102
  2. package/index.d.ts +1 -0
  3. package/index.js +150 -92
  4. package/package.json +1 -1
package/cli.cjs CHANGED
@@ -7,6 +7,10 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
9
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
10
14
  var __copyProps = (to, from, except, desc) => {
11
15
  if (from && typeof from === "object" || typeof from === "function") {
12
16
  for (let key of __getOwnPropNames(from))
@@ -23,8 +27,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
27
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
28
  mod
25
29
  ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
26
31
 
27
32
  // src/lib/cli.ts
33
+ var cli_exports = {};
34
+ __export(cli_exports, {
35
+ generateFromOptions: () => generateFromOptions
36
+ });
37
+ module.exports = __toCommonJS(cli_exports);
28
38
  var import_commander = require("commander");
29
39
  var path8 = __toESM(require("path"));
30
40
  var fs4 = __toESM(require("fs"));
@@ -402,8 +412,10 @@ var TokenGenerator = class {
402
412
  __name(this, "TokenGenerator");
403
413
  }
404
414
  project;
405
- constructor(project) {
415
+ config;
416
+ constructor(project, config) {
406
417
  this.project = project;
418
+ this.config = config;
407
419
  }
408
420
  generate(outputDir) {
409
421
  const tokensDir = path.join(outputDir, "tokens");
@@ -417,23 +429,55 @@ var TokenGenerator = class {
417
429
  ],
418
430
  moduleSpecifier: "@angular/core"
419
431
  });
432
+ const clientName = this.config.clientName || "DEFAULT";
433
+ const upperCaseClientName = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
420
434
  sourceFile.addVariableStatement({
421
435
  isExported: true,
422
436
  declarationKind: import_ts_morph2.VariableDeclarationKind.Const,
423
437
  declarations: [
424
438
  {
425
- name: "BASE_PATH",
426
- initializer: `new InjectionToken<string>('BASE_PATH', {
439
+ name: `${upperCaseClientName}_BASE_PATH`,
440
+ initializer: `new InjectionToken<string>('${upperCaseClientName}_BASE_PATH', {
427
441
  providedIn: 'root',
428
- factory: () => '/api', // Default fallback
442
+ factory: () => '/api',
429
443
  })`
430
444
  }
431
445
  ],
432
446
  leadingTrivia: `/**
433
- * Injection token for the base API path
447
+ * Base path token for ${clientName} client
448
+ */
449
+ `
450
+ });
451
+ sourceFile.addVariableStatement({
452
+ isExported: true,
453
+ declarationKind: import_ts_morph2.VariableDeclarationKind.Const,
454
+ declarations: [
455
+ {
456
+ name: `${upperCaseClientName}_HTTP_CLIENT`,
457
+ initializer: `new InjectionToken<HttpClient>('${upperCaseClientName}_HTTP_CLIENT')`
458
+ }
459
+ ],
460
+ leadingTrivia: `/**
461
+ * HTTP client token for ${clientName} client
434
462
  */
435
463
  `
436
464
  });
465
+ if (!this.config.clientName) {
466
+ sourceFile.addVariableStatement({
467
+ isExported: true,
468
+ declarationKind: import_ts_morph2.VariableDeclarationKind.Const,
469
+ declarations: [
470
+ {
471
+ name: "BASE_PATH",
472
+ initializer: `${upperCaseClientName}_BASE_PATH`
473
+ }
474
+ ],
475
+ leadingTrivia: `/**
476
+ * @deprecated Use ${upperCaseClientName}_BASE_PATH instead
477
+ */
478
+ `
479
+ });
480
+ }
437
481
  sourceFile.saveSync();
438
482
  }
439
483
  };
@@ -1702,6 +1746,8 @@ var ServiceGenerator = class {
1702
1746
  });
1703
1747
  }
1704
1748
  addImports(sourceFile, usedTypes) {
1749
+ const clientName = this.config.clientName || "DEFAULT";
1750
+ const upperCaseClientName = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
1705
1751
  sourceFile.addImportDeclarations([
1706
1752
  {
1707
1753
  namedImports: [
@@ -1729,11 +1775,15 @@ var ServiceGenerator = class {
1729
1775
  },
1730
1776
  {
1731
1777
  namedImports: [
1732
- "BASE_PATH"
1778
+ `${upperCaseClientName}_BASE_PATH`,
1779
+ `${upperCaseClientName}_HTTP_CLIENT`
1733
1780
  ],
1734
1781
  moduleSpecifier: "../tokens"
1735
1782
  }
1736
1783
  ]);
1784
+ if (!this.config.clientName) {
1785
+ sourceFile.getImportDeclaration("../tokens")?.addNamedImport("BASE_PATH");
1786
+ }
1737
1787
  if (usedTypes.size > 0) {
1738
1788
  sourceFile.addImportDeclaration({
1739
1789
  namedImports: Array.from(usedTypes).sort(),
@@ -1743,6 +1793,8 @@ var ServiceGenerator = class {
1743
1793
  }
1744
1794
  addServiceClass(sourceFile, controllerName, operations) {
1745
1795
  const className = `${controllerName}Service`;
1796
+ const clientName = this.config.clientName || "DEFAULT";
1797
+ const upperCaseClientName = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
1746
1798
  sourceFile.insertText(0, SERVICE_GENERATOR_HEADER_COMMENT(controllerName));
1747
1799
  const serviceClass = sourceFile.addClass({
1748
1800
  name: className,
@@ -1761,14 +1813,14 @@ var ServiceGenerator = class {
1761
1813
  type: "HttpClient",
1762
1814
  scope: import_ts_morph3.Scope.Private,
1763
1815
  isReadonly: true,
1764
- initializer: "inject(HttpClient)"
1816
+ initializer: `inject(${upperCaseClientName}_HTTP_CLIENT, { optional: true }) ?? inject(HttpClient)`
1765
1817
  });
1766
1818
  serviceClass.addProperty({
1767
1819
  name: "basePath",
1768
1820
  type: "string",
1769
1821
  scope: import_ts_morph3.Scope.Private,
1770
1822
  isReadonly: true,
1771
- initializer: "inject(BASE_PATH)"
1823
+ initializer: `inject(${upperCaseClientName}_BASE_PATH)`
1772
1824
  });
1773
1825
  operations.forEach((operation) => {
1774
1826
  this.methodGenerator.addServiceMethod(serviceClass, operation);
@@ -1832,24 +1884,34 @@ var ProviderGenerator = class {
1832
1884
  overwrite: true
1833
1885
  });
1834
1886
  sourceFile.insertText(0, PROVIDER_GENERATOR_HEADER_COMMENT);
1887
+ const clientName = this.config.clientName || "Default";
1888
+ const pascalClientName = this.pascalCase(clientName);
1889
+ const upperCaseClientName = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
1835
1890
  sourceFile.addImportDeclarations([
1836
1891
  {
1837
1892
  namedImports: [
1838
1893
  "EnvironmentProviders",
1839
1894
  "Provider",
1840
- "makeEnvironmentProviders"
1895
+ "makeEnvironmentProviders",
1896
+ "Type",
1897
+ "Injector",
1898
+ "inject"
1841
1899
  ],
1842
1900
  moduleSpecifier: "@angular/core"
1843
1901
  },
1844
1902
  {
1845
1903
  namedImports: [
1846
- "HTTP_INTERCEPTORS"
1904
+ "HttpClient",
1905
+ "HttpInterceptor",
1906
+ "HttpHandler",
1907
+ "HttpRequest"
1847
1908
  ],
1848
1909
  moduleSpecifier: "@angular/common/http"
1849
1910
  },
1850
1911
  {
1851
1912
  namedImports: [
1852
- "BASE_PATH"
1913
+ `${upperCaseClientName}_BASE_PATH`,
1914
+ `${upperCaseClientName}_HTTP_CLIENT`
1853
1915
  ],
1854
1916
  moduleSpecifier: "./tokens"
1855
1917
  }
@@ -1863,10 +1925,10 @@ var ProviderGenerator = class {
1863
1925
  });
1864
1926
  }
1865
1927
  sourceFile.addInterface({
1866
- name: "NgOpenapiConfig",
1928
+ name: `${pascalClientName}Config`,
1867
1929
  isExported: true,
1868
1930
  docs: [
1869
- "Configuration options for ng-openapi providers"
1931
+ `Configuration options for ${clientName} API client`
1870
1932
  ],
1871
1933
  properties: [
1872
1934
  {
@@ -1876,6 +1938,14 @@ var ProviderGenerator = class {
1876
1938
  "Base API URL"
1877
1939
  ]
1878
1940
  },
1941
+ {
1942
+ name: "interceptors",
1943
+ type: "Type<HttpInterceptor>[]",
1944
+ hasQuestionToken: true,
1945
+ docs: [
1946
+ "HTTP interceptors to apply to this client's requests"
1947
+ ]
1948
+ },
1879
1949
  {
1880
1950
  name: "enableDateTransform",
1881
1951
  type: "boolean",
@@ -1886,105 +1956,102 @@ var ProviderGenerator = class {
1886
1956
  }
1887
1957
  ]
1888
1958
  });
1889
- this.addMainProviderFunction(sourceFile);
1890
- this.addAsyncProviderFunction(sourceFile);
1959
+ this.addInterceptorChainHelper(sourceFile);
1960
+ this.addClientProviderFunction(sourceFile, pascalClientName, upperCaseClientName);
1891
1961
  sourceFile.saveSync();
1892
1962
  }
1893
- addMainProviderFunction(sourceFile) {
1894
- const hasDateInterceptor = this.config.options.dateType === "Date";
1895
- const functionBody = `
1896
- const providers: Provider[] = [
1897
- // Base path token
1898
- {
1899
- provide: BASE_PATH,
1900
- useValue: config.basePath
1901
- }
1902
- ];
1903
-
1904
- ${hasDateInterceptor ? `// Add date interceptor if enabled (default: true)
1905
- if (config.enableDateTransform !== false) {
1906
- providers.push({
1907
- provide: HTTP_INTERCEPTORS,
1908
- useClass: DateInterceptor,
1909
- multi: true
1910
- });
1911
- }` : `// Date transformation not available (dateType: 'string' was used in generation)`}
1912
-
1913
- return makeEnvironmentProviders(providers);`;
1963
+ addInterceptorChainHelper(sourceFile) {
1914
1964
  sourceFile.addFunction({
1915
- name: "provideNgOpenapi",
1916
- isExported: true,
1965
+ name: "createHttpClientWithInterceptors",
1917
1966
  docs: [
1918
- "Provides all necessary configuration for ng-openapi generated services",
1919
- "",
1920
- "@example",
1921
- "```typescript",
1922
- "// In your app.config.ts",
1923
- "import { provideNgOpenapi } from './api/providers';",
1924
- "",
1925
- "export const appConfig: ApplicationConfig = {",
1926
- " providers: [",
1927
- " provideNgOpenapi({",
1928
- " basePath: 'https://api.example.com'",
1929
- " }),",
1930
- " // other providers...",
1931
- " ]",
1932
- "};",
1933
- "```"
1967
+ "Creates an HttpClient with a custom interceptor chain"
1934
1968
  ],
1935
1969
  parameters: [
1936
1970
  {
1937
- name: "config",
1938
- type: "NgOpenapiConfig"
1971
+ name: "baseClient",
1972
+ type: "HttpClient"
1973
+ },
1974
+ {
1975
+ name: "interceptors",
1976
+ type: "HttpInterceptor[]"
1939
1977
  }
1940
1978
  ],
1941
- returnType: "EnvironmentProviders",
1942
- statements: functionBody
1979
+ returnType: "HttpClient",
1980
+ statements: `
1981
+ if (!interceptors.length) {
1982
+ return baseClient;
1983
+ }
1984
+
1985
+ // Create a custom handler that applies interceptors in sequence
1986
+ let handler = baseClient.handler;
1987
+
1988
+ // Apply interceptors in reverse order (last interceptor wraps the original handler)
1989
+ for (let i = interceptors.length - 1; i >= 0; i--) {
1990
+ const currentHandler = handler;
1991
+ const interceptor = interceptors[i];
1992
+
1993
+ handler = {
1994
+ handle: (req: HttpRequest<any>) => interceptor.intercept(req, currentHandler)
1995
+ };
1996
+ }
1997
+
1998
+ // Return a new HttpClient with the custom handler
1999
+ return new (baseClient.constructor as any)(handler);`
1943
2000
  });
1944
2001
  }
1945
- addAsyncProviderFunction(sourceFile) {
2002
+ addClientProviderFunction(sourceFile, pascalClientName, upperCaseClientName) {
1946
2003
  const hasDateInterceptor = this.config.options.dateType === "Date";
1947
2004
  const functionBody = `
1948
- const providers: Provider[] = [];
1949
-
1950
- // Handle async base path
1951
- if (typeof config.basePath === 'string') {
1952
- providers.push({
1953
- provide: BASE_PATH,
2005
+ const providers: Provider[] = [
2006
+ // Base path token
2007
+ {
2008
+ provide: ${upperCaseClientName}_BASE_PATH,
1954
2009
  useValue: config.basePath
1955
- });
1956
- } else {
1957
- providers.push({
1958
- provide: BASE_PATH,
1959
- useFactory: config.basePath
1960
- });
1961
- }
1962
-
1963
- ${hasDateInterceptor ? `// Add date interceptor if enabled (default: true)
1964
- if (config.enableDateTransform !== false) {
1965
- providers.push({
1966
- provide: HTTP_INTERCEPTORS,
1967
- useClass: DateInterceptor,
1968
- multi: true
1969
- });
1970
- }` : `// Date transformation not available (dateType: 'string' was used in generation)`}
2010
+ },
2011
+
2012
+ // HTTP client with custom interceptors
2013
+ {
2014
+ provide: ${upperCaseClientName}_HTTP_CLIENT,
2015
+ useFactory: (baseClient: HttpClient, injector: Injector) => {
2016
+ const interceptorInstances: HttpInterceptor[] = [];
2017
+
2018
+ // Add custom interceptors
2019
+ if (config.interceptors?.length) {
2020
+ config.interceptors.forEach(interceptorClass => {
2021
+ interceptorInstances.push(injector.get(interceptorClass));
2022
+ });
2023
+ }
2024
+
2025
+ ${hasDateInterceptor ? `
2026
+ // Add date interceptor if enabled (default: true)
2027
+ if (config.enableDateTransform !== false) {
2028
+ interceptorInstances.push(injector.get(DateInterceptor));
2029
+ }` : ""}
2030
+
2031
+ return createHttpClientWithInterceptors(baseClient, interceptorInstances);
2032
+ },
2033
+ deps: [HttpClient, Injector]
2034
+ }
2035
+ ];
1971
2036
 
1972
2037
  return makeEnvironmentProviders(providers);`;
1973
2038
  sourceFile.addFunction({
1974
- name: "provideNgOpenapiAsync",
2039
+ name: `provide${pascalClientName}`,
1975
2040
  isExported: true,
1976
2041
  docs: [
1977
- "Alternative function for cases where you need to handle async configuration",
2042
+ `Provides configuration for ${pascalClientName} API client`,
1978
2043
  "",
1979
2044
  "@example",
1980
2045
  "```typescript",
1981
- "// In your app.config.ts",
1982
- "import { provideNgOpenapiAsync } from './api/providers';",
2046
+ `// In your app.config.ts`,
2047
+ `import { provide${pascalClientName} } from './api/providers';`,
2048
+ `import { AuthInterceptor } from './interceptors/auth.interceptor';`,
1983
2049
  "",
1984
2050
  "export const appConfig: ApplicationConfig = {",
1985
2051
  " providers: [",
1986
- " provideNgOpenapiAsync({",
1987
- " basePath: () => import('./config').then(c => c.apiConfig.baseUrl)",
2052
+ ` provide${pascalClientName}({`,
2053
+ " basePath: 'https://api.example.com',",
2054
+ " interceptors: [AuthInterceptor]",
1988
2055
  " }),",
1989
2056
  " // other providers...",
1990
2057
  " ]",
@@ -1994,16 +2061,16 @@ return makeEnvironmentProviders(providers);`;
1994
2061
  parameters: [
1995
2062
  {
1996
2063
  name: "config",
1997
- type: `{
1998
- basePath: string | (() => Promise<string>);
1999
- enableDateTransform?: boolean;
2000
- }`
2064
+ type: `${pascalClientName}Config`
2001
2065
  }
2002
2066
  ],
2003
2067
  returnType: "EnvironmentProviders",
2004
2068
  statements: functionBody
2005
2069
  });
2006
2070
  }
2071
+ pascalCase(str) {
2072
+ return str.replace(/(?:^|[-_])([a-z])/g, (_, char) => char.toUpperCase());
2073
+ }
2007
2074
  };
2008
2075
 
2009
2076
  // src/lib/core/generator.ts
@@ -2033,7 +2100,7 @@ async function generateFromConfig(config) {
2033
2100
  typeGenerator.generate();
2034
2101
  console.log(`\u2705 TypeScript interfaces generated`);
2035
2102
  if (generateServices) {
2036
- const tokenGenerator = new TokenGenerator(project);
2103
+ const tokenGenerator = new TokenGenerator(project, config);
2037
2104
  tokenGenerator.generate(outputPath);
2038
2105
  if (config.options.dateType === "Date") {
2039
2106
  const dateTransformer = new DateTransformerGenerator(project);
@@ -2066,6 +2133,94 @@ async function generateFromConfig(config) {
2066
2133
  }
2067
2134
  __name(generateFromConfig, "generateFromConfig");
2068
2135
 
2136
+ // package.json
2137
+ var package_default = {
2138
+ name: "ng-openapi",
2139
+ version: "0.0.22",
2140
+ description: "Generate Angular services and TypeScript types from OpenAPI/Swagger specifications",
2141
+ keywords: [
2142
+ "angular",
2143
+ "openapi",
2144
+ "swagger",
2145
+ "codegen",
2146
+ "typescript",
2147
+ "generator",
2148
+ "code-generator",
2149
+ "api",
2150
+ "rest",
2151
+ "http",
2152
+ "cli"
2153
+ ],
2154
+ author: {
2155
+ name: "Tareq Jami",
2156
+ email: "info@jami-it.de",
2157
+ url: "http://tareqjami.de"
2158
+ },
2159
+ license: "MIT",
2160
+ homepage: "https://ng-openapi.dev/",
2161
+ bugs: {
2162
+ url: "https://github.com/ng-openapi/ng-openapi/issues"
2163
+ },
2164
+ repository: {
2165
+ type: "git",
2166
+ url: "git+https://github.com/ng-openapi/ng-openapi.git",
2167
+ directory: "packages/ng-openapi"
2168
+ },
2169
+ funding: {
2170
+ type: "github",
2171
+ url: "https://github.com/sponsors/ng-openapi"
2172
+ },
2173
+ main: "./index.cjs",
2174
+ module: "./index.js",
2175
+ types: "./index.d.ts",
2176
+ bin: {
2177
+ "ng-openapi": "./cli.cjs"
2178
+ },
2179
+ files: [
2180
+ "index.js",
2181
+ "index.cjs",
2182
+ "index.d.ts",
2183
+ "cli.cjs",
2184
+ "lib/**/*.js",
2185
+ "lib/**/*.cjs",
2186
+ "lib/**/*.d.ts",
2187
+ "README.md",
2188
+ "LICENSE",
2189
+ "CHANGELOG.md"
2190
+ ],
2191
+ scripts: {
2192
+ prepublishOnly: "echo 'Build the package using: npm run build:ng-openapi from workspace root'",
2193
+ build: "tsup"
2194
+ },
2195
+ dependencies: {
2196
+ commander: "^14.0.0",
2197
+ "ts-morph": "^26.0.0",
2198
+ "ts-node": "^10.9.2",
2199
+ typescript: "^5.8.3",
2200
+ "@types/swagger-schema-official": "^2.0.25"
2201
+ },
2202
+ peerDependencies: {
2203
+ "@angular/core": ">=15",
2204
+ "@angular/common": ">=15"
2205
+ },
2206
+ peerDependenciesMeta: {
2207
+ "@angular/core": {
2208
+ optional: false
2209
+ },
2210
+ "@angular/common": {
2211
+ optional: false
2212
+ }
2213
+ },
2214
+ engines: {
2215
+ node: ">=18.0.0",
2216
+ npm: ">=8.0.0"
2217
+ },
2218
+ publishConfig: {
2219
+ access: "public",
2220
+ registry: "https://registry.npmjs.org/"
2221
+ }
2222
+ };
2223
+
2069
2224
  // src/lib/cli.ts
2070
2225
  var program = new import_commander.Command();
2071
2226
  async function loadConfigFile(configPath) {
@@ -2092,17 +2247,19 @@ __name(loadConfigFile, "loadConfigFile");
2092
2247
  async function generateFromOptions(options) {
2093
2248
  try {
2094
2249
  if (options.config) {
2095
- const config = await loadConfigFile(options.config);
2096
- await generateFromConfig(config);
2097
- } else if (options.input) {
2098
- const inputPath = path8.resolve(options.input);
2099
- if (!fs4.existsSync(inputPath)) {
2100
- console.error(`Error: Input file not found: ${inputPath}`);
2101
- process.exit(1);
2250
+ const configPaths = Array.isArray(options.config) ? options.config : [
2251
+ options.config
2252
+ ];
2253
+ for (const configPath of configPaths) {
2254
+ const config = await loadConfigFile(configPath);
2255
+ await generateFromConfig(config);
2256
+ console.log(`\u2728 Generated client: ${config.clientName || "default"}`);
2102
2257
  }
2258
+ } else if (options.input) {
2103
2259
  const config = {
2104
- input: inputPath,
2260
+ input: path8.resolve(options.input),
2105
2261
  output: options.output || "./src/generated",
2262
+ clientName: options.clientName,
2106
2263
  options: {
2107
2264
  dateType: options.dateType || "Date",
2108
2265
  enumStyle: "enum",
@@ -2116,14 +2273,14 @@ async function generateFromOptions(options) {
2116
2273
  program.help();
2117
2274
  process.exit(1);
2118
2275
  }
2119
- console.log("\u2728 Generation completed successfully!");
2276
+ console.log("\u2728 All clients generated successfully!");
2120
2277
  } catch (error) {
2121
2278
  console.error("\u274C Generation failed:", error instanceof Error ? error.message : error);
2122
2279
  process.exit(1);
2123
2280
  }
2124
2281
  }
2125
2282
  __name(generateFromOptions, "generateFromOptions");
2126
- program.name("ng-openapi").description("Generate Angular services and types from Swagger/OpenAPI spec").version("0.0.1").option("-c, --config <path>", "Path to configuration file").option("-i, --input <path>", "Path to Swagger/OpenAPI specification file").option("-o, --output <path>", "Output directory", "./src/generated").option("--types-only", "Generate only TypeScript interfaces").option("--date-type <type>", "Date type to use (string | Date)", "Date").action(async (options) => {
2283
+ program.name("ng-openapi").description("Generate Angular services and types from Swagger/OpenAPI spec").version(package_default.version).option("-c, --config <path>", "Path to configuration file").option("-i, --input <path>", "Path to Swagger/OpenAPI specification file").option("-o, --output <path>", "Output directory", "./src/generated").option("--types-only", "Generate only TypeScript interfaces").option("--date-type <type>", "Date type to use (string | Date)", "Date").action(async (options) => {
2127
2284
  await generateFromOptions(options);
2128
2285
  });
2129
2286
  program.command("generate").alias("gen").description("Generate code from Swagger specification").option("-c, --config <path>", "Path to configuration file").option("-i, --input <path>", "Path to Swagger/OpenAPI specification file").option("-o, --output <path>", "Output directory", "./src/generated").option("--types-only", "Generate only TypeScript interfaces").option("--date-type <type>", "Date type to use (string | Date)", "Date").action(async (options) => {
@@ -2138,4 +2295,8 @@ program.on("--help", () => {
2138
2295
  console.log(" $ ng-openapi generate -i ./api.yaml --types-only");
2139
2296
  });
2140
2297
  program.parse();
2298
+ // Annotate the CommonJS export names for ESM import in node:
2299
+ 0 && (module.exports = {
2300
+ generateFromOptions
2301
+ });
2141
2302
  //# sourceMappingURL=cli.cjs.map
package/index.d.ts CHANGED
@@ -28,6 +28,7 @@ interface TypeSchema {
28
28
  interface GeneratorConfig {
29
29
  input: string;
30
30
  output: string;
31
+ clientName?: string;
31
32
  options: {
32
33
  dateType: "string" | "Date";
33
34
  enumStyle: "enum" | "union";
package/index.js CHANGED
@@ -441,9 +441,11 @@ var TypeGenerator = _TypeGenerator;
441
441
  var import_ts_morph2 = require("ts-morph");
442
442
  var path = __toESM(require("path"));
443
443
  var _TokenGenerator = class _TokenGenerator {
444
- constructor(project) {
444
+ constructor(project, config) {
445
445
  __publicField(this, "project");
446
+ __publicField(this, "config");
446
447
  this.project = project;
448
+ this.config = config;
447
449
  }
448
450
  generate(outputDir) {
449
451
  const tokensDir = path.join(outputDir, "tokens");
@@ -457,23 +459,55 @@ var _TokenGenerator = class _TokenGenerator {
457
459
  ],
458
460
  moduleSpecifier: "@angular/core"
459
461
  });
462
+ const clientName = this.config.clientName || "DEFAULT";
463
+ const upperCaseClientName = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
460
464
  sourceFile.addVariableStatement({
461
465
  isExported: true,
462
466
  declarationKind: import_ts_morph2.VariableDeclarationKind.Const,
463
467
  declarations: [
464
468
  {
465
- name: "BASE_PATH",
466
- initializer: `new InjectionToken<string>('BASE_PATH', {
469
+ name: `${upperCaseClientName}_BASE_PATH`,
470
+ initializer: `new InjectionToken<string>('${upperCaseClientName}_BASE_PATH', {
467
471
  providedIn: 'root',
468
- factory: () => '/api', // Default fallback
472
+ factory: () => '/api',
469
473
  })`
470
474
  }
471
475
  ],
472
476
  leadingTrivia: `/**
473
- * Injection token for the base API path
477
+ * Base path token for ${clientName} client
478
+ */
479
+ `
480
+ });
481
+ sourceFile.addVariableStatement({
482
+ isExported: true,
483
+ declarationKind: import_ts_morph2.VariableDeclarationKind.Const,
484
+ declarations: [
485
+ {
486
+ name: `${upperCaseClientName}_HTTP_CLIENT`,
487
+ initializer: `new InjectionToken<HttpClient>('${upperCaseClientName}_HTTP_CLIENT')`
488
+ }
489
+ ],
490
+ leadingTrivia: `/**
491
+ * HTTP client token for ${clientName} client
474
492
  */
475
493
  `
476
494
  });
495
+ if (!this.config.clientName) {
496
+ sourceFile.addVariableStatement({
497
+ isExported: true,
498
+ declarationKind: import_ts_morph2.VariableDeclarationKind.Const,
499
+ declarations: [
500
+ {
501
+ name: "BASE_PATH",
502
+ initializer: `${upperCaseClientName}_BASE_PATH`
503
+ }
504
+ ],
505
+ leadingTrivia: `/**
506
+ * @deprecated Use ${upperCaseClientName}_BASE_PATH instead
507
+ */
508
+ `
509
+ });
510
+ }
477
511
  sourceFile.saveSync();
478
512
  }
479
513
  };
@@ -1746,6 +1780,9 @@ var _ServiceGenerator = class _ServiceGenerator {
1746
1780
  });
1747
1781
  }
1748
1782
  addImports(sourceFile, usedTypes) {
1783
+ var _a;
1784
+ const clientName = this.config.clientName || "DEFAULT";
1785
+ const upperCaseClientName = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
1749
1786
  sourceFile.addImportDeclarations([
1750
1787
  {
1751
1788
  namedImports: [
@@ -1773,11 +1810,15 @@ var _ServiceGenerator = class _ServiceGenerator {
1773
1810
  },
1774
1811
  {
1775
1812
  namedImports: [
1776
- "BASE_PATH"
1813
+ `${upperCaseClientName}_BASE_PATH`,
1814
+ `${upperCaseClientName}_HTTP_CLIENT`
1777
1815
  ],
1778
1816
  moduleSpecifier: "../tokens"
1779
1817
  }
1780
1818
  ]);
1819
+ if (!this.config.clientName) {
1820
+ (_a = sourceFile.getImportDeclaration("../tokens")) == null ? void 0 : _a.addNamedImport("BASE_PATH");
1821
+ }
1781
1822
  if (usedTypes.size > 0) {
1782
1823
  sourceFile.addImportDeclaration({
1783
1824
  namedImports: Array.from(usedTypes).sort(),
@@ -1787,6 +1828,8 @@ var _ServiceGenerator = class _ServiceGenerator {
1787
1828
  }
1788
1829
  addServiceClass(sourceFile, controllerName, operations) {
1789
1830
  const className = `${controllerName}Service`;
1831
+ const clientName = this.config.clientName || "DEFAULT";
1832
+ const upperCaseClientName = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
1790
1833
  sourceFile.insertText(0, SERVICE_GENERATOR_HEADER_COMMENT(controllerName));
1791
1834
  const serviceClass = sourceFile.addClass({
1792
1835
  name: className,
@@ -1805,14 +1848,14 @@ var _ServiceGenerator = class _ServiceGenerator {
1805
1848
  type: "HttpClient",
1806
1849
  scope: import_ts_morph3.Scope.Private,
1807
1850
  isReadonly: true,
1808
- initializer: "inject(HttpClient)"
1851
+ initializer: `inject(${upperCaseClientName}_HTTP_CLIENT, { optional: true }) ?? inject(HttpClient)`
1809
1852
  });
1810
1853
  serviceClass.addProperty({
1811
1854
  name: "basePath",
1812
1855
  type: "string",
1813
1856
  scope: import_ts_morph3.Scope.Private,
1814
1857
  isReadonly: true,
1815
- initializer: "inject(BASE_PATH)"
1858
+ initializer: `inject(${upperCaseClientName}_BASE_PATH)`
1816
1859
  });
1817
1860
  operations.forEach((operation) => {
1818
1861
  this.methodGenerator.addServiceMethod(serviceClass, operation);
@@ -1874,24 +1917,34 @@ var _ProviderGenerator = class _ProviderGenerator {
1874
1917
  overwrite: true
1875
1918
  });
1876
1919
  sourceFile.insertText(0, PROVIDER_GENERATOR_HEADER_COMMENT);
1920
+ const clientName = this.config.clientName || "Default";
1921
+ const pascalClientName = this.pascalCase(clientName);
1922
+ const upperCaseClientName = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
1877
1923
  sourceFile.addImportDeclarations([
1878
1924
  {
1879
1925
  namedImports: [
1880
1926
  "EnvironmentProviders",
1881
1927
  "Provider",
1882
- "makeEnvironmentProviders"
1928
+ "makeEnvironmentProviders",
1929
+ "Type",
1930
+ "Injector",
1931
+ "inject"
1883
1932
  ],
1884
1933
  moduleSpecifier: "@angular/core"
1885
1934
  },
1886
1935
  {
1887
1936
  namedImports: [
1888
- "HTTP_INTERCEPTORS"
1937
+ "HttpClient",
1938
+ "HttpInterceptor",
1939
+ "HttpHandler",
1940
+ "HttpRequest"
1889
1941
  ],
1890
1942
  moduleSpecifier: "@angular/common/http"
1891
1943
  },
1892
1944
  {
1893
1945
  namedImports: [
1894
- "BASE_PATH"
1946
+ `${upperCaseClientName}_BASE_PATH`,
1947
+ `${upperCaseClientName}_HTTP_CLIENT`
1895
1948
  ],
1896
1949
  moduleSpecifier: "./tokens"
1897
1950
  }
@@ -1905,10 +1958,10 @@ var _ProviderGenerator = class _ProviderGenerator {
1905
1958
  });
1906
1959
  }
1907
1960
  sourceFile.addInterface({
1908
- name: "NgOpenapiConfig",
1961
+ name: `${pascalClientName}Config`,
1909
1962
  isExported: true,
1910
1963
  docs: [
1911
- "Configuration options for ng-openapi providers"
1964
+ `Configuration options for ${clientName} API client`
1912
1965
  ],
1913
1966
  properties: [
1914
1967
  {
@@ -1918,6 +1971,14 @@ var _ProviderGenerator = class _ProviderGenerator {
1918
1971
  "Base API URL"
1919
1972
  ]
1920
1973
  },
1974
+ {
1975
+ name: "interceptors",
1976
+ type: "Type<HttpInterceptor>[]",
1977
+ hasQuestionToken: true,
1978
+ docs: [
1979
+ "HTTP interceptors to apply to this client's requests"
1980
+ ]
1981
+ },
1921
1982
  {
1922
1983
  name: "enableDateTransform",
1923
1984
  type: "boolean",
@@ -1928,105 +1989,102 @@ var _ProviderGenerator = class _ProviderGenerator {
1928
1989
  }
1929
1990
  ]
1930
1991
  });
1931
- this.addMainProviderFunction(sourceFile);
1932
- this.addAsyncProviderFunction(sourceFile);
1992
+ this.addInterceptorChainHelper(sourceFile);
1993
+ this.addClientProviderFunction(sourceFile, pascalClientName, upperCaseClientName);
1933
1994
  sourceFile.saveSync();
1934
1995
  }
1935
- addMainProviderFunction(sourceFile) {
1936
- const hasDateInterceptor = this.config.options.dateType === "Date";
1937
- const functionBody = `
1938
- const providers: Provider[] = [
1939
- // Base path token
1940
- {
1941
- provide: BASE_PATH,
1942
- useValue: config.basePath
1943
- }
1944
- ];
1945
-
1946
- ${hasDateInterceptor ? `// Add date interceptor if enabled (default: true)
1947
- if (config.enableDateTransform !== false) {
1948
- providers.push({
1949
- provide: HTTP_INTERCEPTORS,
1950
- useClass: DateInterceptor,
1951
- multi: true
1952
- });
1953
- }` : `// Date transformation not available (dateType: 'string' was used in generation)`}
1954
-
1955
- return makeEnvironmentProviders(providers);`;
1996
+ addInterceptorChainHelper(sourceFile) {
1956
1997
  sourceFile.addFunction({
1957
- name: "provideNgOpenapi",
1958
- isExported: true,
1998
+ name: "createHttpClientWithInterceptors",
1959
1999
  docs: [
1960
- "Provides all necessary configuration for ng-openapi generated services",
1961
- "",
1962
- "@example",
1963
- "```typescript",
1964
- "// In your app.config.ts",
1965
- "import { provideNgOpenapi } from './api/providers';",
1966
- "",
1967
- "export const appConfig: ApplicationConfig = {",
1968
- " providers: [",
1969
- " provideNgOpenapi({",
1970
- " basePath: 'https://api.example.com'",
1971
- " }),",
1972
- " // other providers...",
1973
- " ]",
1974
- "};",
1975
- "```"
2000
+ "Creates an HttpClient with a custom interceptor chain"
1976
2001
  ],
1977
2002
  parameters: [
1978
2003
  {
1979
- name: "config",
1980
- type: "NgOpenapiConfig"
2004
+ name: "baseClient",
2005
+ type: "HttpClient"
2006
+ },
2007
+ {
2008
+ name: "interceptors",
2009
+ type: "HttpInterceptor[]"
1981
2010
  }
1982
2011
  ],
1983
- returnType: "EnvironmentProviders",
1984
- statements: functionBody
2012
+ returnType: "HttpClient",
2013
+ statements: `
2014
+ if (!interceptors.length) {
2015
+ return baseClient;
2016
+ }
2017
+
2018
+ // Create a custom handler that applies interceptors in sequence
2019
+ let handler = baseClient.handler;
2020
+
2021
+ // Apply interceptors in reverse order (last interceptor wraps the original handler)
2022
+ for (let i = interceptors.length - 1; i >= 0; i--) {
2023
+ const currentHandler = handler;
2024
+ const interceptor = interceptors[i];
2025
+
2026
+ handler = {
2027
+ handle: (req: HttpRequest<any>) => interceptor.intercept(req, currentHandler)
2028
+ };
2029
+ }
2030
+
2031
+ // Return a new HttpClient with the custom handler
2032
+ return new (baseClient.constructor as any)(handler);`
1985
2033
  });
1986
2034
  }
1987
- addAsyncProviderFunction(sourceFile) {
2035
+ addClientProviderFunction(sourceFile, pascalClientName, upperCaseClientName) {
1988
2036
  const hasDateInterceptor = this.config.options.dateType === "Date";
1989
2037
  const functionBody = `
1990
- const providers: Provider[] = [];
1991
-
1992
- // Handle async base path
1993
- if (typeof config.basePath === 'string') {
1994
- providers.push({
1995
- provide: BASE_PATH,
2038
+ const providers: Provider[] = [
2039
+ // Base path token
2040
+ {
2041
+ provide: ${upperCaseClientName}_BASE_PATH,
1996
2042
  useValue: config.basePath
1997
- });
1998
- } else {
1999
- providers.push({
2000
- provide: BASE_PATH,
2001
- useFactory: config.basePath
2002
- });
2003
- }
2004
-
2005
- ${hasDateInterceptor ? `// Add date interceptor if enabled (default: true)
2006
- if (config.enableDateTransform !== false) {
2007
- providers.push({
2008
- provide: HTTP_INTERCEPTORS,
2009
- useClass: DateInterceptor,
2010
- multi: true
2011
- });
2012
- }` : `// Date transformation not available (dateType: 'string' was used in generation)`}
2043
+ },
2044
+
2045
+ // HTTP client with custom interceptors
2046
+ {
2047
+ provide: ${upperCaseClientName}_HTTP_CLIENT,
2048
+ useFactory: (baseClient: HttpClient, injector: Injector) => {
2049
+ const interceptorInstances: HttpInterceptor[] = [];
2050
+
2051
+ // Add custom interceptors
2052
+ if (config.interceptors?.length) {
2053
+ config.interceptors.forEach(interceptorClass => {
2054
+ interceptorInstances.push(injector.get(interceptorClass));
2055
+ });
2056
+ }
2057
+
2058
+ ${hasDateInterceptor ? `
2059
+ // Add date interceptor if enabled (default: true)
2060
+ if (config.enableDateTransform !== false) {
2061
+ interceptorInstances.push(injector.get(DateInterceptor));
2062
+ }` : ""}
2063
+
2064
+ return createHttpClientWithInterceptors(baseClient, interceptorInstances);
2065
+ },
2066
+ deps: [HttpClient, Injector]
2067
+ }
2068
+ ];
2013
2069
 
2014
2070
  return makeEnvironmentProviders(providers);`;
2015
2071
  sourceFile.addFunction({
2016
- name: "provideNgOpenapiAsync",
2072
+ name: `provide${pascalClientName}`,
2017
2073
  isExported: true,
2018
2074
  docs: [
2019
- "Alternative function for cases where you need to handle async configuration",
2075
+ `Provides configuration for ${pascalClientName} API client`,
2020
2076
  "",
2021
2077
  "@example",
2022
2078
  "```typescript",
2023
- "// In your app.config.ts",
2024
- "import { provideNgOpenapiAsync } from './api/providers';",
2079
+ `// In your app.config.ts`,
2080
+ `import { provide${pascalClientName} } from './api/providers';`,
2081
+ `import { AuthInterceptor } from './interceptors/auth.interceptor';`,
2025
2082
  "",
2026
2083
  "export const appConfig: ApplicationConfig = {",
2027
2084
  " providers: [",
2028
- " provideNgOpenapiAsync({",
2029
- " basePath: () => import('./config').then(c => c.apiConfig.baseUrl)",
2085
+ ` provide${pascalClientName}({`,
2086
+ " basePath: 'https://api.example.com',",
2087
+ " interceptors: [AuthInterceptor]",
2030
2088
  " }),",
2031
2089
  " // other providers...",
2032
2090
  " ]",
@@ -2036,16 +2094,16 @@ return makeEnvironmentProviders(providers);`;
2036
2094
  parameters: [
2037
2095
  {
2038
2096
  name: "config",
2039
- type: `{
2040
- basePath: string | (() => Promise<string>);
2041
- enableDateTransform?: boolean;
2042
- }`
2097
+ type: `${pascalClientName}Config`
2043
2098
  }
2044
2099
  ],
2045
2100
  returnType: "EnvironmentProviders",
2046
2101
  statements: functionBody
2047
2102
  });
2048
2103
  }
2104
+ pascalCase(str) {
2105
+ return str.replace(/(?:^|[-_])([a-z])/g, (_, char) => char.toUpperCase());
2106
+ }
2049
2107
  };
2050
2108
  __name(_ProviderGenerator, "ProviderGenerator");
2051
2109
  var ProviderGenerator = _ProviderGenerator;
@@ -2078,7 +2136,7 @@ function generateFromConfig(config) {
2078
2136
  typeGenerator.generate();
2079
2137
  console.log(`\u2705 TypeScript interfaces generated`);
2080
2138
  if (generateServices) {
2081
- const tokenGenerator = new TokenGenerator(project);
2139
+ const tokenGenerator = new TokenGenerator(project, config);
2082
2140
  tokenGenerator.generate(outputPath);
2083
2141
  if (config.options.dateType === "Date") {
2084
2142
  const dateTransformer = new DateTransformerGenerator(project);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ng-openapi",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "Generate Angular services and TypeScript types from OpenAPI/Swagger specifications",
5
5
  "keywords": [
6
6
  "angular",