perf(dashnote): defer SDK value imports in note + contract helpers#77
perf(dashnote): defer SDK value imports in note + contract helpers#77
Conversation
NotesWorkspace, SessionContext, and LoginModal statically imported contract.ts, createNote.ts, and updateNote.ts, each of which pulled Document/DataContract/Identifier from @dashevo/evo-sdk. That anchored the 8 MB SDK chunk to the entry graph via a hoisted static import, so the browser fetched it before first paint despite the modulePreload filter in vite.config.ts. Move the value imports behind a lazy module cache (matching the loginWithPrivateKey pattern in 2b2c5a0) so the chunk loads only when an authenticated write actually needs it. Throttled Lighthouse (Slow 4G + 4× CPU, simulated mobile): LCP 30.8 s → 2.0 s, FCP 16.1 s → 1.7 s, perf score 0.55 → 0.98. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
Warning Rate limit exceeded
To continue reviewing without waiting, purchase usage credits in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThree files in the dashnote example app convert from static imports of evo-sdk symbols (Identifier, DataContract, Document) to lazy dynamic imports via a shared loader pattern. Each file introduces a typed module loader with caching and uses it to defer SDK symbol acquisition until runtime. ChangesLazy SDK Module Loading
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
example-apps/dashnote/src/dash/createNote.ts (1)
9-22: ⚡ Quick winExtract the lazy
loadSdkModuleloader into a shared module to avoid triplicated code.This 12-line loader (type alias + cached promise +
loadSdkModule()) is duplicated verbatim inupdateNote.ts(lines 12–25) andcontract.ts(lines 11–26). Beyond the DRY hit, each file maintains its ownsdkModulePromisecache; since ESimport()is module-graph-cached at runtime they still resolve to the same module instance, but a single shared promise is cleaner and gives one place to evolve retry/log/telemetry behavior as moresrc/dash/*helpers adopt the pattern.♻️ Proposed extraction
Create
example-apps/dashnote/src/dash/sdkModule.ts:// Defer the `@dashevo/evo-sdk` value import so it doesn't anchor the SDK chunk // to the entry graph via files statically imported by SessionContext / // LoginModal / NotesWorkspace. Cached after first call; cleared on failure // so a transient chunk fetch can retry. export type SdkModule = typeof import("@dashevo/evo-sdk"); let sdkModulePromise: Promise<SdkModule> | null = null; export function loadSdkModule(): Promise<SdkModule> { if (!sdkModulePromise) { sdkModulePromise = import("@dashevo/evo-sdk").catch((err) => { sdkModulePromise = null; throw err; }); } return sdkModulePromise; }Then in each consumer:
-// Defer the `@dashevo/evo-sdk` value import so it doesn't anchor the SDK chunk -// to the entry graph via NotesWorkspace's static import of this file. Cached -// after first call; cleared on failure so a transient chunk fetch can retry. -type SdkModule = typeof import("@dashevo/evo-sdk"); -let sdkModulePromise: Promise<SdkModule> | null = null; -function loadSdkModule(): Promise<SdkModule> { - if (!sdkModulePromise) { - sdkModulePromise = import("@dashevo/evo-sdk").catch((err) => { - sdkModulePromise = null; - throw err; - }); - } - return sdkModulePromise; -} +import { loadSdkModule } from "./sdkModule";As per coding guidelines: "Files in src/dash/ must reference
@dashevo/evo-sdkor shared core re-exports/SDK-shape types" — a sharedsdkModule.tsneighbor insrc/dash/fits cleanly within that boundary.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@example-apps/dashnote/src/dash/createNote.ts` around lines 9 - 22, Extract the duplicated loader into a single shared module by creating a new src/dash/sdkModule.ts that exports the SdkModule type, the cached sdkModulePromise, and the loadSdkModule() function (which sets sdkModulePromise, imports "@dashevo/evo-sdk", clears the cache on failure and rethrows); then remove the local declarations of SdkModule, sdkModulePromise and loadSdkModule from createNote.ts, updateNote.ts and contract.ts and replace them with imports from './sdkModule' (e.g. import { loadSdkModule, SdkModule } from './sdkModule') so all three consumers use the single shared loader and cache.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@example-apps/dashnote/src/dash/createNote.ts`:
- Around line 9-22: Extract the duplicated loader into a single shared module by
creating a new src/dash/sdkModule.ts that exports the SdkModule type, the cached
sdkModulePromise, and the loadSdkModule() function (which sets sdkModulePromise,
imports "@dashevo/evo-sdk", clears the cache on failure and rethrows); then
remove the local declarations of SdkModule, sdkModulePromise and loadSdkModule
from createNote.ts, updateNote.ts and contract.ts and replace them with imports
from './sdkModule' (e.g. import { loadSdkModule, SdkModule } from './sdkModule')
so all three consumers use the single shared loader and cache.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: df9f4fac-9766-4181-9c34-8a2f5e6c9e0b
📒 Files selected for processing (3)
example-apps/dashnote/src/dash/contract.tsexample-apps/dashnote/src/dash/createNote.tsexample-apps/dashnote/src/dash/updateNote.ts
Pull the duplicated `loadSdkModule` helper (type alias + cached promise + loader function with retry-on-failure) out of contract.ts, createNote.ts, and updateNote.ts into a single src/dash/sdkModule.ts, and have each consumer import it. No behavior change — ESM module-graph caching makes the previous three private promises functionally equivalent to one shared promise. Tutorial files now show only their Dash SDK recipe without the bundler-deferral plumbing. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Summary
contract.ts,createNote.ts, andupdateNote.tshad top-levelimport { Document, DataContract, Identifier } from "@dashevo/evo-sdk". Reachable from the entry graph viaSessionContext,LoginModal, andNotesWorkspace, this anchored the 8 MB SDK chunk to the boot critical path with a hoisted static import — themodulePreloadfilter invite.config.tsonly suppressed the<link>tag, not the dependency edge.loginWithPrivateKeypattern from feat(dashnote): preview identity + key-type fitness on WIF paste #75 / 2b2c5a0). The synchronous exports ofcontract.ts(NOTE_SCHEMAS,loadStoredContractId, etc.) stay sync sinceSessionContextcalls them during initial render.dist/assets/index-*.jsno longer containsfrom "./evo-sdk.module-*.js"; only three dynamicimport()calls remain.Throttled Lighthouse (Slow 4G + 4× CPU, simulated mobile)
Total bytes transferred is unchanged — the win isn't fewer bytes, it's that those bytes no longer block first paint. The SDK now downloads only when an authenticated path (login, save note, register contract, switch contract ID) actually needs it.
Summary by CodeRabbit