( code: string, edits: readonly ArtifactEdit[], )
| 235 | * name the edit by position and say what to do, because the model retries |
| 236 | * from the message alone. */ |
| 237 | export const applyArtifactEdits = ( |
| 238 | code: string, |
| 239 | edits: readonly ArtifactEdit[], |
| 240 | ): AppliedArtifactEdits => { |
| 241 | let current = code; |
| 242 | for (const [index, edit] of edits.entries()) { |
| 243 | const position = `Edit ${index + 1} of ${edits.length}`; |
| 244 | if (edit.oldText === "") { |
| 245 | return { ok: false, message: `${position} has an empty oldText, which matches nothing.` }; |
| 246 | } |
| 247 | if (edit.oldText === edit.newText) { |
| 248 | return { |
| 249 | ok: false, |
| 250 | message: `${position} is a no-op: oldText and newText are identical.`, |
| 251 | }; |
| 252 | } |
| 253 | const occurrences = countOccurrences(current, edit.oldText); |
| 254 | if (occurrences === 0) { |
| 255 | return { |
| 256 | ok: false, |
| 257 | message: [ |
| 258 | `${position} matched nothing: its oldText does not appear in the current source.`, |
| 259 | "Copy oldText verbatim from the source, whitespace included", |
| 260 | index > 0 ? "— and remember each edit sees the result of the earlier ones." : ".", |
| 261 | ].join(" "), |
| 262 | }; |
| 263 | } |
| 264 | if (occurrences > 1 && edit.replaceAll !== true) { |
| 265 | return { |
| 266 | ok: false, |
| 267 | message: [ |
| 268 | `${position} is ambiguous: its oldText appears ${occurrences} times in the current source.`, |
| 269 | "Include enough surrounding text to make it unique, or set replaceAll: true to change every occurrence.", |
| 270 | ].join(" "), |
| 271 | }; |
| 272 | } |
| 273 | // split/join for both arms: `String.replace` gives `$&` and friends in the |
| 274 | // replacement special meaning, and generated JSX is full of `$`. |
| 275 | current = |
| 276 | edit.replaceAll === true |
| 277 | ? current.split(edit.oldText).join(edit.newText) |
| 278 | : current.replace(edit.oldText, () => edit.newText); |
| 279 | } |
| 280 | return { ok: true, code: current }; |
| 281 | }; |
| 282 | |
| 283 | // --------------------------------------------------------------------------- |
| 284 | // Create-time smoke render |
no test coverage detected