Skip to content

Commit 351df61

Browse files
committed
ok
1 parent 5c8cb54 commit 351df61

8 files changed

Lines changed: 24 additions & 76 deletions

File tree

eslint.config.mjs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,13 @@ export default tseslint.config(
7676
Existing rule you had
7777
=========================== */
7878

79-
'@typescript-eslint/no-explicit-any': 'off'
79+
'@typescript-eslint/no-explicit-any': 'off',
80+
81+
'padding-line-between-statements': [
82+
'error',
83+
{ blankLine: 'never', prev: 'if', next: '*' },
84+
{ blankLine: 'never', prev: '*', next: 'if' }
85+
]
8086
}
8187
}
8288
)

example/middleware.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export function rateLimitMiddleware(
2727
const ip = req.ip
2828
const now = Date.now()
2929
const record = requestCounts.get(ip!)
30-
3130
if (!record || now > record.resetTime) {
3231
requestCounts.set(ip!, {
3332
count: 1,
@@ -36,15 +35,13 @@ export function rateLimitMiddleware(
3635
next()
3736
return
3837
}
39-
4038
if (record.count >= maxRequests) {
4139
res.status(429).json({
4240
error: 'Too Many Requests',
4341
retryAfter: Math.ceil((record.resetTime - now) / 1_000)
4442
})
4543
return
4644
}
47-
4845
record.count++
4946
next()
5047
}

src/controller.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ export function addController(
104104
)
105105
continue // skip
106106
}
107-
108107
let hasSuccessResponse = false
109108
const returnType = method.getReturnType()
110109

@@ -168,7 +167,6 @@ export function addController(
168167
}
169168
}
170169
}
171-
172170
// Add default success response
173171
if (!hasSuccessResponse) {
174172
if (
@@ -190,7 +188,6 @@ export function addController(
190188
}
191189
}
192190
}
193-
194191
// We use another array for codegen parameters instead of operation.parameters
195192
// because we want to have Request() and Body() in the codegen one
196193
// to send it to the method at runtime
@@ -328,7 +325,6 @@ export function addController(
328325

329326
// Security
330327
operation.security = [...controllerSecurities, ...getSecurities(method)]
331-
332328
// OperationId
333329
if (method.getDecorator('OperationId')) {
334330
operation.operationId = extractDecoratorValues(
@@ -338,12 +334,10 @@ export function addController(
338334
const name = method.getName()
339335
operation.operationId = name.charAt(0).toUpperCase() + name.slice(1)
340336
}
341-
342337
// Deprecated
343338
if (method.getDecorator('Deprecated')) {
344339
operation.deprecated = true
345340
}
346-
347341
// Add to spec + codegen
348342
const isHidden =
349343
typeof (

src/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ export async function generate(config: OpenAPIConfiguration) {
148148
if (typeof config.openapi.securitySchemes !== 'undefined') {
149149
spec.components!.securitySchemes = config.openapi.securitySchemes
150150
}
151-
152151
// Codegen object
153152
const codegenControllers: CodeGenControllers = {}
154153
const controllersPathByName: Record<string, string> = {}
@@ -215,7 +214,6 @@ export async function generate(config: OpenAPIConfiguration) {
215214
spec.components!.schemas![name] = resolved
216215
}
217216
}
218-
219217
// Export all responses
220218
if (
221219
typeof config.openapi.outputErrorsToDescription !== 'undefined' &&
@@ -327,7 +325,6 @@ export async function generate(config: OpenAPIConfiguration) {
327325
rows.map(row => `| ${row.join(' | ')} |`).join('\n')
328326
spec.info.description = `# Errors\n${markdown}`
329327
}
330-
331328
// Write OpenAPI file(s)
332329
const jsonContent = JSON.stringify(spec, null, '\t')
333330

@@ -339,7 +336,6 @@ export async function generate(config: OpenAPIConfiguration) {
339336
// Process each file path
340337
for (const filePath of filePaths) {
341338
const resolvedPath = path.resolve(root, filePath)
342-
343339
// Determine format based on file extension
344340
if (
345341
filePath.toLowerCase().endsWith('.yaml') ||

src/resolve.ts

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,6 @@ export function resolve(
304304
// For other and anonymous types, don't use ref
305305
return resolveObjectType(type, spec)
306306
}
307-
308307
// Add to spec components if not already resolved
309308
// tslint:disable-next-line: strict-type-predicates
310309
if (typeof spec.components!.schemas![typeName] === 'undefined') {
@@ -422,6 +421,14 @@ function resolveProperties(
422421
writeOnly: true
423422
})
424423
}
424+
if (
425+
jsDocTags.some(tag => tag.name === 'additionalproperties') &&
426+
!('$ref' in resolvedType)
427+
) {
428+
appendMetaToResolvedType(resolvedType, {
429+
additionalProperties: false
430+
})
431+
}
425432
// JSDoc tags
426433
appendJsDocTags(jsDocTags, resolvedType)
427434
// initializer
@@ -451,11 +458,9 @@ function resolveProperties(
451458
// OpenAPI don't want the required[] prop if it's empty
452459
delete result.required
453460
}
454-
455461
// Check for index signatures regardless of whether explicit properties exist
456462
const stringIndexType = type.getStringIndexType()
457463
const numberIndexType = type.getNumberIndexType()
458-
459464
// Handle mapped types and objects with index signatures (ex: { [key: string]: any } or Record<string, any>)
460465
if (
461466
(typeof stringIndexType !== 'undefined' &&
@@ -474,7 +479,6 @@ function resolveProperties(
474479
// Check if the type represents a Record-like structure that should have additionalProperties
475480
const typeSymbol = type.getSymbol()
476481
const typeText = type.getText()
477-
478482
// Handle cases where the type might be a transformed Record type
479483
if (typeSymbol?.getName() === '__type' || typeText.includes('Record<')) {
480484
// For anonymous types that might be transformed Record types,
@@ -485,7 +489,6 @@ function resolveProperties(
485489
if (apparentType && apparentType !== type) {
486490
const apparentStringIndexType = apparentType.getStringIndexType()
487491
const apparentNumberIndexType = apparentType.getNumberIndexType()
488-
489492
if (
490493
(typeof apparentStringIndexType !== 'undefined' &&
491494
apparentStringIndexType.getText() !== 'never') ||
@@ -500,7 +503,6 @@ function resolveProperties(
500503
}
501504
}
502505
}
503-
504506
return result
505507
}
506508

@@ -530,7 +532,6 @@ function resolveMappedObjectType(
530532
const omittedKeys = typeArguments[1].isUnion()
531533
? typeArguments[1].getUnionTypes().map(t => String(t.getLiteralValue()))
532534
: [String(typeArguments[1].getLiteralValue())]
533-
534535
if (helperName === 'Omit' && omittedKeys.includes('toJSON')) {
535536
// If we're omitting toJSON, don't follow it to avoid infinite loops
536537
shouldSkipToJSON = true
@@ -539,22 +540,19 @@ function resolveMappedObjectType(
539540
shouldSkipToJSON = true
540541
}
541542
}
542-
543543
// Check if the subject type has a toJSON method
544544
const toJSONProperty = subjectType.getProperty('toJSON')
545545
if (toJSONProperty && !shouldSkipToJSON) {
546546
const node = getDeclarationForProperty(subjectType, toJSONProperty) as
547547
| MethodDeclaration
548548
| MethodSignature
549549
const toJSONReturnType = resolve(node.getReturnType(), spec)
550-
551550
// Apply mapped type transformation to the toJSON return type
552551
if ('$ref' in toJSONReturnType) {
553552
// For reference types, we need to create a new schema with the transformation applied
554553
// This is more complex, so for now we'll fall back to normal resolution
555554
return resolveObjectType(type, spec)
556555
}
557-
558556
if (toJSONReturnType.type === 'object' && toJSONReturnType.properties) {
559557
const transformedSchema = { ...toJSONReturnType }
560558

@@ -571,7 +569,6 @@ function resolveMappedObjectType(
571569
return transformedSchema
572570
}
573571
}
574-
575572
// Fall back to normal object resolution if no toJSON or transformation failed
576573
return resolveObjectType(type, spec)
577574
}
@@ -582,7 +579,6 @@ function resolveMappedObjectType(
582579
function hasInterfaceInheritance(type: Type): boolean {
583580
const symbol = type.getSymbol()
584581
if (!symbol) return false
585-
586582
const declarations = symbol.getDeclarations()
587583

588584
return declarations.some(
@@ -599,13 +595,11 @@ function getBaseInterfaces(
599595
): OpenAPIV3.ReferenceObject[] {
600596
const symbol = type.getSymbol()
601597
if (!symbol) return []
602-
603598
const baseRefs: OpenAPIV3.ReferenceObject[] = []
604599
const declarations = symbol.getDeclarations()
605600

606601
for (const decl of declarations) {
607602
if (!Node.isInterfaceDeclaration(decl)) continue
608-
609603
const extendsExpressions = decl.getExtends()
610604
for (const extendsExpr of extendsExpressions) {
611605
const baseType = extendsExpr.getType()
@@ -631,17 +625,14 @@ function getOwnInterfaceProperties(
631625
// Fallback to normal property resolution
632626
return resolveProperties(type, spec)
633627
}
634-
635628
const declarations = symbol.getDeclarations()
636629
const interfaceDecl = declarations.find(decl =>
637630
Node.isInterfaceDeclaration(decl)
638631
)
639-
640632
if (!interfaceDecl) {
641633
// Fallback to normal property resolution
642634
return resolveProperties(type, spec)
643635
}
644-
645636
const result: ResolvePropertiesReturnType = {
646637
properties: {},
647638
required: []
@@ -675,7 +666,6 @@ function getOwnInterfaceProperties(
675666
// Handle JSDoc tags
676667
const jsDocTags = propSig.getSymbol()?.compilerSymbol.getJsDocTags() ?? []
677668
appendJsDocTags(jsDocTags, resolvedType)
678-
679669
// Add to properties
680670
if (
681671
!('type' in resolvedType && (resolvedType.type as any) === 'undefined')
@@ -686,14 +676,11 @@ function getOwnInterfaceProperties(
686676
}
687677
}
688678
}
689-
690679
// Handle method signatures (but ignore them like in resolveProperties)
691680
// Methods are already filtered out by only looking at property signatures
692-
693681
if (result.required!.length === 0) {
694682
delete result.required
695683
}
696-
697684
return result
698685
}
699686

@@ -719,31 +706,26 @@ function resolveObjectType(
719706
| MethodSignature
720707
return resolve(node.getReturnType(), spec)
721708
}
722-
723709
// Check for interface inheritance
724710
if (!hasInterfaceInheritance(type)) {
725711
return {
726712
type: 'object',
727713
...resolveProperties(type, spec)
728714
}
729715
}
730-
731716
const baseRefs = getBaseInterfaces(type, spec)
732-
733717
// If there are no base interfaces, fall back to normal resolution
734718
if (baseRefs.length === 0) {
735719
return {
736720
type: 'object',
737721
...resolveProperties(type, spec)
738722
}
739723
}
740-
741724
// Shallow copy to avoid mutating the original baseRefs array
742725
const allOfElements = Array.from<
743726
OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject
744727
>(baseRefs)
745728
const ownProps = getOwnInterfaceProperties(type, spec)
746-
747729
// Add own properties if any exist
748730
if (
749731
Object.keys(ownProps.properties).length > 0 ||
@@ -754,7 +736,6 @@ function resolveObjectType(
754736
...ownProps
755737
})
756738
}
757-
758739
return {
759740
allOf: allOfElements
760741
}
@@ -823,7 +804,6 @@ export function appendJsDocTags(
823804
if (!supportedTags.includes(tag.name) || !tag.text) {
824805
continue
825806
}
826-
827807
const textValue = tag.text.map(t => t.text).join('\n')
828808
const value = numericTags.includes(tag.name)
829809
? parseFloat(textValue)
@@ -845,7 +825,6 @@ export function appendInitializer(
845825
// Default value
846826
const initializer = node.getInitializer()
847827
const initializerType = initializer?.getType()
848-
849828
if (initializerType?.isLiteral()) {
850829
const initializerLiteralType = initializerType.compilerType as
851830
| ts.StringLiteralType

0 commit comments

Comments
 (0)