Skip to content

fix: apply init-script renames in a single pass per file - #277

Open
beeman wants to merge 1 commit into
mainfrom
beeman/single-pass-init-rename
Open

fix: apply init-script renames in a single pass per file#277
beeman wants to merge 1 commit into
mainfrom
beeman/single-pass-init-rename

Conversation

@beeman

@beeman beeman commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Why

Replacing one search term at a time re-scanned the output of earlier substitutions, so a project name containing a template term compounded on every pass: naming a counter-template project mycountergill produced mymymycountergillgillgill in program names, file names, and the IDL path the setup script then failed to resolve. The same compounding existed across calls — the package.json project rename, each rename entry, and template option renames each walked overlapping trees in sequence, so even a single-pass replacement per call would still re-scan what a previous call wrote.

What

  • searchAndReplace combines all substitutions into one alternation regex (longest match first) and applies them in a single pass over each file's contents and name, so no replacement output is ever re-matched
  • New searchAndReplaceScopes applies multiple rename sources together: substitutions are unioned per file, with the narrowest scope winning on conflicting terms
  • initScriptRename collects the project rename and all rename entries as scopes and applies them in one call
  • initScriptOptions returns its rename scopes instead of applying them, and initScriptRename folds them into the same pass after the entry scopes
  • An unreadable scope root logs and continues instead of aborting the remaining scopes, and dry runs no longer recurse into directories under the paths they only pretended to rename
  • New file-system-level test suite (test/init-script-rename-files.test.ts) reproduces the issue scenario end to end, plus unit tests for compounding, duplicated substitutions, scope precedence, and error isolation

Notes

Verified against the real CLI with live templates using hostile names (my-scaffold-counter on kit/nextjs, counter-scaffold on web3js/web3js-next-tailwind-counter) — no doubling in either. Two known issues are intentionally out of scope: a single lowercase rename term still maps to its kebab form in Rust contexts (pub mod my-counter), and the project-wide rename rewrites the template's own README install command; both predate this change.

Fixes #192, fixes #193

@changeset-bot

changeset-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 60f0db9

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 6, 2026

Copy link
Copy Markdown

Open in StackBlitz

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

commit: 60f0db9

@greptile-apps

greptile-apps Bot commented Aug 6, 2026

Copy link
Copy Markdown

Greptile Summary

This PR applies project, template-entry, and option renames together in one pass per file, preventing replacement output from being processed again.

  • Adds scope-aware substitutions with narrow-scope precedence.
  • Preserves explicitly targeted paths beneath normally excluded directories.
  • Updates init-script orchestration to collect option rename scopes before applying renames.
  • Adds filesystem and unit coverage for compounding, scope precedence, dry runs, and isolated scope errors.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the previously reported excluded-directory regression is fixed, and the earlier commit-signature report was already established as invalid for an SSH-signed commit verified by GitHub.

Important Files Changed

Filename Overview
src/utils/search-and-replace.ts Introduces single-pass, scope-aware replacement and correctly retains explicit scopes beneath excluded directories.
src/utils/init-script-rename.ts Collects project, entry, and option rename scopes and applies them in one combined pass.
src/utils/init-script-options.ts Returns option instructions and deferred rename scopes for combined processing.
src/utils/create-app-task-run-init-script.ts Passes collected option scopes into the unified init-script rename operation.
test/search-and-replace.test.ts Covers single-pass substitutions, precedence, excluded-path targeting, and per-scope error isolation.
test/init-script-rename-files.test.ts Adds filesystem-level regression coverage for project names containing template rename terms.

Reviews (3): Last reviewed commit: "fix: apply init-script renames in a sing..." | Re-trigger Greptile

Comment thread .changeset/single-pass-init-rename.md
Comment thread src/utils/search-and-replace.ts
@beeman
beeman force-pushed the beeman/single-pass-init-rename branch from 6c76af0 to f4c6c54 Compare August 6, 2026 00:28
@beeman
beeman requested a review from dev-jodee August 6, 2026 02:06
Replacing one search term at a time re-scanned the output of earlier substitutions, so a project name containing a template term compounded on every pass: naming a counter-template project `mycountergill` produced `mymymycountergillgillgill` in program names, file names, and the IDL path the setup script then failed to resolve.

`searchAndReplace` now combines all substitutions into one alternation regex (longest match first) and applies them in a single pass over each file's contents and name, so no replacement output is ever re-matched.

The same compounding existed across calls: the package.json project rename, each rename entry, and template option renames each walked overlapping trees in sequence. These are now collected as scopes and applied together through `searchAndReplaceScopes`, which unions the substitutions per file with the narrowest scope winning on conflicting terms.

`initScriptOptions` returns its rename scopes instead of applying them, and `initScriptRename` folds them into the same pass after the entry scopes.

An unreadable scope root now logs and continues instead of aborting the remaining scopes, and dry runs no longer recurse into directories under the paths they only pretended to rename.

Fixes #192, fixes #193
@beeman
beeman force-pushed the beeman/single-pass-init-rename branch from f4c6c54 to 60f0db9 Compare August 7, 2026 13:48
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.

Cannot run pnmp run setup script Creating a template with kebab case and gill adds repetition to the program name

1 participant