diff --git a/scripts/ruby/rename.rb b/scripts/ruby/rename.rb index 31e3875..0b2e61c 100755 --- a/scripts/ruby/rename.rb +++ b/scripts/ruby/rename.rb @@ -56,6 +56,10 @@ def build_patterns(from, to) to_snake_pl = pluralize(to_snake) from_pl = pluralize(from) to_pl = pluralize(to) + from_flat = from.downcase + to_flat = to.downcase + from_flat_pl = from_flat + "s" + to_flat_pl = to_flat + "s" # Ruby's \b treats `_` as a word char, so \bshop\b doesn't fire # inside `shop_id` or `accounts_shopkeeper`. Hand-rolled boundaries: @@ -78,6 +82,12 @@ def build_patterns(from, to) [/#{pascal_l}#{Regexp.escape(from)}#{pascal_r}/, to], [/#{snake_l}#{Regexp.escape(from_snake_pl)}#{snake_r}/, to_snake_pl], [/#{snake_l}#{Regexp.escape(from_snake)}#{snake_r}/, to_snake], + # flat-lowercase (runs AFTER snake so single-word rename pairs + # where flat == snake can't win over snake's replacement; catches + # multi-word compounds collapsed in URLs / package names / + # email addresses, e.g. `nativeapptemplate.com`). + [/#{snake_l}#{Regexp.escape(from_flat_pl)}#{snake_r}/, to_flat_pl], + [/#{snake_l}#{Regexp.escape(from_flat)}#{snake_r}/, to_flat], [/#{snake_l}#{Regexp.escape(from_pl.upcase)}(?![A-Za-z])/, to_pl.upcase], [/#{snake_l}#{Regexp.escape(from.upcase)}(?![A-Za-z])/, to.upcase], ] diff --git a/src/agents/workers/rails.ts b/src/agents/workers/rails.ts index 1e0b300..bc137bf 100644 --- a/src/agents/workers/rails.ts +++ b/src/agents/workers/rails.ts @@ -31,12 +31,14 @@ export async function runRailsWorker(domain: DomainSpec): Promise await prepareFresh(outDir); await copyFiltered(substrate, outDir); - const plan = domain.renamePlan.map((p) => `${p.from}->${p.to}`).join(", "); + const productPairs = buildProductRenamePairs(domain.slug); + const renamePlan: readonly RenamePair[] = [...productPairs, ...domain.renamePlan]; + const plan = renamePlan.map((p) => `${p.from}->${p.to}`).join(", "); trace("rails", `running scripts/ruby/rename.rb: ${plan}`); const renameStats = await runRuby<{ renamePlan: readonly RenamePair[]; root: string }, RenameStats>( "rename.rb", - { renamePlan: domain.renamePlan, root: outDir }, + { renamePlan, root: outDir }, ); trace( @@ -54,6 +56,22 @@ export async function runRailsWorker(domain: DomainSpec): Promise }; } +function slugToPascal(slug: string): string { + return slug + .split(/[-_]/) + .filter(Boolean) + .map((s) => s.charAt(0).toUpperCase() + s.slice(1)) + .join(""); +} + +function buildProductRenamePairs(slug: string): readonly RenamePair[] { + const pascal = slugToPascal(slug); + return [ + { from: "Nativeapptemplateapi", to: `${pascal}Api` }, + { from: "NativeAppTemplate", to: pascal }, + ]; +} + async function prepareFresh(dir: string): Promise { try { await stat(dir);