swaggie 1.9.0-alpha.2 → 1.9.0-alpha.5

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.
@@ -58,8 +58,9 @@ function renderSchema(
58
58
  if ('x-ts-type' in schema) {
59
59
  const result = [];
60
60
  const s = schema ;
61
- if (_nullishCoalesce(s.description, () => ( s.title))) {
62
- result.push(_jsDocs.renderComment.call(void 0, _nullishCoalesce(s.description, () => ( s.title))));
61
+ const xTsComment = _jsDocs.buildSchemaComment.call(void 0, s);
62
+ if (xTsComment) {
63
+ result.push(xTsComment);
63
64
  }
64
65
  result.push(`export type ${safeName} = ${schema['x-ts-type']};`);
65
66
  return result.join('\n');
@@ -74,8 +75,9 @@ function renderSchema(
74
75
 
75
76
  const result = [];
76
77
  const schemaContext = `components.schemas.${safeName}`;
77
- if (_nullishCoalesce(schema.description, () => ( schema.title))) {
78
- result.push(_jsDocs.renderComment.call(void 0, _nullishCoalesce(schema.description, () => ( schema.title))));
78
+ const schemaComment = _jsDocs.buildSchemaComment.call(void 0, schema);
79
+ if (schemaComment) {
80
+ result.push(schemaComment);
79
81
  }
80
82
 
81
83
  if ('x-enumNames' in schema || 'x-enum-varnames' in schema) {
@@ -138,7 +140,10 @@ function renderSchema(
138
140
  return result.join('\n');
139
141
  } else {
140
142
  const objectType = _swagger.getTypeFromSchema.call(void 0, schema, options, schemaContext);
141
- const hasAdditionalProperties = !!schema.additionalProperties;
143
+ // A bare `type: object` with no properties is a free-form object per the OpenAPI spec
144
+ const isFreeFormObject =
145
+ schema.type === 'object' && !schema.properties && !schema.additionalProperties;
146
+ const hasAdditionalProperties = !!schema.additionalProperties || isFreeFormObject;
142
147
 
143
148
  const objectContents = generateObjectTypeContents(schema, options, schemaContext);
144
149
  if (hasAdditionalProperties) {
@@ -288,8 +293,13 @@ function renderTypeProp(
288
293
  const lines = [];
289
294
  const type = _swagger.getTypeFromSchema.call(void 0, definition, options, `${schemaContext}.properties.${propName}`);
290
295
 
291
- if ('description' in definition || 'title' in definition) {
292
- const renderedComment = _jsDocs.renderComment.call(void 0, _nullishCoalesce(definition.description, () => ( definition.title)));
296
+ if (
297
+ 'description' in definition ||
298
+ 'title' in definition ||
299
+ 'format' in definition ||
300
+ 'default' in definition
301
+ ) {
302
+ const renderedComment = _jsDocs.buildSchemaComment.call(void 0, definition );
293
303
  if (renderedComment) {
294
304
  lines.push(indentComment(renderedComment, ' '));
295
305
  }
@@ -3,6 +3,7 @@
3
3
  * regardless of generation mode.
4
4
  */
5
5
  const FILE_HEADER =
6
+ '/* istanbul ignore file -- auto-generated code */\n' +
6
7
  '/* tslint:disable */\n' +
7
8
  '/* eslint-disable */\n' +
8
9
  '//----------------------\n' +
@@ -12,5 +13,5 @@
12
13
  '// </auto-generated>\n' +
13
14
  '//----------------------\n' +
14
15
  '// ReSharper disable InconsistentNaming\n' +
15
- '// deno-lint-ignore-file\n' +
16
- '// biome-ignore-all lint: auto-generated code\n\n'; exports.FILE_HEADER = FILE_HEADER;
16
+ '// biome-ignore-all lint: auto-generated code\n' +
17
+ '// deno-lint-ignore-file\n\n'; exports.FILE_HEADER = FILE_HEADER;
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
3
 
4
4
  /**
@@ -80,6 +80,40 @@ function stripSpecificHtmlTags(str) {
80
80
  return renderComment(result.join('\n'));
81
81
  } exports.prepareJsDocsForOperation = prepareJsDocsForOperation;
82
82
 
83
+ /**
84
+ * Builds a JSDoc comment string from schema metadata, including optional
85
+ * `@default` and `@format` tags when those fields are present on the schema.
86
+ */
87
+ function buildSchemaComment(schema
88
+
89
+
90
+
91
+
92
+ ) {
93
+ const lines = [];
94
+
95
+ const text = _nullishCoalesce(schema.description, () => ( schema.title));
96
+ if (text) {
97
+ lines.push(text);
98
+ }
99
+
100
+ if (schema.default !== undefined) {
101
+ const val =
102
+ typeof schema.default === 'string'
103
+ ? `"${schema.default}"`
104
+ : typeof schema.default === 'object'
105
+ ? JSON.stringify(schema.default)
106
+ : String(schema.default);
107
+ lines.push(`@default ${val}`);
108
+ }
109
+
110
+ if (schema.format !== undefined) {
111
+ lines.push(`@format ${schema.format}`);
112
+ }
113
+
114
+ return renderComment(lines.join('\n'));
115
+ } exports.buildSchemaComment = buildSchemaComment;
116
+
83
117
  function renderComment(comment) {
84
118
  if (!comment) {
85
119
  return '';
@@ -28,15 +28,16 @@
28
28
  },
29
29
  "swr-axios": {
30
30
  "barrel.ejs": "\n/**\n * Serializes a params object into a query string that is compatible with different REST APIs.\n * Implementation from: https://github.com/suhaotian/xior/blob/main/src/utils.ts\n * Kudos to @suhaotian for the original implementation\n */\nfunction encodeParams<T = any>(\n params: T,\n parentKey: string | null = null,\n options?: {\n allowDots?: boolean;\n serializeDate?: (value: Date) => string;\n arrayFormat?: 'indices' | 'repeat' | 'brackets';\n }\n): string {\n if (params === undefined || params === null) return '';\n const encodedParams: string[] = [];\n const paramsIsArray = Array.isArray(params);\n const { arrayFormat, allowDots, serializeDate } = options || {};\n\n const getKey = (key: string) => {\n if (allowDots && !paramsIsArray) return `.${key}`;\n if (paramsIsArray) {\n if (arrayFormat === 'brackets') {\n return '[]';\n }\n if (arrayFormat === 'repeat') {\n return '';\n }\n }\n return `[${key}]`;\n };\n\n for (const key in params) {\n if (Object.prototype.hasOwnProperty.call(params, key)) {\n let value = (params as any)[key];\n if (value !== undefined) {\n const encodedKey = parentKey ? `${parentKey}${getKey(key)}` : (key as string);\n\n // biome-ignore lint/suspicious/noGlobalIsNan: <explanation>\n if (!isNaN(value) && value instanceof Date) {\n value = serializeDate ? serializeDate(value) : value.toISOString();\n }\n if (typeof value === 'object') {\n // If the value is an object or array, recursively encode its contents\n const result = encodeParams(value, encodedKey, options);\n if (result !== '') encodedParams.push(result);\n } else {\n // Otherwise, encode the key-value pair\n encodedParams.push(`${encodeURIComponent(encodedKey)}=${encodeURIComponent(value)}`);\n }\n }\n }\n }\n\n return encodedParams.join('&');\n}\n\n",
31
- "baseClient.ejs": "import Axios, { type AxiosPromise, type AxiosRequestConfig } from \"axios\";\nimport useSWR, { type SWRConfiguration, type Key } from 'swr';\n\nexport const axios = Axios.create({\n baseURL: '<%= it.baseUrl || '' -%>',\n paramsSerializer: (params: any) =>\n encodeParams(params, null, {\n allowDots: <%= it.allowDots %>,\n arrayFormat: '<%= it.arrayFormat %>',\n }),\n});\n\ninterface SwrConfig extends SWRConfiguration {\n /* Custom key for SWR. You don't have to worry about this as by default it's the URL. You can use standard SWR Key here if you need more flexibility. */\n key?: Key;\n\n /* Configuration for axios fetcher */\n axios?: AxiosRequestConfig;\n}\n\n",
32
- "client.ejs": "export const <%= it.camelCaseName -%>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n <% }); %>\n};\n\n<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\n\nfunction toOpName(name) {\n var n = name.toLowerCase().startsWith('get') ? name.substring(3) : name;\n return n.charAt(0).toUpperCase() + n.slice(1);\n}\n%>\n\nexport const <%= it.camelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = toOpName(operation.name);\n var swrOperation = Object.assign({\n swrOpName: 'use' + opName,\n clientName: it.camelCaseName,\n }, operation);\n%>\n<%~ include('swrOperation.ejs', swrOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n%>\n <%= keyName %>: (<% operation.parameters.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? ' | null' : '' %>, <% }); %>) => `<%= operation.url %><% if(operation.query && operation.query.length > 0) { %>?${encodeParams({<% operation.query.forEach((parameter) => { %>'<%= parameter.originalName %>': <%= parameter.name %>, <% }); %>})}<% } %>`,\n<% }); %>\n },\n};\n",
31
+ "baseClient.ejs": "import Axios, { type AxiosPromise, type AxiosRequestConfig } from \"axios\";\nimport useSWR, { type SWRConfiguration, type Key } from 'swr';\nimport useSWRMutation, { type SWRMutationConfiguration } from 'swr/mutation';\n\nexport const axios = Axios.create({\n baseURL: '<%= it.baseUrl || '' -%>',\n paramsSerializer: (params: any) =>\n encodeParams(params, null, {\n allowDots: <%= it.allowDots %>,\n arrayFormat: '<%= it.arrayFormat %>',\n }),\n});\n\ninterface SwrConfig extends SWRConfiguration {\n /* Custom key for SWR. You don't have to worry about this as by default it's the URL. You can use standard SWR Key here if you need more flexibility. */\n key?: Key;\n}\n\n",
32
+ "client.ejs": "export const <%= it.camelCaseName -%>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n <% }); %>\n};\n\n<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\n\nfunction toOpName(name) {\n var n = name.toLowerCase().startsWith('get') ? name.substring(3) : name;\n return n.charAt(0).toUpperCase() + n.slice(1);\n}\n\nfunction safeOperation(operation, clientName) {\n var safeParams = operation.parameters.map(function(p) {\n return p.name === clientName ? Object.assign({}, p, { name: '_' + p.name }) : p;\n });\n return Object.assign({}, operation, { parameters: safeParams });\n}\n%>\n\nexport const <%= it.camelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = toOpName(operation.name);\n var swrOperation = Object.assign({\n swrOpName: 'use' + opName,\n clientName: it.camelCaseName,\n }, safeOperation(operation, it.camelCaseName));\n%>\n<%~ include('swrOperation.ejs', swrOperation); %>\n\n<% }); %>\n },\n\n mutations: {\n<% mutationOperations.forEach((operation) => {\n var opName = operation.name.charAt(0).toUpperCase() + operation.name.slice(1);\n var swrMutationOperation = Object.assign({\n mutOpName: 'use' + opName,\n clientName: it.camelCaseName,\n }, safeOperation(operation, it.camelCaseName));\n%>\n<%~ include('swrMutationOperation.ejs', swrMutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n var safeOp = safeOperation(operation, it.camelCaseName);\n%>\n <%= keyName %>: (<% safeOp.parameters.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>, <% }); %>) => `<%= operation.url %><% if(safeOp.query && safeOp.query.length > 0) { %>?${encodeParams({<% safeOp.query.forEach((parameter) => { %>'<%= parameter.originalName %>': <%= parameter.name %>, <% }); %>})}<% } %>`,\n<% }); %>\n },\n};\n",
33
33
  "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n $config?: AxiosRequestConfig\n ): AxiosPromise<<%~ it.returnType %>> {\n const url = `<%= it.url %>`;\n\n return axios.request<<%~ it.returnType %>>({\n url: url,\n method: '<%= it.method %>',\n<% if(it.body) { %>\n<% if(it.body.contentType === 'urlencoded') { %>\n data: new URLSearchParams(<%= it.body.name %> as any),\n<% } else { %>\n data: <%= it.body.name %>,\n<% } %>\n<% } %>\n<% if(it.query && it.query.length > 0) { %>\n params: {\n <% it.query.forEach((parameter) => { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% }); %>\n },\n<% } %>\n<% if(it.headers && it.headers.length > 0) { %>\n headers: {\n <% it.headers.forEach((parameter) => { %>\n <% if (parameter.value) { %>\n '<%= parameter.originalName %>': '<%= parameter.value %>',\n <% } else { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% } %>\n <% }); %>\n},\n<% } %>\n ...$config,\n });\n },\n",
34
- "swrOperation.ejs": "<% var docs = it.jsDocs ? it.jsDocs.replace(/^/gm, ' ') + '\\n' : ''; %>\n<%~ docs %>\n <%= it.swrOpName %>(\n<% it.parameters.forEach((parameter) => { %> <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>,\n<% }); %> $config?: SwrConfig\n ) {\n const url = `<%= it.url %>`;\n const { axios: $axiosConf, key, ...config } = $config || {};\n\n<% if(it.query && it.query.length > 0) { %>\n const cacheKey = encodeParams({<% it.query.forEach((parameter) => { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,<% }); %>\n });\n const cacheUrl = cacheKey ? `${url}?${cacheKey}` : url;\n<% } else { %>\n const cacheUrl = url;\n<% } %>\n\n const { data, error, isLoading, mutate } = useSWR<<%~ it.returnType %>>(\n key ?? cacheUrl,\n () => axios.request({\n url: url,\n method: '<%= it.method %>',\n<% if(it.query && it.query.length > 0) { %>\n params: {\n<% it.query.forEach((parameter) => { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n<% }); %>\n },\n<% } %>\n<% if(it.headers && it.headers.length > 0) { %>\n headers: {\n<% it.headers.forEach((parameter) => { %>\n<% if (parameter.value) { %>\n '<%= parameter.originalName %>': '<%= parameter.value %>',\n<% } else { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n<% } %>\n<% }); %>\n },\n<% } %>\n ...$axiosConf,\n }).then((resp) => resp.data),\n config\n );\n\n return { data, isLoading, error, mutate };\n },\n"
34
+ "swrMutationOperation.ejs": "<%\nvar hasParams = it.parameters.length > 0;\n\nvar variablesType;\nif (!hasParams) {\n variablesType = 'void';\n} else {\n var parts = it.parameters.map(function(p) {\n return p.name + (p.skippable ? '?' : '') + ': ' + p.type + (p.optional ? ' | null' : '');\n });\n variablesType = '{ ' + parts.join('; ') + ' }';\n}\n\nvar callArgs = it.parameters.map(function(p) { return 'arg.' + p.name; });\n\n// Stable SWR key: replace all ${...} path expressions with *\nvar swrKey = it.url.replace(/\\$\\{(?:[^{}]|\\{[^{}]*\\})*\\}/g, '*');\n\nvar docs = it.jsDocs ? it.jsDocs.replace(/^/gm, ' ') + '\\n' : '';\n%>\n<%~ docs %>\n <%= it.mutOpName %>(\n $config?: SWRMutationConfiguration<<%~ it.returnType %>, Error, string, <%~ variablesType %>>,\n $httpConfig?: AxiosRequestConfig\n ) {\n return useSWRMutation<<%~ it.returnType %>, Error, string, <%~ variablesType %>>(\n '<%= swrKey %>',\n (_key: string, { arg }<%= hasParams ? ': { arg: ' + variablesType + ' }' : '' %>) =>\n <%= it.clientName %>Client.<%= it.name %>(<%= callArgs.join(', ') %><%= callArgs.length > 0 ? ', ' : '' %>$httpConfig).then((resp) => resp.data),\n $config\n );\n },\n",
35
+ "swrOperation.ejs": "<% var docs = it.jsDocs ? it.jsDocs.replace(/^/gm, ' ') + '\\n' : ''; %>\n<%~ docs %>\n <%= it.swrOpName %>(\n<% it.parameters.forEach((parameter) => { %> <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>,\n<% }); %> $config?: Omit<SwrConfig, 'key'> & { key?: Key },\n $httpConfig?: AxiosRequestConfig\n ) {\n const { key, ...config } = $config || {};\n const cacheUrl = key ?? <%= it.clientName %>.queryKeys.<%= it.swrOpName.charAt(3).toLowerCase() + it.swrOpName.slice(4) %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>);\n\n const { data, error, isLoading, mutate } = useSWR<<%~ it.returnType %>>(\n cacheUrl,\n () => <%= it.clientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>$httpConfig).then((resp) => resp.data),\n config\n );\n\n return { data, isLoading, error, mutate };\n },\n"
35
36
  },
36
37
  "tsq-xior": {
37
38
  "barrel.ejs": "",
38
39
  "baseClient.ejs": "import xior, { type XiorResponse, type XiorRequestConfig, encodeParams } from \"xior\";\nimport { type UseQueryOptions, type UseMutationOptions, useQuery, useMutation } from '@tanstack/react-query';\n\nexport const http = xior.create({\n baseURL: '<%= it.baseUrl || '' %>',\n paramsSerializer: (params: any) =>\n encodeParams(params, true, null, {\n allowDots: <%= it.allowDots %>,\n arrayFormat: '<%= it.arrayFormat %>',\n }),\n});\n\n",
39
- "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n};\n\n<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\n\n// Helper: strip leading \"get\" and capitalise\nfunction toOpName(name) {\n var n = name.toLowerCase().startsWith('get') ? name.substring(3) : name;\n return n.charAt(0).toUpperCase() + n.slice(1);\n}\n%>\n\nexport const <%= it.camelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = toOpName(operation.name);\n var queryOperation = Object.assign({\n rqOpName: 'use' + opName,\n opKey: it.camelCaseName + opName,\n clientName: it.camelCaseName,\n }, operation);\n%>\n<%~ include('queryOperation.ejs', queryOperation); %>\n\n<% }); %>\n },\n\n mutations: {\n<% mutationOperations.forEach((operation) => {\n var opName = operation.name.charAt(0).toUpperCase() + operation.name.slice(1);\n var mutationOperation = Object.assign({\n mutOpName: 'use' + opName,\n clientName: it.camelCaseName,\n }, operation);\n%>\n<%~ include('mutationOperation.ejs', mutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n%>\n <%= keyName %>: (<% operation.parameters.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? ' | null' : '' %>, <% }); %>) => ['<%= it.camelCaseName %>', '<%= it.camelCaseName + opName %>'<% operation.parameters.forEach((parameter) => { %>, <%= parameter.name %><% }); %>] as const,\n<% }); %>\n },\n};\n",
40
+ "client.ejs": "export const <%= it.camelCaseName %>Client = {\n <% it.operations.forEach((operation) => { %>\n<%~ include('operation.ejs', operation); %>\n\n<% }); %>\n};\n\n<%\nvar getOperations = it.operations.filter((o) => o.method === 'GET');\nvar mutationOperations = it.operations.filter((o) => o.method !== 'GET');\n\n// Helper: strip leading \"get\" and capitalise\nfunction toOpName(name) {\n var n = name.toLowerCase().startsWith('get') ? name.substring(3) : name;\n return n.charAt(0).toUpperCase() + n.slice(1);\n}\n\n// Returns a deep copy of operation with any parameter whose name collides with\n// the group object name (clientName) renamed to _<name> to avoid TS shadowing.\nfunction safeOperation(operation, clientName) {\n var safeParams = operation.parameters.map(function(p) {\n return p.name === clientName ? Object.assign({}, p, { name: '_' + p.name }) : p;\n });\n return Object.assign({}, operation, { parameters: safeParams });\n}\n%>\n\nexport const <%= it.camelCaseName %> = {\n queries: {\n<% getOperations.forEach((operation) => {\n var opName = toOpName(operation.name);\n var queryOperation = Object.assign({\n rqOpName: 'use' + opName,\n opKey: it.camelCaseName + opName,\n clientName: it.camelCaseName,\n }, safeOperation(operation, it.camelCaseName));\n%>\n<%~ include('queryOperation.ejs', queryOperation); %>\n\n<% }); %>\n },\n\n mutations: {\n<% mutationOperations.forEach((operation) => {\n var opName = operation.name.charAt(0).toUpperCase() + operation.name.slice(1);\n var mutationOperation = Object.assign({\n mutOpName: 'use' + opName,\n clientName: it.camelCaseName,\n }, safeOperation(operation, it.camelCaseName));\n%>\n<%~ include('mutationOperation.ejs', mutationOperation); %>\n\n<% }); %>\n },\n\n queryKeys: {\n<% getOperations.forEach((operation) => {\n var opName = toOpName(operation.name);\n var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);\n var safeOp = safeOperation(operation, it.camelCaseName);\n%>\n <%= keyName %>: (<% safeOp.parameters.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>, <% }); %>) => ['<%= it.camelCaseName %>', '<%= it.camelCaseName + opName %>'<% safeOp.parameters.forEach((parameter) => { %>, <%= parameter.name %><% }); %>] as const,\n<% }); %>\n },\n};\n",
40
41
  "mutationOperation.ejs": "<%\nvar hasParams = it.parameters.length > 0;\n\nvar variablesType;\nif (!hasParams) {\n variablesType = 'void';\n} else {\n var parts = it.parameters.map(function(p) {\n return p.name + (p.skippable ? '?' : '') + ': ' + p.type + (p.optional ? ' | null' : '');\n });\n variablesType = '{ ' + parts.join('; ') + ' }';\n}\n\nvar callArgs = it.parameters.map(function(p) { return 'vars.' + p.name; });\n\nvar baseAdditionalParams = ' * @param $config (optional) Additional configuration for TanStack Query\\n * @param $httpConfig (optional) Additional configuration for xior request (actually executes the request)';\nvar rawDocs = it.jsDocs\n ? it.jsDocs.replace(/(\\s*)\\*\\/\\s*$/, '\\n' + baseAdditionalParams + '\\n */')\n : '';\nvar docs = rawDocs ? rawDocs.replace(/^/gm, ' ') + '\\n' : '';\n%>\n<%~ docs %>\n <%= it.mutOpName %><TData = <%~ it.returnType %>, TError = Error>(\n $config?: UseMutationOptions<<%~ it.returnType %>, TError, <%~ variablesType %>>,\n $httpConfig?: XiorRequestConfig\n ) {\n return useMutation<<%~ it.returnType %>, TError, <%~ variablesType %>>({\n mutationFn: (<%= hasParams ? 'vars' : '' %>) => <%= it.clientName %>Client.<%= it.name %>(<%= callArgs.join(', ') %><%= callArgs.length > 0 ? ', ' : '' %>$httpConfig).then(res => res.data),\n ...$config\n });\n },\n",
41
42
  "operation.ejs": "<%~ it.jsDocs %>\n\n <%= it.name %>(<% it.parameters.forEach((parameter) => { %>\n<%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %> <%= parameter.optional ? (parameter.skippable ? '| null' : '| null | undefined') : '' %>,\n <% }); %>\n$config?: XiorRequestConfig\n ): Promise<XiorResponse<<%~ it.returnType %>>> {\n const url = `<%= it.url %>`;\n\n return http.request<<%~ it.returnType %>>({\n url: url,\n method: '<%= it.method %>',\n<% if(it.body) { %>\n<% if(it.body.contentType === 'urlencoded') { %>\n data: new URLSearchParams(<%= it.body.name %> as any),\n<% } else { %>\n data: <%= it.body.name %>,\n<% } %>\n<% } %>\n<% if(it.query && it.query.length > 0) { %>\n params: {\n <% it.query.forEach((parameter) => { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% }); %>\n },\n<% } %>\n<% if(it.headers && it.headers.length > 0) { %>\n headers: {\n <% it.headers.forEach((parameter) => { %>\n <% if (parameter.value) { %>\n '<%= parameter.originalName %>': '<%= parameter.value %>',\n <% } else { %>\n '<%= parameter.originalName %>': <%= parameter.name %>,\n <% } %>\n <% }); %>\n},\n<% } %>\n ...$config,\n });\n },\n",
42
43
  "queryOperation.ejs": "<%\nvar baseAdditionalParams = ' * @param $config (optional) Additional configuration for TanStack Query\\n * @param $httpConfig (optional) Additional configuration for xior request (actually executes the request)';\nvar rawDocs = it.jsDocs\n ? it.jsDocs.replace(/(\\s*)\\*\\/\\s*$/, '\\n' + baseAdditionalParams + '\\n */')\n : '';\nvar docs = rawDocs ? rawDocs.replace(/^/gm, ' ') + '\\n' : '';\n%>\n<%~ docs %>\n <%= it.rqOpName %><TData = <%~ it.returnType %>, TError = Error>(\n<% it.parameters.forEach((parameter) => { %> <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>,\n<% }); %> $config?: Omit<UseQueryOptions<<%~ it.returnType %>, TError, TData>, 'queryKey' | 'queryFn'>,\n $httpConfig?: XiorRequestConfig\n ) {\n return useQuery<<%~ it.returnType %>, TError, TData>({\n queryKey: <%= it.clientName %>.queryKeys.<%= it.rqOpName.charAt(3).toLowerCase() + it.rqOpName.slice(4) %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>),\n queryFn: () => <%= it.clientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>$httpConfig).then(res => res.data),\n ...$config\n });\n },\n"
@@ -249,11 +249,11 @@ function getTypeFromObject(
249
249
 
250
250
  if (schema.additionalProperties) {
251
251
  const extraProps = schema.additionalProperties;
252
- objectWithIndexSignatureType = `{ [key: string]: ${
252
+ objectWithIndexSignatureType = `Record<string, ${
253
253
  extraProps === true
254
- ? 'any'
254
+ ? unknownType
255
255
  : getTypeFromSchemaResolved(extraProps, options, `${context}.additionalProperties`)
256
- } }`;
256
+ }>`;
257
257
  }
258
258
 
259
259
  if (objectWithNamedPropsType && objectWithIndexSignatureType) {
@@ -268,6 +268,12 @@ function getTypeFromObject(
268
268
  return objectWithIndexSignatureType;
269
269
  }
270
270
 
271
+ // A bare `type: object` with no properties and no additionalProperties is a
272
+ // free-form object per the OpenAPI spec (equivalent to additionalProperties: true).
273
+ if (schema.type === 'object') {
274
+ return `Record<string, ${unknownType}>`;
275
+ }
276
+
271
277
  return unknownType;
272
278
  }
273
279
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swaggie",
3
- "version": "1.9.0-alpha.2",
3
+ "version": "1.9.0-alpha.5",
4
4
  "description": "Generate a fully typed TypeScript API client from your OpenAPI 3 spec",
5
5
  "author": {
6
6
  "name": "Piotr Dabrowski",
@@ -1,5 +1,6 @@
1
1
  import Axios, { type AxiosPromise, type AxiosRequestConfig } from "axios";
2
2
  import useSWR, { type SWRConfiguration, type Key } from 'swr';
3
+ import useSWRMutation, { type SWRMutationConfiguration } from 'swr/mutation';
3
4
 
4
5
  export const axios = Axios.create({
5
6
  baseURL: '<%= it.baseUrl || '' -%>',
@@ -13,8 +14,5 @@ export const axios = Axios.create({
13
14
  interface SwrConfig extends SWRConfiguration {
14
15
  /* Custom key for SWR. You don't have to worry about this as by default it's the URL. You can use standard SWR Key here if you need more flexibility. */
15
16
  key?: Key;
16
-
17
- /* Configuration for axios fetcher */
18
- axios?: AxiosRequestConfig;
19
17
  }
20
18
 
@@ -7,11 +7,19 @@ export const <%= it.camelCaseName -%>Client = {
7
7
 
8
8
  <%
9
9
  var getOperations = it.operations.filter((o) => o.method === 'GET');
10
+ var mutationOperations = it.operations.filter((o) => o.method !== 'GET');
10
11
 
11
12
  function toOpName(name) {
12
13
  var n = name.toLowerCase().startsWith('get') ? name.substring(3) : name;
13
14
  return n.charAt(0).toUpperCase() + n.slice(1);
14
15
  }
16
+
17
+ function safeOperation(operation, clientName) {
18
+ var safeParams = operation.parameters.map(function(p) {
19
+ return p.name === clientName ? Object.assign({}, p, { name: '_' + p.name }) : p;
20
+ });
21
+ return Object.assign({}, operation, { parameters: safeParams });
22
+ }
15
23
  %>
16
24
 
17
25
  export const <%= it.camelCaseName %> = {
@@ -21,10 +29,23 @@ export const <%= it.camelCaseName %> = {
21
29
  var swrOperation = Object.assign({
22
30
  swrOpName: 'use' + opName,
23
31
  clientName: it.camelCaseName,
24
- }, operation);
32
+ }, safeOperation(operation, it.camelCaseName));
25
33
  %>
26
34
  <%~ include('swrOperation.ejs', swrOperation); %>
27
35
 
36
+ <% }); %>
37
+ },
38
+
39
+ mutations: {
40
+ <% mutationOperations.forEach((operation) => {
41
+ var opName = operation.name.charAt(0).toUpperCase() + operation.name.slice(1);
42
+ var swrMutationOperation = Object.assign({
43
+ mutOpName: 'use' + opName,
44
+ clientName: it.camelCaseName,
45
+ }, safeOperation(operation, it.camelCaseName));
46
+ %>
47
+ <%~ include('swrMutationOperation.ejs', swrMutationOperation); %>
48
+
28
49
  <% }); %>
29
50
  },
30
51
 
@@ -32,8 +53,9 @@ export const <%= it.camelCaseName %> = {
32
53
  <% getOperations.forEach((operation) => {
33
54
  var opName = toOpName(operation.name);
34
55
  var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);
56
+ var safeOp = safeOperation(operation, it.camelCaseName);
35
57
  %>
36
- <%= keyName %>: (<% operation.parameters.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? ' | null' : '' %>, <% }); %>) => `<%= operation.url %><% if(operation.query && operation.query.length > 0) { %>?${encodeParams({<% operation.query.forEach((parameter) => { %>'<%= parameter.originalName %>': <%= parameter.name %>, <% }); %>})}<% } %>`,
58
+ <%= keyName %>: (<% safeOp.parameters.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>, <% }); %>) => `<%= operation.url %><% if(safeOp.query && safeOp.query.length > 0) { %>?${encodeParams({<% safeOp.query.forEach((parameter) => { %>'<%= parameter.originalName %>': <%= parameter.name %>, <% }); %>})}<% } %>`,
37
59
  <% }); %>
38
60
  },
39
61
  };
@@ -0,0 +1,32 @@
1
+ <%
2
+ var hasParams = it.parameters.length > 0;
3
+
4
+ var variablesType;
5
+ if (!hasParams) {
6
+ variablesType = 'void';
7
+ } else {
8
+ var parts = it.parameters.map(function(p) {
9
+ return p.name + (p.skippable ? '?' : '') + ': ' + p.type + (p.optional ? ' | null' : '');
10
+ });
11
+ variablesType = '{ ' + parts.join('; ') + ' }';
12
+ }
13
+
14
+ var callArgs = it.parameters.map(function(p) { return 'arg.' + p.name; });
15
+
16
+ // Stable SWR key: replace all ${...} path expressions with *
17
+ var swrKey = it.url.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, '*');
18
+
19
+ var docs = it.jsDocs ? it.jsDocs.replace(/^/gm, ' ') + '\n' : '';
20
+ %>
21
+ <%~ docs %>
22
+ <%= it.mutOpName %>(
23
+ $config?: SWRMutationConfiguration<<%~ it.returnType %>, Error, string, <%~ variablesType %>>,
24
+ $httpConfig?: AxiosRequestConfig
25
+ ) {
26
+ return useSWRMutation<<%~ it.returnType %>, Error, string, <%~ variablesType %>>(
27
+ '<%= swrKey %>',
28
+ (_key: string, { arg }<%= hasParams ? ': { arg: ' + variablesType + ' }' : '' %>) =>
29
+ <%= it.clientName %>Client.<%= it.name %>(<%= callArgs.join(', ') %><%= callArgs.length > 0 ? ', ' : '' %>$httpConfig).then((resp) => resp.data),
30
+ $config
31
+ );
32
+ },
@@ -2,45 +2,15 @@
2
2
  <%~ docs %>
3
3
  <%= it.swrOpName %>(
4
4
  <% it.parameters.forEach((parameter) => { %> <%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>,
5
- <% }); %> $config?: SwrConfig
5
+ <% }); %> $config?: Omit<SwrConfig, 'key'> & { key?: Key },
6
+ $httpConfig?: AxiosRequestConfig
6
7
  ) {
7
- const url = `<%= it.url %>`;
8
- const { axios: $axiosConf, key, ...config } = $config || {};
9
-
10
- <% if(it.query && it.query.length > 0) { %>
11
- const cacheKey = encodeParams({<% it.query.forEach((parameter) => { %>
12
- '<%= parameter.originalName %>': <%= parameter.name %>,<% }); %>
13
- });
14
- const cacheUrl = cacheKey ? `${url}?${cacheKey}` : url;
15
- <% } else { %>
16
- const cacheUrl = url;
17
- <% } %>
8
+ const { key, ...config } = $config || {};
9
+ const cacheUrl = key ?? <%= it.clientName %>.queryKeys.<%= it.swrOpName.charAt(3).toLowerCase() + it.swrOpName.slice(4) %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>);
18
10
 
19
11
  const { data, error, isLoading, mutate } = useSWR<<%~ it.returnType %>>(
20
- key ?? cacheUrl,
21
- () => axios.request({
22
- url: url,
23
- method: '<%= it.method %>',
24
- <% if(it.query && it.query.length > 0) { %>
25
- params: {
26
- <% it.query.forEach((parameter) => { %>
27
- '<%= parameter.originalName %>': <%= parameter.name %>,
28
- <% }); %>
29
- },
30
- <% } %>
31
- <% if(it.headers && it.headers.length > 0) { %>
32
- headers: {
33
- <% it.headers.forEach((parameter) => { %>
34
- <% if (parameter.value) { %>
35
- '<%= parameter.originalName %>': '<%= parameter.value %>',
36
- <% } else { %>
37
- '<%= parameter.originalName %>': <%= parameter.name %>,
38
- <% } %>
39
- <% }); %>
40
- },
41
- <% } %>
42
- ...$axiosConf,
43
- }).then((resp) => resp.data),
12
+ cacheUrl,
13
+ () => <%= it.clientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %><%= parameter.name %>, <% }); %>$httpConfig).then((resp) => resp.data),
44
14
  config
45
15
  );
46
16
 
@@ -14,6 +14,15 @@ function toOpName(name) {
14
14
  var n = name.toLowerCase().startsWith('get') ? name.substring(3) : name;
15
15
  return n.charAt(0).toUpperCase() + n.slice(1);
16
16
  }
17
+
18
+ // Returns a deep copy of operation with any parameter whose name collides with
19
+ // the group object name (clientName) renamed to _<name> to avoid TS shadowing.
20
+ function safeOperation(operation, clientName) {
21
+ var safeParams = operation.parameters.map(function(p) {
22
+ return p.name === clientName ? Object.assign({}, p, { name: '_' + p.name }) : p;
23
+ });
24
+ return Object.assign({}, operation, { parameters: safeParams });
25
+ }
17
26
  %>
18
27
 
19
28
  export const <%= it.camelCaseName %> = {
@@ -24,7 +33,7 @@ export const <%= it.camelCaseName %> = {
24
33
  rqOpName: 'use' + opName,
25
34
  opKey: it.camelCaseName + opName,
26
35
  clientName: it.camelCaseName,
27
- }, operation);
36
+ }, safeOperation(operation, it.camelCaseName));
28
37
  %>
29
38
  <%~ include('queryOperation.ejs', queryOperation); %>
30
39
 
@@ -37,7 +46,7 @@ export const <%= it.camelCaseName %> = {
37
46
  var mutationOperation = Object.assign({
38
47
  mutOpName: 'use' + opName,
39
48
  clientName: it.camelCaseName,
40
- }, operation);
49
+ }, safeOperation(operation, it.camelCaseName));
41
50
  %>
42
51
  <%~ include('mutationOperation.ejs', mutationOperation); %>
43
52
 
@@ -48,8 +57,9 @@ export const <%= it.camelCaseName %> = {
48
57
  <% getOperations.forEach((operation) => {
49
58
  var opName = toOpName(operation.name);
50
59
  var keyName = opName.charAt(0).toLowerCase() + opName.slice(1);
60
+ var safeOp = safeOperation(operation, it.camelCaseName);
51
61
  %>
52
- <%= keyName %>: (<% operation.parameters.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? ' | null' : '' %>, <% }); %>) => ['<%= it.camelCaseName %>', '<%= it.camelCaseName + opName %>'<% operation.parameters.forEach((parameter) => { %>, <%= parameter.name %><% }); %>] as const,
62
+ <%= keyName %>: (<% safeOp.parameters.forEach((parameter) => { %><%= parameter.name %><%= parameter.skippable ? '?' : '' %>: <%~ parameter.type %><%= parameter.optional ? (parameter.skippable ? ' | null' : ' | null | undefined') : '' %>, <% }); %>) => ['<%= it.camelCaseName %>', '<%= it.camelCaseName + opName %>'<% safeOp.parameters.forEach((parameter) => { %>, <%= parameter.name %><% }); %>] as const,
53
63
  <% }); %>
54
64
  },
55
65
  };