(source: string, modelNames: ReadonlyArray<string>, enabled: boolean)
| 272 | } |
| 273 | |
| 274 | function syncFacade(source: string, modelNames: ReadonlyArray<string>, enabled: boolean): string { |
| 275 | let out = source |
| 276 | for (const name of modelNames) { |
| 277 | const n = escapeRe(name) |
| 278 | if (enabled) { |
| 279 | // Base mode: the facade is the generated `class __X extends OpaqueFacade<...>()(_X)` |
| 280 | // (owned by its modelFacade block); the user owns `export class X extends __X { ...statics... }`. |
| 281 | // Leave both alone — only the block preset regenerates `__X`. |
| 282 | const baseMode = new RegExp( |
| 283 | `(^|\\n)\\s*class\\s+__${n}\\s+extends\\s+(?:[A-Za-z_$][\\w$]*\\.)?OpaqueFacade(?:Class)?\\s*<` |
| 284 | ) |
| 285 | if (baseMode.test(out)) continue |
| 286 | const existingClass = new RegExp( |
| 287 | `(^|\\n)\\s*export\\s+class\\s+${n}\\s+extends\\s+(?:[A-Za-z_$][\\w$]*\\.)?OpaqueFacade(?:Class)?\\s*<` |
| 288 | ) |
| 289 | const existingConst = new RegExp(`(^|\\n)\\s*export\\s+const\\s+${n}\\s*:\\s*${n}\\.Schema\\s*=`) |
| 290 | if (existingClass.test(out) || existingConst.test(out)) { |
| 291 | // Upgrade a 3-arg facade (`<X, X.Encoded, X.Make>`) to the 5-arg form by |
| 292 | // appending the services. Idempotent: when the services are already |
| 293 | // present (group 3), leave the match untouched so we don't collapse a |
| 294 | // dprint-wrapped multi-line declaration back to one line (which would |
| 295 | // oscillate with the formatter forever). |
| 296 | out = out.replace( |
| 297 | new RegExp( |
| 298 | `(OpaqueFacade(?:Class)?<\\s*)${n}(?:\\.Type)?(\\s*,\\s*${n}\\.Encoded\\s*,\\s*${n}\\.Make)((?:\\s*,\\s*${n}\\.DecodingServices\\s*,\\s*${n}\\.EncodingServices)?)(\\s*>)`, |
| 299 | "g" |
| 300 | ), |
| 301 | (match, p1: string, p2: string, services: string, p3: string) => |
| 302 | services ? match : `${p1}${name}${p2}, ${name}.DecodingServices, ${name}.EncodingServices${p3}` |
| 303 | ) |
| 304 | const privateClassRe = new RegExp(`(^|\\n)(\\s*)class\\s+_${n}\\b`) |
| 305 | const privateMatch = privateClassRe.exec(out) |
| 306 | if (privateMatch) { |
| 307 | const start = privateMatch.index + privateMatch[1]!.length |
| 308 | const end = findClassEnd(out, start) |
| 309 | if (end !== -1) { |
| 310 | const pIndent = privateMatch[2]! |
| 311 | const ct = out.slice(start, end) |
| 312 | const replacement = tryStructPrivate(ct, name, pIndent) ?? syncFacadeSourceCtor(ct, name) |
| 313 | out = `${out.slice(0, start)}${replacement}${out.slice(end)}` |
| 314 | } |
| 315 | } |
| 316 | const facadeBlock = new RegExp( |
| 317 | `// codegen:start[^\\n]*\\{[^}]*\\bpreset:\\s*modelFacade\\b[^}]*\\bclassName:\\s*_${n}\\b[^}]*\\}[\\s\\S]*?export\\s+(?:const|class)\\s+${n}\\b` |
| 318 | ) |
| 319 | if (!facadeBlock.test(out)) { |
| 320 | const facadeLine = new RegExp( |
| 321 | `(^|\\n)([ \\t]*)export\\s+const\\s+${n}\\s*:\\s*${n}\\.Schema\\s*=\\s*((?:(?:[A-Za-z_$][\\w$]*\\.)?)OpaqueFacade<\\s*${n}\\s*,\\s*${n}\\.Encoded\\s*,\\s*${n}\\.Make(?:\\s*,\\s*${n}\\.DecodingServices\\s*,\\s*${n}\\.EncodingServices)?\\s*>\\(\\)\\(\\s*_${n}\\s*\\))` |
| 322 | ) |
| 323 | out = out.replace(facadeLine, (_match, lineStart: string, indent: string, expression: string) => { |
| 324 | const prefix = /^((?:[A-Za-z_$][\w$]*\.)?)OpaqueFacade/.exec(expression)?.[1] ?? "" |
| 325 | return [ |
| 326 | `${lineStart}${indent}// codegen:start {preset: modelFacade, className: _${name}${schemaOption(prefix)}}`, |
| 327 | `${indent}${facadeClassLine(name, prefix)}`, |
| 328 | `${indent}// codegen:end` |
| 329 | ] |
| 330 | .join("\n") |
| 331 | }) |
no test coverage detected
searching dependent graphs…