Conversation
commit: |
aidenybai
added a commit
that referenced
this pull request
Jun 20, 2026
When `language` is omitted, scramble defaulted to tsx, so plain-TS snippets with value-position generics (`fn<T>()`, `<T,>() => …`) parsed under JSX rules and returned null. Respect an explicit language; with no hint, try tsx then fall back to ts. Addresses Bugbot review on #912.
Rewrites a snippet so every identifier (incl. React APIs, JSX tags, and DOM/a11y attributes) becomes a role-prefixed placeholder applied consistently so aliasing survives, and every literal is blinded. Returns the readable scrambled source plus a stable FNV-1a hash. Parsing respects an explicit `language`; with none it tries tsx then falls back to ts so value-position generics still parse.
- Keep the leading `#` on PrivateIdentifier placeholders (and scope their lookup key) so private fields stay private, re-parse, and never collide with a public name of the same text. - Blind JSXText runs so visible text between tags isn't leaked. - Visit an identifier's children so a typed binding's `typeAnnotation` names are scrambled too.
A single source name can play two roles (e.g. `className` as both a destructured var and a JSX attribute label). Keying the placeholder cache by name alone let the first-seen role win, so structurally identical snippets that differed only in an underlying name scrambled (and hashed) differently, breaking the naming-invariant dedup key. Key by (role, name).
Blanking a TemplateElement used `raw.length`, which can exceed the reported span when the quasi contains escapes — overrunning `span.end` and rewriting unrelated source. Trim the actual delimiter chars off the span ends instead, keeping the blanked region strictly inside the node and escape-safe in both parser modes.
scramble is a react-doctor-specific snippet anonymizer (for telemetry / privacy), not a general-purpose deslop-js capability. Exposing it on the separately-published deslop-js package needlessly widened that package's surface. Relocate it to react-doctor as an internal cli/utils helper alongside the other anonymizers, drop the now-unneeded deslop-js export + changeset, and add oxc-parser as a direct react-doctor dependency. Implementation simplified while preserving identical output/hash: a single local AstNode/Span type, inlined isAstNode guard, no offsetShift round-trip in the template branch, and trimmed comments.
…ot bytes oxc AST spans (and String.slice) are UTF-16 code-unit indices, so the diagnostic offset/length must be too. The prior "byte range" wording invited a caller to pass raw oxlint Diagnostic byte offsets, which would pick the wrong node on non-ASCII source. Document the actual UTF-16 contract.
Drop narration comments that restate the code; keep only the notes that guard real footguns the code can't convey — the UTF-16 offset contract, the tsx→ts parse fallback, oxc's template-span quirk, and the role-keying hash-invariance rationale.
Rename the local AstNode interface (and its isAstNode guard) to OxcAstNode / isOxcAstNode, matching the existing names in deslop-js's oxc-ast-node.ts, and align the field types (start?/end? as number) with that canonical shape.
scramble redeclared OxcAstNode/isOxcAstNode, a near-copy of deslop-js's oxc-ast-node util. Expose that guard + interface from deslop-js's entry (they were already internal) and import them in react-doctor instead of keeping a third copy. No runtime behavior change.
For each scan, scramble a capped, hash-deduped sample of the structural shapes rules fire on (identifiers/literals blinded) and emit them as child spans of the run trace. No real source, names, or paths leave the machine; a no-op when Sentry tracing is off. Promotes core's private getUtf16Offset to a shared utf8OffsetToUtf16 util (reused by resolve-use-call-binding) for the byte->UTF-16 offset conversion the scramble call needs.
…i become facades Move the deslop analysis engine into the private @react-doctor/core package (under src/deslop, exposed via the @react-doctor/core/deslop subpath) so the whole monorepo consumes one source of truth, and reduce deslop-js to a thin re-export facade over it. vp pack still bundles the engine into deslop-js's published dist (CJS + ESM) alongside the sibling parse-worker.mjs, keeping the tarball self-contained; the public API (analyze, defineConfig, isOxcAstNode, every exported type) is unchanged. To keep the turbo build graph acyclic, core no longer declares deslop-js (it only resolves the "deslop-js" specifier at runtime via import.meta.resolve); the packages that run the dead-code path (react-doctor, deslop-cli, api) each depend on deslop-js directly. core's dead-code integration tests now use an in-process worker so they exercise core's own engine instead of the not-yet-built facade. The deslop test suite + fixtures moved into packages/core/tests/deslop and switched from node:test to vite-plus/test.
# Conflicts: # packages/core/tests/deslop/fixtures/dependency-tooling/.gitignore # packages/core/tests/deslop/fixtures/dependency-tooling/node_modules/@babel/cli/package.json # packages/core/tests/deslop/fixtures/dependency-tooling/node_modules/@formatjs/cli/package.json # packages/core/tests/deslop/fixtures/dependency-tooling/node_modules/@tauri-apps/cli/package.json # packages/core/tests/deslop/fixtures/dependency-tooling/node_modules/@tinacms/cli/package.json # packages/core/tests/deslop/fixtures/dependency-tooling/node_modules/chokidar-cli/package.json # packages/core/tests/deslop/fixtures/dependency-tooling/node_modules/jest-cli/package.json # packages/core/tests/deslop/fixtures/dependency-tooling/node_modules/react-chartjs-2/package.json # packages/core/tests/deslop/fixtures/dependency-tooling/node_modules/react-redux/package.json # packages/core/tests/deslop/fixtures/remark-config-deps/.gitignore # packages/core/tests/deslop/fixtures/remark-config-deps/node_modules/remark-cli/package.json # packages/deslop-js/src/index.ts
analyze.test.ts asserts POSIX-relative paths against the deslop engine's raw output, which uses OS-native separators (production normalizes via check-dead-code's toRelativeFilePath). The cases never executed on Windows under deslop-js's `node --test tests/*.test.ts` glob; exclude them on win32, mirroring check-dead-code.test.ts's existing skipIf(win32).
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c674127. Configure here.
scanWorkspaceFull enqueues scans with runDeadCode: true, which routes
through core's checkDeadCode and resolves deslop-js via
import.meta.resolve("deslop-js"). Since core no longer depends on
deslop-js (it resolves the specifier at runtime), each consumer that
triggers dead-code must declare it directly — react-doctor, deslop-cli,
and api already do; this adds the missing dependency to language-server
so full workspace audits resolve the engine under pnpm's strict layout.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Why
Two related strands of work on this branch:
Anonymized diagnostic snippets for telemetry. We want structural insight into which code shapes trigger diagnostics without ever shipping user source.
scrambleis an AST-based anonymizer (oxc) that blinds identifiers, literals, and JSX/text while preserving structure and producing a stable FNV-1a fingerprint, so we can ship a scrambled snippet around each diagnostic to Sentry.One source of truth for the deslop engine. The deslop dead-code/duplication engine lived in the published
deslop-jspackage, but@react-doctor/coreis the diagnostic engine the whole monorepo builds on. This embeds deslop into core and makesdeslop-js/deslop-clithin facades, so everyone consumes core and there's a single engine to maintain — mirroring how react-doctor already houses its internals in core.What changed
Scramble + telemetry
scramble, the AST-based snippet anonymizer, and housed it as an internalreact-doctorutil (scramble-snippet.ts) rather than adeslop-jspublic API.record-diagnostic-snippets.ts: converts oxlint UTF-8 byte offsets → UTF-16, scrambles the minimal AST node at each diagnostic, dedupes by structural hash, caps per scan, and emits one Sentry span per distinct snippet. Wired intoinspect.ts.utf8OffsetToUtf16helper into core and reused it.OxcAstNode/isOxcAstNodeonto deslop-js's canonical definition.Embed the deslop engine in core
deslop-js/src/**→@react-doctor/coresrc/deslop/**, exposed via the@react-doctor/core/deslopsubpath.deslop-js/src/index.tsis nowexport * from "@react-doctor/core/deslop"— public API (analyze,defineConfig,isOxcAstNode, all types) unchanged.vp packbundles the engine intodeslop-js's dist (CJS + ESM) with a siblingparse-worker.mjs, keeping oxc/glob deps external. core emits its owndist/deslop.js+dist/parse-worker.mjs.deslop-js(it only resolves the"deslop-js"specifier at runtime viaimport.meta.resolve);react-doctor,deslop-cli, andapieach depend ondeslop-jsdirectly.in-process-dead-code-worker.ts) that mirrors the production worker's normalization, so they test core's own engine without depending on the not-yet-built facade.packages/core/tests/deslopand switchednode:test→vite-plus/test; scoped vitest to skip the broken-by-design fixture files.Changesets:
scramble-diagnostic-snippets(react-doctor),deslop-export-oxc-ast-node+deslop-engine-in-core(deslop-js).Test plan
pnpm build— turbo graph acyclic; core emitsdeslop.js+parse-worker.mjs; facade emits dual CJS/ESM + its ownparse-worker.mjspnpm typecheck,pnpm lint,pnpm format:check— greenpnpm test— 11/11 packages (core incl. the 506 migrated deslop tests; deslop-cli against the facade; react-doctor e2e)pnpm smoke:json-report— OKoxc-parser/oxc-resolver/fast-glob/minimatch/typescriptstay external in both core'sdeslop.jsand the facade'sindex.cjs/index.mjsunused-file+unused-dependency(facade → bundled engine → spawnedparse-worker.mjs)Note
Medium Risk
Large monorepo packaging refactor (engine ownership, build graph, published bundles) plus new telemetry that touches source files; snippet content is structurally anonymized and gated on Sentry tracing, but any telemetry path warrants careful privacy review.
Overview
Moves the deslop dead-code engine into
@react-doctor/core(src/deslop,@react-doctor/core/deslopexport) and makesdeslop-jsa pure re-export whilevp packstill bundles CJS/ESM plusparse-worker.mjsso the published package stays self-contained.@react-doctor/coredrops its workspace dependency ondeslop-jsand pulls in the former engine deps (oxc-parser,fast-glob, etc.);apiandlanguage-servergain a directdeslop-jsworkspace link to keep the turbo graph acyclic. The deslop test suite and fixtures relocate topackages/core/tests/deslop, with dead-code integration tests using an in-process worker instead of spawning the facade.Adds optional Sentry telemetry for structural diagnostic shapes: new
scramble(oxc AST anonymizer) andrecordDiagnosticSnippets, which convert oxlint UTF-8 byte offsets to UTF-16, scramble the minimal node around each diagnostic, dedupe by hash, cap samples, and emit child spans when tracing is on—wired frominspect.ts. Sharedutf8OffsetToUtf16is extracted in core and reused by oxlint binding resolution.isOxcAstNode/OxcAstNodeare published from the deslop entry for AST walking.Reviewed by Cursor Bugbot for commit 596c0c7. Bugbot is set up for automated code reviews on this repo. Configure here.