Skip to content

fix(init): preserve scope/path separators in project slugs#886

Merged
MathurAditya724 merged 1 commit intomainfrom
fix/slugify-scope-separator
Apr 29, 2026
Merged

fix(init): preserve scope/path separators in project slugs#886
MathurAditya724 merged 1 commit intomainfrom
fix/slugify-scope-separator

Conversation

@MathurAditya724
Copy link
Copy Markdown
Member

Summary

sentry init produces unreadable project slugs for monorepo / npm-scoped
package names. Reported case: `package.json` with `"name": "@t3tools/web"`
created a Sentry project with slug `t3toolsweb` instead of `t3tools-web`.

Root cause

`slugify()` in `src/lib/utils.ts` mirrors Sentry's frontend canonical
implementation
(`static/app/utils/slugify.tsx`),
which strips invalid characters (including `@` and `/`) in a single pass.
For npm scopes and monorepo path segments this destroys structural
information — `/` should be preserved as a hyphen, not silently dropped.

Fix

Normalize `/` and `\` to a space before the invalid-character strip
step. The existing `[-\s]+` collapse rule then folds them into hyphens.

```ts
return name
.normalize("NFKD")
.toLowerCase()
.replace(/[\\/]+/g, " ") // ← new
.replace(/[^a-z0-9_\\s-]/g, "")
.replace(/[-\s]+/g, "-")
.replace(/^-|-$/g, "");
```

Behavior change

Input Before After
`@t3tools/web` `t3toolsweb` `t3tools-web`
`@scope/pkg` `scopepkg` `scope-pkg`
`packages/api` `packagesapi` `packages-api`
`apps\\api` `appsapi` `apps-api`
`@scope/My App` `scopemy-app` `scope-my-app`

All other inputs unchanged:

  • `My Cool App` → `my-cool-app`
  • `Café Project` → `cafe-project`
  • `my_app` → `my_app`
  • `---foo---` → `foo`

Tests

  • `test/lib/utils.test.ts` (new) — unit cases for JSDoc examples, npm
    scopes, monorepo paths, edge cases (empty, all-invalid input,
    leading/trailing separators).
  • `test/lib/utils.property.test.ts` (new) — invariants for any input:
    • Output always matches `/^[a-z0-9_-]*$/`
    • Output never starts/ends with `-`, never contains `--`
    • `slugify(slugify(x)) === slugify(x)` (idempotent)
    • For any `a/b` with both segments alphanumeric, output is exactly `a-b`

26 new tests pass; full suite (6,343 tests) passes; lint clean; typecheck clean.

Note for affected users

Anyone who previously ran `sentry init` against a scoped-package project
got a Sentry project with the broken slug (e.g. `t3toolsweb`). After this
fix lands, a re-run of `sentry init` will:

  1. Compute the new (correct) slug, e.g. `t3tools-web`
  2. Find no existing project with that slug
  3. Create a new project named `@t3tools/web` with slug `t3tools-web`
  4. Leave the old `t3toolsweb` project orphaned

The old project can be deleted via the Sentry UI or
`sentry project delete /t3toolsweb`. We're not adding a fallback
lookup ("if new slug not found, try old slug") because it adds
significant complexity for a small affected user base, and the orphaned
project is easy to clean up manually.

Out of scope

  • Not normalizing other characters (`:`, `.`, `+`) — only `/` and `\`
    are unambiguous structural separators. Targeting just the reported bug.
  • Not changing the function signature — the one-line behavior tweak is
    safer than expanding the API.
  • Not changing the upstream Sentry frontend `slugify` — this is a CLI
    improvement specifically for input shapes (npm scopes, monorepo paths)
    the CLI receives that the web UI typically doesn't.

Reported by users running `sentry init` in monorepos with npm-scoped
package names. The package.json `name` field `@t3tools/web` was being
slugified to `t3toolsweb` (silently mashed) instead of `t3tools-web`.

Root cause: `slugify` mirrored Sentry's frontend canonical implementation
(getsentry/sentry/static/app/utils/slugify.tsx), which strips `@` and
`/` along with other invalid characters in a single pass. For npm scopes
and monorepo path segments this destroys structural information.

Fix: normalize `/` and `\` to a space before the strip step. Existing
collapse rule `[-\s]+` then folds them into hyphens.

  slugify("@t3tools/web")    // before: "t3toolsweb"   now: "t3tools-web"
  slugify("@scope/pkg")      // before: "scopepkg"     now: "scope-pkg"
  slugify("packages/api")    // before: "packagesapi"  now: "packages-api"
  slugify("apps\\api")       // before: "appsapi"      now: "apps-api"

All other inputs unchanged: `My Cool App` → `my-cool-app`, `Café Project`
→ `cafe-project`, `my_app` → `my_app`, etc.

Adds:
- test/lib/utils.test.ts — unit cases for JSDoc examples, npm scopes,
  monorepo paths, edge cases (empty, all-invalid, leading/trailing
  separators)
- test/lib/utils.property.test.ts — invariants for any input: output
  matches /^[a-z0-9_-]*$/, no leading/trailing/consecutive hyphens,
  idempotent, separator-between-segments always becomes a hyphen

Note for users who previously ran `sentry init` against a scoped-package
project: a re-run will produce a new slug (e.g. `t3tools-web`) and
create a new Sentry project. The old `t3toolsweb` project is orphaned
and can be deleted manually via the Sentry UI or `sentry project delete`.
@github-actions
Copy link
Copy Markdown
Contributor

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://cli.sentry.dev/_preview/pr-886/

Built to branch gh-pages at 2026-04-29 19:03 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@github-actions
Copy link
Copy Markdown
Contributor

Codecov Results 📊

6343 passed | Total: 6343 | Pass Rate: 100% | Execution Time: 0ms

📊 Comparison with Base Branch

Metric Change
Total Tests 📈 +26
Passed Tests 📈 +26
Failed Tests
Skipped Tests

All tests are passing successfully.

✅ Patch coverage is 100.00%. Project has 13883 uncovered lines.
❌ Project coverage is 74.87%. Comparing base (base) to head (head).

Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
- Coverage    75.97%    74.87%     -1.1%
==========================================
  Files          294       294         —
  Lines        54449     55248      +799
  Branches         0         0         —
==========================================
+ Hits         41365     41365         —
- Misses       13084     13883      +799
- Partials         0         0         —

Generated by Codecov Action

@MathurAditya724 MathurAditya724 merged commit 6c394d6 into main Apr 29, 2026
23 checks passed
@MathurAditya724 MathurAditya724 deleted the fix/slugify-scope-separator branch April 29, 2026 20:12
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.

1 participant