@@ -1130,6 +1130,29 @@ function cleanSingleParam(param: string): string {
11301130 return param
11311131}
11321132
1133+ /**
1134+ * Find the index after any leading block comments and whitespace in a key.
1135+ * Used to locate the actual identifier so modifiers like `async` can be
1136+ * detected even when JSDoc precedes the method name.
1137+ */
1138+ function findIdentifierStart ( key : string ) : number {
1139+ let i = 0
1140+ const n = key . length
1141+ while ( i < n ) {
1142+ // Skip whitespace
1143+ while ( i < n && ( key . charCodeAt ( i ) === 32 || key . charCodeAt ( i ) === 9 || key . charCodeAt ( i ) === 10 || key . charCodeAt ( i ) === 13 ) ) i ++
1144+ // Skip /* ... */ block comments (handles nested */ since dtsx-stripped)
1145+ if ( i + 1 < n && key . charCodeAt ( i ) === 47 && key . charCodeAt ( i + 1 ) === 42 ) {
1146+ i += 2
1147+ while ( i + 1 < n && ! ( key . charCodeAt ( i ) === 42 && key . charCodeAt ( i + 1 ) === 47 ) ) i ++
1148+ i += 2 // skip the closing '*/'
1149+ continue
1150+ }
1151+ break
1152+ }
1153+ return i
1154+ }
1155+
11331156/**
11341157 * Parse object properties
11351158 */
@@ -1143,6 +1166,13 @@ function parseObjectProperties(content: string): Array<[string, string]> {
11431166 let inKey = true
11441167 let inComment = false
11451168 let commentDepth = 0
1169+ // True while collecting the value of a method shorthand (between the
1170+ // method's '(' and the body's closing ' }').
1171+ let inMethodShorthand = false
1172+ // True between the closing ')' of a method shorthand's params and its body '{'.
1173+ // While true, commas at depth 0 belong to the return-type annotation
1174+ // (e.g. `Promise<Record<string, X>>`) and must not split properties.
1175+ let methodAwaitingBody = false
11461176
11471177 for ( let i = 0 ; i < content . length ; i ++ ) {
11481178 const cc = content . charCodeAt ( i )
@@ -1196,38 +1226,61 @@ function parseObjectProperties(content: string): Array<[string, string]> {
11961226 }
11971227 else if ( ! inString && ! inComment ) {
11981228 if ( cc === 40 /* ( */ && depth === 0 && inKey ) {
1199- // Method definition like: methodName(params) or async methodName<T>(params)
1200- // Must be checked BEFORE general bracket tracking so ( isn't swallowed
1229+ // Method definition like: methodName(params) or async methodName<T>(params).
1230+ // Must be checked BEFORE general bracket tracking so ( isn't swallowed.
12011231 currentKey = current . trim ( )
1202- // Remove 'async' from the key if present, prefix value with 'async ' marker
1232+ // The key may carry leading JSDoc/block comments. Split off the
1233+ // comment block so `async`/`*` modifiers right before the
1234+ // identifier can still be detected and stripped.
1235+ const idStart = findIdentifierStart ( currentKey )
1236+ const commentLead = idStart > 0 ? currentKey . slice ( 0 , idStart ) : ''
1237+ let identifier = currentKey . slice ( idStart )
12031238 let methodPrefix = ''
1204- if ( currentKey . startsWith ( 'async ' ) ) {
1205- currentKey = currentKey . slice ( 6 ) . trim ( )
1239+ if ( identifier . startsWith ( 'async ' ) || identifier . startsWith ( 'async\t' ) || identifier . startsWith ( 'async\n ') ) {
1240+ identifier = identifier . slice ( 6 ) . trimStart ( )
12061241 methodPrefix = 'async '
12071242 }
1208- // Remove generator '*' prefix from key, prefix value with '*' marker
1209- if ( currentKey . startsWith ( '*' ) ) {
1210- currentKey = currentKey . slice ( 1 ) . trim ( )
1243+ if ( identifier . startsWith ( '*' ) ) {
1244+ identifier = identifier . slice ( 1 ) . trimStart ( )
12111245 methodPrefix += '*'
12121246 }
1247+ currentKey = commentLead + identifier
12131248 current = methodPrefix + char // Start with any prefix + opening parenthesis
12141249 inKey = false
1215- depth = 1 // We're now inside the method definition
1250+ inMethodShorthand = true
1251+ methodAwaitingBody = false
1252+ depth = 1 // We're now inside the method definition's params
12161253 }
12171254 else if ( cc === 123 /* { */ || cc === 91 /* [ */ || cc === 40 /* ( */ ) {
12181255 depth ++
12191256 current += char
1257+ // Body '{' of a method shorthand — type annotation phase ends here.
1258+ if ( methodAwaitingBody && cc === 123 /* { */ ) {
1259+ methodAwaitingBody = false
1260+ }
12201261 }
12211262 else if ( cc === 125 /* } */ || cc === 93 /* ] */ || cc === 41 /* ) */ ) {
12221263 depth --
12231264 current += char
1265+ if ( inMethodShorthand ) {
1266+ // Closing ')' of params at depth 0 → enter return-type annotation
1267+ // phase (commas in `Record<K, V>` etc must not split properties).
1268+ if ( cc === 41 /* ) */ && depth === 0 && ! methodAwaitingBody ) {
1269+ methodAwaitingBody = true
1270+ }
1271+ // Closing ' }' at depth 0 ends the method shorthand value entirely.
1272+ else if ( cc === 125 /* } */ && depth === 0 ) {
1273+ inMethodShorthand = false
1274+ methodAwaitingBody = false
1275+ }
1276+ }
12241277 }
12251278 else if ( cc === 58 /* : */ && depth === 0 && inKey ) {
12261279 currentKey = current . trim ( )
12271280 current = ''
12281281 inKey = false
12291282 }
1230- else if ( cc === 44 /* , */ && depth === 0 ) {
1283+ else if ( cc === 44 /* , */ && depth === 0 && ! methodAwaitingBody ) {
12311284 if ( currentKey && current . trim ( ) ) {
12321285 const value = current . trim ( )
12331286 properties . push ( [ currentKey , value ] )
0 commit comments