ng-openapi 0.0.22 → 0.0.24-rc.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 (4) hide show
  1. package/cli.cjs +263 -104
  2. package/index.d.ts +1 -0
  3. package/index.js +150 -94
  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,35 @@ 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
+ "InjectionToken"
1841
1899
  ],
1842
1900
  moduleSpecifier: "@angular/core"
1843
1901
  },
1844
1902
  {
1845
1903
  namedImports: [
1904
+ "HttpClient",
1905
+ "HttpInterceptor",
1906
+ "HttpHandler",
1907
+ "HttpBackend",
1846
1908
  "HTTP_INTERCEPTORS"
1847
1909
  ],
1848
1910
  moduleSpecifier: "@angular/common/http"
1849
1911
  },
1850
1912
  {
1851
1913
  namedImports: [
1852
- "BASE_PATH"
1914
+ `${upperCaseClientName}_BASE_PATH`,
1915
+ `${upperCaseClientName}_HTTP_CLIENT`
1853
1916
  ],
1854
1917
  moduleSpecifier: "./tokens"
1855
1918
  }
@@ -1863,10 +1926,10 @@ var ProviderGenerator = class {
1863
1926
  });
1864
1927
  }
1865
1928
  sourceFile.addInterface({
1866
- name: "NgOpenapiConfig",
1929
+ name: `${pascalClientName}Config`,
1867
1930
  isExported: true,
1868
1931
  docs: [
1869
- "Configuration options for ng-openapi providers"
1932
+ `Configuration options for ${clientName} API client`
1870
1933
  ],
1871
1934
  properties: [
1872
1935
  {
@@ -1876,6 +1939,14 @@ var ProviderGenerator = class {
1876
1939
  "Base API URL"
1877
1940
  ]
1878
1941
  },
1942
+ {
1943
+ name: "interceptors",
1944
+ type: "Type<HttpInterceptor>[]",
1945
+ hasQuestionToken: true,
1946
+ docs: [
1947
+ "HTTP interceptors to apply to this client's requests"
1948
+ ]
1949
+ },
1879
1950
  {
1880
1951
  name: "enableDateTransform",
1881
1952
  type: "boolean",
@@ -1886,46 +1957,99 @@ var ProviderGenerator = class {
1886
1957
  }
1887
1958
  ]
1888
1959
  });
1889
- this.addMainProviderFunction(sourceFile);
1890
- this.addAsyncProviderFunction(sourceFile);
1960
+ sourceFile.addVariableStatement({
1961
+ isExported: true,
1962
+ declarationKind: "const",
1963
+ declarations: [
1964
+ {
1965
+ name: `${upperCaseClientName}_INTERCEPTORS`,
1966
+ initializer: `new InjectionToken<HttpInterceptor[]>('${upperCaseClientName}_INTERCEPTORS')`
1967
+ }
1968
+ ],
1969
+ leadingTrivia: `/**
1970
+ * Interceptor token for ${clientName} client
1971
+ */
1972
+ `
1973
+ });
1974
+ this.addSimpleProviderFunction(sourceFile, pascalClientName, upperCaseClientName);
1891
1975
  sourceFile.saveSync();
1892
1976
  }
1893
- addMainProviderFunction(sourceFile) {
1977
+ addSimpleProviderFunction(sourceFile, pascalClientName, upperCaseClientName) {
1894
1978
  const hasDateInterceptor = this.config.options.dateType === "Date";
1895
1979
  const functionBody = `
1896
1980
  const providers: Provider[] = [
1897
1981
  // Base path token
1898
1982
  {
1899
- provide: BASE_PATH,
1983
+ provide: ${upperCaseClientName}_BASE_PATH,
1900
1984
  useValue: config.basePath
1985
+ },
1986
+
1987
+ // Collect interceptors for this client
1988
+ {
1989
+ provide: ${upperCaseClientName}_INTERCEPTORS,
1990
+ useFactory: (injector: Injector) => {
1991
+ const interceptorInstances: HttpInterceptor[] = [];
1992
+
1993
+ // Add custom interceptors
1994
+ if (config.interceptors?.length) {
1995
+ config.interceptors.forEach(interceptorClass => {
1996
+ interceptorInstances.push(injector.get(interceptorClass));
1997
+ });
1998
+ }
1999
+
2000
+ ${hasDateInterceptor ? `
2001
+ // Add date interceptor if enabled (default: true)
2002
+ if (config.enableDateTransform !== false) {
2003
+ interceptorInstances.push(injector.get(DateInterceptor));
2004
+ }` : ""}
2005
+
2006
+ return interceptorInstances;
2007
+ },
2008
+ deps: [Injector]
2009
+ },
2010
+
2011
+ // Create HTTP client with interceptors
2012
+ {
2013
+ provide: ${upperCaseClientName}_HTTP_CLIENT,
2014
+ useFactory: (backend: HttpBackend, interceptors: HttpInterceptor[]) => {
2015
+ if (!interceptors.length) {
2016
+ return new HttpClient(backend);
2017
+ }
2018
+
2019
+ // Create handler chain
2020
+ let handler = backend;
2021
+ for (let i = interceptors.length - 1; i >= 0; i--) {
2022
+ const interceptor = interceptors[i];
2023
+ const currentHandler = handler;
2024
+ handler = {
2025
+ handle: req => interceptor.intercept(req, currentHandler)
2026
+ };
2027
+ }
2028
+
2029
+ return new HttpClient(handler);
2030
+ },
2031
+ deps: [HttpBackend, ${upperCaseClientName}_INTERCEPTORS]
1901
2032
  }
1902
2033
  ];
1903
2034
 
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
2035
  return makeEnvironmentProviders(providers);`;
1914
2036
  sourceFile.addFunction({
1915
- name: "provideNgOpenapi",
2037
+ name: `provide${pascalClientName}`,
1916
2038
  isExported: true,
1917
2039
  docs: [
1918
- "Provides all necessary configuration for ng-openapi generated services",
2040
+ `Provides configuration for ${pascalClientName} API client`,
1919
2041
  "",
1920
2042
  "@example",
1921
2043
  "```typescript",
1922
- "// In your app.config.ts",
1923
- "import { provideNgOpenapi } from './api/providers';",
2044
+ `// In your app.config.ts`,
2045
+ `import { provide${pascalClientName} } from './api/providers';`,
2046
+ `import { AuthInterceptor } from './interceptors/auth.interceptor';`,
1924
2047
  "",
1925
2048
  "export const appConfig: ApplicationConfig = {",
1926
2049
  " providers: [",
1927
- " provideNgOpenapi({",
1928
- " basePath: 'https://api.example.com'",
2050
+ ` provide${pascalClientName}({`,
2051
+ " basePath: 'https://api.example.com',",
2052
+ " interceptors: [AuthInterceptor]",
1929
2053
  " }),",
1930
2054
  " // other providers...",
1931
2055
  " ]",
@@ -1935,74 +2059,15 @@ return makeEnvironmentProviders(providers);`;
1935
2059
  parameters: [
1936
2060
  {
1937
2061
  name: "config",
1938
- type: "NgOpenapiConfig"
2062
+ type: `${pascalClientName}Config`
1939
2063
  }
1940
2064
  ],
1941
2065
  returnType: "EnvironmentProviders",
1942
2066
  statements: functionBody
1943
2067
  });
1944
2068
  }
1945
- addAsyncProviderFunction(sourceFile) {
1946
- const hasDateInterceptor = this.config.options.dateType === "Date";
1947
- 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,
1954
- 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)`}
1971
-
1972
- return makeEnvironmentProviders(providers);`;
1973
- sourceFile.addFunction({
1974
- name: "provideNgOpenapiAsync",
1975
- isExported: true,
1976
- docs: [
1977
- "Alternative function for cases where you need to handle async configuration",
1978
- "",
1979
- "@example",
1980
- "```typescript",
1981
- "// In your app.config.ts",
1982
- "import { provideNgOpenapiAsync } from './api/providers';",
1983
- "",
1984
- "export const appConfig: ApplicationConfig = {",
1985
- " providers: [",
1986
- " provideNgOpenapiAsync({",
1987
- " basePath: () => import('./config').then(c => c.apiConfig.baseUrl)",
1988
- " }),",
1989
- " // other providers...",
1990
- " ]",
1991
- "};",
1992
- "```"
1993
- ],
1994
- parameters: [
1995
- {
1996
- name: "config",
1997
- type: `{
1998
- basePath: string | (() => Promise<string>);
1999
- enableDateTransform?: boolean;
2000
- }`
2001
- }
2002
- ],
2003
- returnType: "EnvironmentProviders",
2004
- statements: functionBody
2005
- });
2069
+ pascalCase(str) {
2070
+ return str.replace(/(?:^|[-_])([a-z])/g, (_, char) => char.toUpperCase());
2006
2071
  }
2007
2072
  };
2008
2073
 
@@ -2033,7 +2098,7 @@ async function generateFromConfig(config) {
2033
2098
  typeGenerator.generate();
2034
2099
  console.log(`\u2705 TypeScript interfaces generated`);
2035
2100
  if (generateServices) {
2036
- const tokenGenerator = new TokenGenerator(project);
2101
+ const tokenGenerator = new TokenGenerator(project, config);
2037
2102
  tokenGenerator.generate(outputPath);
2038
2103
  if (config.options.dateType === "Date") {
2039
2104
  const dateTransformer = new DateTransformerGenerator(project);
@@ -2066,6 +2131,94 @@ async function generateFromConfig(config) {
2066
2131
  }
2067
2132
  __name(generateFromConfig, "generateFromConfig");
2068
2133
 
2134
+ // package.json
2135
+ var package_default = {
2136
+ name: "ng-openapi",
2137
+ version: "0.0.23",
2138
+ description: "Generate Angular services and TypeScript types from OpenAPI/Swagger specifications",
2139
+ keywords: [
2140
+ "angular",
2141
+ "openapi",
2142
+ "swagger",
2143
+ "codegen",
2144
+ "typescript",
2145
+ "generator",
2146
+ "code-generator",
2147
+ "api",
2148
+ "rest",
2149
+ "http",
2150
+ "cli"
2151
+ ],
2152
+ author: {
2153
+ name: "Tareq Jami",
2154
+ email: "info@jami-it.de",
2155
+ url: "http://tareqjami.de"
2156
+ },
2157
+ license: "MIT",
2158
+ homepage: "https://ng-openapi.dev/",
2159
+ bugs: {
2160
+ url: "https://github.com/ng-openapi/ng-openapi/issues"
2161
+ },
2162
+ repository: {
2163
+ type: "git",
2164
+ url: "git+https://github.com/ng-openapi/ng-openapi.git",
2165
+ directory: "packages/ng-openapi"
2166
+ },
2167
+ funding: {
2168
+ type: "github",
2169
+ url: "https://github.com/sponsors/ng-openapi"
2170
+ },
2171
+ main: "./index.cjs",
2172
+ module: "./index.js",
2173
+ types: "./index.d.ts",
2174
+ bin: {
2175
+ "ng-openapi": "./cli.cjs"
2176
+ },
2177
+ files: [
2178
+ "index.js",
2179
+ "index.cjs",
2180
+ "index.d.ts",
2181
+ "cli.cjs",
2182
+ "lib/**/*.js",
2183
+ "lib/**/*.cjs",
2184
+ "lib/**/*.d.ts",
2185
+ "README.md",
2186
+ "LICENSE",
2187
+ "CHANGELOG.md"
2188
+ ],
2189
+ scripts: {
2190
+ prepublishOnly: "echo 'Build the package using: npm run build:ng-openapi from workspace root'",
2191
+ build: "tsup"
2192
+ },
2193
+ dependencies: {
2194
+ commander: "^14.0.0",
2195
+ "ts-morph": "^26.0.0",
2196
+ "ts-node": "^10.9.2",
2197
+ typescript: "^5.8.3",
2198
+ "@types/swagger-schema-official": "^2.0.25"
2199
+ },
2200
+ peerDependencies: {
2201
+ "@angular/core": ">=15",
2202
+ "@angular/common": ">=15"
2203
+ },
2204
+ peerDependenciesMeta: {
2205
+ "@angular/core": {
2206
+ optional: false
2207
+ },
2208
+ "@angular/common": {
2209
+ optional: false
2210
+ }
2211
+ },
2212
+ engines: {
2213
+ node: ">=18.0.0",
2214
+ npm: ">=8.0.0"
2215
+ },
2216
+ publishConfig: {
2217
+ access: "public",
2218
+ registry: "https://registry.npmjs.org/"
2219
+ }
2220
+ };
2221
+
2069
2222
  // src/lib/cli.ts
2070
2223
  var program = new import_commander.Command();
2071
2224
  async function loadConfigFile(configPath) {
@@ -2092,17 +2245,19 @@ __name(loadConfigFile, "loadConfigFile");
2092
2245
  async function generateFromOptions(options) {
2093
2246
  try {
2094
2247
  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);
2248
+ const configPaths = Array.isArray(options.config) ? options.config : [
2249
+ options.config
2250
+ ];
2251
+ for (const configPath of configPaths) {
2252
+ const config = await loadConfigFile(configPath);
2253
+ await generateFromConfig(config);
2254
+ console.log(`\u2728 Generated client: ${config.clientName || "default"}`);
2102
2255
  }
2256
+ } else if (options.input) {
2103
2257
  const config = {
2104
- input: inputPath,
2258
+ input: path8.resolve(options.input),
2105
2259
  output: options.output || "./src/generated",
2260
+ clientName: options.clientName,
2106
2261
  options: {
2107
2262
  dateType: options.dateType || "Date",
2108
2263
  enumStyle: "enum",
@@ -2116,14 +2271,14 @@ async function generateFromOptions(options) {
2116
2271
  program.help();
2117
2272
  process.exit(1);
2118
2273
  }
2119
- console.log("\u2728 Generation completed successfully!");
2274
+ console.log("\u2728 All clients generated successfully!");
2120
2275
  } catch (error) {
2121
2276
  console.error("\u274C Generation failed:", error instanceof Error ? error.message : error);
2122
2277
  process.exit(1);
2123
2278
  }
2124
2279
  }
2125
2280
  __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) => {
2281
+ 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
2282
  await generateFromOptions(options);
2128
2283
  });
2129
2284
  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 +2293,8 @@ program.on("--help", () => {
2138
2293
  console.log(" $ ng-openapi generate -i ./api.yaml --types-only");
2139
2294
  });
2140
2295
  program.parse();
2296
+ // Annotate the CommonJS export names for ESM import in node:
2297
+ 0 && (module.exports = {
2298
+ generateFromOptions
2299
+ });
2141
2300
  //# 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,35 @@ 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
+ "InjectionToken"
1883
1932
  ],
1884
1933
  moduleSpecifier: "@angular/core"
1885
1934
  },
1886
1935
  {
1887
1936
  namedImports: [
1937
+ "HttpClient",
1938
+ "HttpInterceptor",
1939
+ "HttpHandler",
1940
+ "HttpBackend",
1888
1941
  "HTTP_INTERCEPTORS"
1889
1942
  ],
1890
1943
  moduleSpecifier: "@angular/common/http"
1891
1944
  },
1892
1945
  {
1893
1946
  namedImports: [
1894
- "BASE_PATH"
1947
+ `${upperCaseClientName}_BASE_PATH`,
1948
+ `${upperCaseClientName}_HTTP_CLIENT`
1895
1949
  ],
1896
1950
  moduleSpecifier: "./tokens"
1897
1951
  }
@@ -1905,10 +1959,10 @@ var _ProviderGenerator = class _ProviderGenerator {
1905
1959
  });
1906
1960
  }
1907
1961
  sourceFile.addInterface({
1908
- name: "NgOpenapiConfig",
1962
+ name: `${pascalClientName}Config`,
1909
1963
  isExported: true,
1910
1964
  docs: [
1911
- "Configuration options for ng-openapi providers"
1965
+ `Configuration options for ${clientName} API client`
1912
1966
  ],
1913
1967
  properties: [
1914
1968
  {
@@ -1918,6 +1972,14 @@ var _ProviderGenerator = class _ProviderGenerator {
1918
1972
  "Base API URL"
1919
1973
  ]
1920
1974
  },
1975
+ {
1976
+ name: "interceptors",
1977
+ type: "Type<HttpInterceptor>[]",
1978
+ hasQuestionToken: true,
1979
+ docs: [
1980
+ "HTTP interceptors to apply to this client's requests"
1981
+ ]
1982
+ },
1921
1983
  {
1922
1984
  name: "enableDateTransform",
1923
1985
  type: "boolean",
@@ -1928,46 +1990,99 @@ var _ProviderGenerator = class _ProviderGenerator {
1928
1990
  }
1929
1991
  ]
1930
1992
  });
1931
- this.addMainProviderFunction(sourceFile);
1932
- this.addAsyncProviderFunction(sourceFile);
1993
+ sourceFile.addVariableStatement({
1994
+ isExported: true,
1995
+ declarationKind: "const",
1996
+ declarations: [
1997
+ {
1998
+ name: `${upperCaseClientName}_INTERCEPTORS`,
1999
+ initializer: `new InjectionToken<HttpInterceptor[]>('${upperCaseClientName}_INTERCEPTORS')`
2000
+ }
2001
+ ],
2002
+ leadingTrivia: `/**
2003
+ * Interceptor token for ${clientName} client
2004
+ */
2005
+ `
2006
+ });
2007
+ this.addSimpleProviderFunction(sourceFile, pascalClientName, upperCaseClientName);
1933
2008
  sourceFile.saveSync();
1934
2009
  }
1935
- addMainProviderFunction(sourceFile) {
2010
+ addSimpleProviderFunction(sourceFile, pascalClientName, upperCaseClientName) {
1936
2011
  const hasDateInterceptor = this.config.options.dateType === "Date";
1937
2012
  const functionBody = `
1938
2013
  const providers: Provider[] = [
1939
2014
  // Base path token
1940
2015
  {
1941
- provide: BASE_PATH,
2016
+ provide: ${upperCaseClientName}_BASE_PATH,
1942
2017
  useValue: config.basePath
2018
+ },
2019
+
2020
+ // Collect interceptors for this client
2021
+ {
2022
+ provide: ${upperCaseClientName}_INTERCEPTORS,
2023
+ useFactory: (injector: Injector) => {
2024
+ const interceptorInstances: HttpInterceptor[] = [];
2025
+
2026
+ // Add custom interceptors
2027
+ if (config.interceptors?.length) {
2028
+ config.interceptors.forEach(interceptorClass => {
2029
+ interceptorInstances.push(injector.get(interceptorClass));
2030
+ });
2031
+ }
2032
+
2033
+ ${hasDateInterceptor ? `
2034
+ // Add date interceptor if enabled (default: true)
2035
+ if (config.enableDateTransform !== false) {
2036
+ interceptorInstances.push(injector.get(DateInterceptor));
2037
+ }` : ""}
2038
+
2039
+ return interceptorInstances;
2040
+ },
2041
+ deps: [Injector]
2042
+ },
2043
+
2044
+ // Create HTTP client with interceptors
2045
+ {
2046
+ provide: ${upperCaseClientName}_HTTP_CLIENT,
2047
+ useFactory: (backend: HttpBackend, interceptors: HttpInterceptor[]) => {
2048
+ if (!interceptors.length) {
2049
+ return new HttpClient(backend);
2050
+ }
2051
+
2052
+ // Create handler chain
2053
+ let handler = backend;
2054
+ for (let i = interceptors.length - 1; i >= 0; i--) {
2055
+ const interceptor = interceptors[i];
2056
+ const currentHandler = handler;
2057
+ handler = {
2058
+ handle: req => interceptor.intercept(req, currentHandler)
2059
+ };
2060
+ }
2061
+
2062
+ return new HttpClient(handler);
2063
+ },
2064
+ deps: [HttpBackend, ${upperCaseClientName}_INTERCEPTORS]
1943
2065
  }
1944
2066
  ];
1945
2067
 
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
2068
  return makeEnvironmentProviders(providers);`;
1956
2069
  sourceFile.addFunction({
1957
- name: "provideNgOpenapi",
2070
+ name: `provide${pascalClientName}`,
1958
2071
  isExported: true,
1959
2072
  docs: [
1960
- "Provides all necessary configuration for ng-openapi generated services",
2073
+ `Provides configuration for ${pascalClientName} API client`,
1961
2074
  "",
1962
2075
  "@example",
1963
2076
  "```typescript",
1964
- "// In your app.config.ts",
1965
- "import { provideNgOpenapi } from './api/providers';",
2077
+ `// In your app.config.ts`,
2078
+ `import { provide${pascalClientName} } from './api/providers';`,
2079
+ `import { AuthInterceptor } from './interceptors/auth.interceptor';`,
1966
2080
  "",
1967
2081
  "export const appConfig: ApplicationConfig = {",
1968
2082
  " providers: [",
1969
- " provideNgOpenapi({",
1970
- " basePath: 'https://api.example.com'",
2083
+ ` provide${pascalClientName}({`,
2084
+ " basePath: 'https://api.example.com',",
2085
+ " interceptors: [AuthInterceptor]",
1971
2086
  " }),",
1972
2087
  " // other providers...",
1973
2088
  " ]",
@@ -1977,74 +2092,15 @@ return makeEnvironmentProviders(providers);`;
1977
2092
  parameters: [
1978
2093
  {
1979
2094
  name: "config",
1980
- type: "NgOpenapiConfig"
2095
+ type: `${pascalClientName}Config`
1981
2096
  }
1982
2097
  ],
1983
2098
  returnType: "EnvironmentProviders",
1984
2099
  statements: functionBody
1985
2100
  });
1986
2101
  }
1987
- addAsyncProviderFunction(sourceFile) {
1988
- const hasDateInterceptor = this.config.options.dateType === "Date";
1989
- 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,
1996
- 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)`}
2013
-
2014
- return makeEnvironmentProviders(providers);`;
2015
- sourceFile.addFunction({
2016
- name: "provideNgOpenapiAsync",
2017
- isExported: true,
2018
- docs: [
2019
- "Alternative function for cases where you need to handle async configuration",
2020
- "",
2021
- "@example",
2022
- "```typescript",
2023
- "// In your app.config.ts",
2024
- "import { provideNgOpenapiAsync } from './api/providers';",
2025
- "",
2026
- "export const appConfig: ApplicationConfig = {",
2027
- " providers: [",
2028
- " provideNgOpenapiAsync({",
2029
- " basePath: () => import('./config').then(c => c.apiConfig.baseUrl)",
2030
- " }),",
2031
- " // other providers...",
2032
- " ]",
2033
- "};",
2034
- "```"
2035
- ],
2036
- parameters: [
2037
- {
2038
- name: "config",
2039
- type: `{
2040
- basePath: string | (() => Promise<string>);
2041
- enableDateTransform?: boolean;
2042
- }`
2043
- }
2044
- ],
2045
- returnType: "EnvironmentProviders",
2046
- statements: functionBody
2047
- });
2102
+ pascalCase(str) {
2103
+ return str.replace(/(?:^|[-_])([a-z])/g, (_, char) => char.toUpperCase());
2048
2104
  }
2049
2105
  };
2050
2106
  __name(_ProviderGenerator, "ProviderGenerator");
@@ -2078,7 +2134,7 @@ function generateFromConfig(config) {
2078
2134
  typeGenerator.generate();
2079
2135
  console.log(`\u2705 TypeScript interfaces generated`);
2080
2136
  if (generateServices) {
2081
- const tokenGenerator = new TokenGenerator(project);
2137
+ const tokenGenerator = new TokenGenerator(project, config);
2082
2138
  tokenGenerator.generate(outputPath);
2083
2139
  if (config.options.dateType === "Date") {
2084
2140
  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.24-rc.0",
4
4
  "description": "Generate Angular services and TypeScript types from OpenAPI/Swagger specifications",
5
5
  "keywords": [
6
6
  "angular",