Skip to content

fix: apply search and replace in a single pass - #287

Open
rajanpanth wants to merge 2 commits into
solana-foundation:mainfrom
rajanpanth:fix/single-pass-search-replace
Open

fix: apply search and replace in a single pass#287
rajanpanth wants to merge 2 commits into
solana-foundation:mainfrom
rajanpanth:fix/single-pass-search-replace

Conversation

@rajanpanth

Copy link
Copy Markdown
Contributor

Fixes #192.

@beeman's diagnosis on the issue was right, and the loop is in searchAndReplace itself: replacements are applied sequentially over the accumulated content, so a later pair can rewrite an earlier pair's output.

Renaming the counter template to my-counter passes several name variants, all of which search for counter:

'counter' -> 'my-counter'   content becomes  my-counter
'counter' -> 'mycounter'    the counter inside my-counter matches again -> my-mycounter
'counter' -> 'my_counter'   and again        -> my-mymy_counter

which is exactly the mymymycounter in the report. It is not specific to kebab case or to the counter template: it triggers whenever a replacement value contains another search string.

The general shape is worth calling out too, since it corrupts values that have nothing to do with package names:

searchAndReplace(dir, ['foo', 'bar'], ['bar', 'baz'])   // 'foo' became 'baz', not 'bar'

Fix

All pairs are compiled into one alternation and applied in a single pass, so replaced text is never rescanned. Search strings are sorted longest-first, so the most specific pattern wins (my_counter is preferred over counter), and the first pair for a duplicated search string is the one that applies, matching the previous first-write-wins behavior.

The same replacer now drives path renaming, which had an identical sequential loop and the same bug for file and directory names.

Verification

  • 12 tests in test/search-and-replace.test.ts pass, 4 added: the issue's exact shape, the general rescan case, longest-match preference, and the path-rename path.
  • Reverting search-and-replace.ts alone fails all 4 new tests.
  • pnpm lint and pnpm test:types clean.
  • Full suite is unchanged: 14 failures across 4 files, identical with and without this change (they reproduce on an unmodified checkout here).

Relationship to #286

I have a separate open PR, #286, that dedupes package-name variants in init-script-rename.ts. That reduces how often duplicate pairs are generated, but it does not fix the underlying rescan, which still corrupts any content where one replacement contains another search string. This PR fixes the root cause and is independent; either can land first, and #286 becomes a tidiness improvement rather than a fix.

Replacements ran sequentially over accumulated content, so a later pair
could rewrite an earlier pair's output. Renaming the counter template to
my-counter produced my-mycounter, because the compact name variant then
matched the counter inside the kebab result. All patterns now match in
one pass, longest first, so replaced text is never rescanned. The same
replacer is used for path renames.

Fixes solana-foundation#192
@changeset-bot

changeset-bot Bot commented Aug 23, 2026

Copy link
Copy Markdown

馃 Changeset detected

Latest commit: 0b07569

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
create-solana-dapp Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Aug 23, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/create-solana-dapp@287

commit: 0b07569

@greptile-apps

greptile-apps Bot commented Aug 23, 2026

Copy link
Copy Markdown

Greptile Summary

This PR changes template content and path renaming to apply all literal replacements in one pass, preventing later pairs from rewriting earlier output.

  • Adds a shared replacer with first-pair handling for duplicate search strings and longest-match precedence.
  • Uses the same replacement behavior for file contents and filesystem paths.
  • Adds regression coverage and a patch changeset.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/utils/search-and-replace.ts Introduces a shared single-pass literal replacer and applies it consistently to file contents and path names; no eligible follow-up defect was established.
test/search-and-replace.test.ts Adds focused regression tests for duplicate sources, replacement rescanning, longest-match precedence, and path renaming.
.changeset/single-pass-search-replace.md Records an appropriate patch release for the corrected template-renaming behavior.

Reviews (2): Last reviewed commit: "chore: add changeset for the single-pass..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Creating a template with kebab case and gill adds repetition to the program name

1 participant