Skip to content

Fix diagnostic snippet source path resolution#19

Merged
warunalakshitha merged 2 commits intoballerina-platform:mainfrom
snelusha:fix/package-diagnostics
Apr 1, 2026
Merged

Fix diagnostic snippet source path resolution#19
warunalakshitha merged 2 commits intoballerina-platform:mainfrom
snelusha:fix/package-diagnostics

Conversation

@snelusha
Copy link
Copy Markdown
Contributor

@snelusha snelusha commented Mar 28, 2026

Purpose

$subject

Changes

This pull request addresses error handling and path resolution issues in diagnostic snippet processing across two key areas:

Web Store (File Tree)

  • Enhanced error handling in the loadSharedFiles function by wrapping shared tree grafting operations in try/catch logic
  • Changed behavior to gracefully handle failures by returning a loaded: false state instead of throwing exceptions, improving application stability

Diagnostics Printer (Go)

  • Introduced a snippetSourcePath utility function to properly resolve the actual file paths for reading diagnostic snippets
  • Updated the snippet reading logic to use intelligent path resolution that accounts for whether the path is a file, directory, or project reference
  • This ensures diagnostic snippets are read from the correct location based on the provided context

Together, these changes improve the reliability of diagnostic snippet retrieval by implementing proper error handling and fixing the source path resolution logic.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

These changes add error handling to shared file loading and introduce a helper function for resolving source file paths in diagnostics printing. The file-tree store now gracefully handles failures when loading shared files, while the diagnostics printer uses a new utility to determine the correct source file path based on available context.

Changes

Cohort / File(s) Summary
File Tree Error Handling
apps/web/src/stores/file-tree-store.ts
Wrapped graftSharedTree() and tree refresh logic in try/catch to handle exceptions gracefully, returning { loaded: false, openPath: null } on failure instead of throwing.
Diagnostics Path Resolution
packages/wasm/diagnostics_printer.go
Introduced snippetSourcePath() helper function to resolve the actual source file path based on whether the provided path is a file or directory, and updated printSourceSnippet() to use this helper for file lookups.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A rabbit hops through error paths with care,
Where trees now catch what once would fall somewhere.
New helpers guide the diagnostics way,
Through files and paths, we safely play.
Thump thump!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is largely incomplete, providing only a minimal 'Purpose' section with a single sentence while omitting most required template sections such as Goals, Approach, User stories, Release notes, Documentation, Testing, and Security checks. Expand the description to include at least Goals, Approach, Release notes, and relevant test coverage details. Explain the problem being solved and how the changes fix the diagnostic snippet path resolution issue.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix diagnostic snippet source path resolution' is clear and directly relates to the main changes in the pull request, particularly the Go diagnostic changes that introduce source path resolution.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (8)
apps/web/src/styles.css (1)

153-160: Redundant font-sans on both * and body.

The font-sans is now applied to both the universal * selector (Line 155) and body (Line 158). The * selector already covers body, making the body declaration redundant. Consider removing font-sans from one location.

Option: Remove from body if keeping on *
 body {
-    `@apply` font-sans bg-background text-foreground;
+    `@apply` bg-background text-foreground;
 }

Note: Using * for font-family is more explicit but less performant than relying on inheritance from html or body. If all children should inherit the font, applying to body alone and relying on CSS inheritance is generally preferred.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/styles.css` around lines 153 - 160, The CSS repeats font-sans on
both the universal selector (*) and body inside the `@layer` base block; remove
the redundant font-sans from one place—preferably delete font-sans from the *
rule (or alternatively remove it from body) so only a single declaration applies
(refer to the `@layer` base block and the selectors "*" and "body" to locate the
change).
packages/wasm/package.json (1)

4-7: Add directory creation to build script to ensure dist exists before compilation.

The go build -o dist/ballerina.wasm command will fail if the dist directory doesn't exist. Since clean removes this directory, explicitly create it during the build:

Proposed fix
 "scripts": {
     "clean": "rm -rf dist",
-    "build": "GOOS=js GOARCH=wasm go build -o dist/ballerina.wasm ."
+    "build": "mkdir -p dist && GOOS=js GOARCH=wasm go build -o dist/ballerina.wasm ."
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/wasm/package.json` around lines 4 - 7, Update the "build" script in
package.json to ensure the dist directory is created before running the Go
build: modify the scripts.build entry (paired with the existing "clean" script)
so it creates the dist directory if missing and then runs GOOS=js GOARCH=wasm go
build -o dist/ballerina.wasm .; this guarantees the output path exists and
prevents build failures when "clean" removed dist.
apps/web/package.json (1)

9-9: Consider cross-platform compatibility for the copy script.

The cp command is Unix-specific and won't work on Windows. Since Turbo manages this task via apps/web/turbo.json and CI runs on Ubuntu, this is acceptable if Windows development isn't a requirement. However, for broader compatibility, consider using a cross-platform alternative.

♻️ Optional: Cross-platform alternative using Node.js built-in
-"copy:wasm": "cp ../../packages/wasm/dist/ballerina.wasm public/ballerina.wasm"
+"copy:wasm": "node -e \"require('fs').cpSync('../../packages/wasm/dist/ballerina.wasm', 'public/ballerina.wasm')\""

Or add a small copy script using shx or similar cross-platform tool.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/package.json` at line 9, The "copy:wasm" npm script uses the
Unix-only `cp` command; replace it with a cross-platform approach by either (a)
installing a cross-platform CLI like "shx" and updating the "copy:wasm" script
to use "shx cp ../../packages/wasm/dist/ballerina.wasm public/ballerina.wasm",
or (b) implement a tiny Node-based copy script (e.g., a script file referenced
by the "copy:wasm" package.json script that uses fs.copyFileSync) and update the
"copy:wasm" script to call that file; update package.json to add the new
devDependency or script entry and ensure turbo.json/task invocation still points
to the "copy:wasm" script name.
apps/web/src/components/ui/checkbox.tsx (1)

8-26: Consider forwarding ref for focus management.

The Checkbox component doesn't forward a ref, which may limit programmatic focus control or integration with form libraries that need ref access to the underlying checkbox element. In React 19, ref is a regular prop, so you can simply destructure and pass it.

♻️ Optional: forward ref to the primitive
-function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
+function Checkbox({ className, ref, ...props }: CheckboxPrimitive.Root.Props) {
 	return (
 		<CheckboxPrimitive.Root
+			ref={ref}
 			data-slot="checkbox"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/checkbox.tsx` around lines 8 - 26, The Checkbox
component should forward its ref so consumers can programmatically focus the
underlying DOM node; update the Checkbox declaration to use React.forwardRef
(keep the same props shape) so it accepts a ref param and pass that ref into
<CheckboxPrimitive.Root ref={ref} ...>, ensuring the ref type matches the
primitive root element (use the appropriate React/CheckboxPrimitive Root
element/ref type); keep all existing props spread and className handling and
leave CheckboxPrimitive.Indicator and HugeiconsIcon unchanged.
apps/web/src/hooks/use-share.ts (1)

33-37: Resetting processed.current when share is absent may cause re-processing.

When share becomes falsy (e.g., after dropShareParam()), this code sets processed.current = null. However, if the component re-renders before the navigation completes and share is still present momentarily, this could cause the same share to be processed again.

Consider only resetting when share is absent and not when !ready:

Proposed fix
 React.useEffect(() => {
-	if (!ready || !share) {
-		processed.current = null;
+	if (!ready) return;
+	if (!share) {
+		if (processed.current !== null) processed.current = null;
 		return;
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/hooks/use-share.ts` around lines 33 - 37, The effect in
use-share currently resets processed.current whenever either !ready or !share,
which can cause re-processing; update the React.useEffect in use-share to only
clear processed.current when share is falsy (e.g., after dropShareParam()) and
avoid clearing on !ready — e.g., check share first and if (!share) set
processed.current = null and return, then separately return early on !ready so
the same share isn't nulled while navigation is pending; modify the effect
around processed.current, ready, and share in useShare hook accordingly.
apps/web/src/components/ui/sonner.tsx (1)

58-65: Consider using a valid CSS value for --border-radius.

The value "none" is a string, which may not be the intended CSS value for border-radius. If you want no border radius, use "0" instead. The string "none" is not a valid CSS <length> value for border-radius.

Proposed fix
 style={
   {
     "--normal-bg": "var(--popover)",
     "--normal-text": "var(--popover-foreground)",
     "--normal-border": "var(--border)",
-    "--border-radius": "none",
+    "--border-radius": "0",
   } as React.CSSProperties
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/sonner.tsx` around lines 58 - 65, The inline style
for the Sonner UI uses an invalid CSS value for border-radius ("none"); update
the style object in the component that sets "--border-radius" (the style passed
to the Sonner/toast root in apps/web/src/components/ui/sonner.tsx) to use a
valid CSS length such as "0" or "0px" instead of "none" and keep the cast to
React.CSSProperties intact so the property remains typed correctly.
apps/web/src/lib/file-tree-utils.ts (1)

18-21: Minor: Redundant kind check.

The dir.kind !== "dir" check on line 20 is redundant since the find predicate on line 19 already ensures n.kind === "dir". However, TypeScript may need this for type narrowing, so it's acceptable to keep.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/file-tree-utils.ts` around lines 18 - 21, The code performs
a redundant runtime check `dir.kind !== "dir"` after using find with `n.kind ===
"dir"`, so replace the find call with a proper type predicate to satisfy
TypeScript and then remove the second check: change the predicate to something
like `nodes.find((n): n is Dir => n.kind === "dir" && n.name === seg)` (use your
Dir type), then you can safely remove `dir.kind !== "dir'` and keep using
`dir.children` and `nodes = dir.children ?? [];` referencing `segments`,
`nodes`, and `dir`.
apps/web/src/lib/share.ts (1)

15-23: Await the stream writer promises instead of discarding them.

writer.write() and writer.close() return promises per the Streams API spec. Using void here bypasses these operations and can lead to unhandled errors. Awaiting them ensures proper error handling and ordering—errors from the writable side are surfaced before consuming the readable stream.

♻️ Suggested change
 async function compressAndEncode(text: string): Promise<string> {
 	const bytes = new TextEncoder().encode(text);
 	const stream = new CompressionStream("gzip");
 	const writer = stream.writable.getWriter();
-	void writer.write(bytes);
-	void writer.close();
+	await writer.write(bytes);
+	await writer.close();
 	const compressed = new Uint8Array(
 		await new Response(stream.readable).arrayBuffer(),
 	);
@@
 async function decodeAndDecompress(b64: string): Promise<string> {
 	const bytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
 	const stream = new DecompressionStream("gzip");
 	const writer = stream.writable.getWriter();
-	void writer.write(bytes);
-	void writer.close();
+	await writer.write(bytes);
+	await writer.close();
 	const decompressed = await new Response(stream.readable).arrayBuffer();
 	return new TextDecoder().decode(decompressed);
 }

Also applies to: lines 33-40

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/share.ts` around lines 15 - 23, In compressAndEncode, don't
discard the promises from stream.writable.getWriter(); await the
writer.write(bytes) and await writer.close() so write/close errors surface and
ordering is correct; update the same pattern in the other compression block in
this file (the second occurrence of writer.write/writer.close) to await those
promises as well and keep using the resulting
Response(stream.readable).arrayBuffer() only after the writer has resolved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/biome.json`:
- Around line 5-13: Replace the accidental re-include pattern "!!dist/" in the
"includes" array with the exclusion pattern "!dist/" so the build output
directory is excluded from Biome checks; update the includes entry that
currently reads "!!dist/" to "!dist/" in the includes array to prevent
linting/formatting from processing dist/.

In `@apps/web/src/components/app-sidebar.tsx`:
- Around line 381-382: The check noLocalFiles && index === 0 inside
localTree.map is unreachable because localTree.map only runs when
localTree.length > 0; instead compute a flag like shouldAutoExpandFirst =
localTree.length === 0 && remoteTree.length > 0 and use that flag when rendering
the remoteTree (or whichever map runs when localTree is empty) so the first
visible entry can auto-expand; update both occurrences that currently use
noLocalFiles (the localTree.map block and the similar block around the other
occurrence) to reference shouldAutoExpandFirst and use index === 0 there.

In `@apps/web/src/hooks/use-share.ts`:
- Around line 43-65: The promise returned by decodeSharePayload(share) lacks
rejection handling; update the call site to add a .catch handler (or use
try/catch with async/await) for decodeSharePayload to handle thrown errors,
clear processed.current, call dropShareParam(), and surface the error (e.g.,
toast.error or processLogger) so rejections don't go unhandled; keep existing
success-path logic that calls loadSharedFiles(payload.root,
payload.openRelativePath), uses processed.current = loaded ? share : null,
toasts on failure, calls openFile(openPath) and showNotice() when appropriate,
and ensure dropShareParam() always runs even on error.

In `@apps/web/src/lib/fs/fs-roots.ts`:
- Around line 1-7: fs-roots.ts currently imports join from path-utils which in
turn imports LOCAL_ROOT/SHARED_ROOT creating a circular dependency; fix it by
removing the dependency on join in fs-roots.ts and either inline the simple join
logic (e.g., concatenate with "/") when computing EXAMPLES_ROOT and SHARED_ROOT
or move join into a new low-level utility module that both fs-roots.ts and
path-utils.ts can import; update the definitions of EXAMPLES_ROOT and
SHARED_ROOT (which use TEMP_ROOT) to use the inlined/relocated join so they no
longer import join from path-utils and break the cycle.

In `@apps/web/src/routes/__root.tsx`:
- Around line 12-15: ThemeProvider is currently rendered without configuring the
theme attribute, but your Tailwind dark styles expect a .dark class; update the
ThemeProvider usage to include attribute="class" so next-themes toggles the
.dark class (modify the ThemeProvider element that wraps Outlet and Toaster).
Ensure you pass attribute="class" on the ThemeProvider component so class-based
dark mode is applied.

In `@packages/wasm/diagnostics_printer.go`:
- Line 25: The code uses filepath.Join to build paths for io/fs reads (e.g., the
call to filepath.Join at diagnostics_printer.go line where the FS read occurs);
change that call to path.Join so the logical FS path uses forward slashes
(import the "path" package and remove or keep "path/filepath" as needed),
ensuring all fs.FS ReadFile/Open calls use path.Join instead of filepath.Join to
avoid OS-specific separators breaking reads on Windows.

---

Nitpick comments:
In `@apps/web/package.json`:
- Line 9: The "copy:wasm" npm script uses the Unix-only `cp` command; replace it
with a cross-platform approach by either (a) installing a cross-platform CLI
like "shx" and updating the "copy:wasm" script to use "shx cp
../../packages/wasm/dist/ballerina.wasm public/ballerina.wasm", or (b) implement
a tiny Node-based copy script (e.g., a script file referenced by the "copy:wasm"
package.json script that uses fs.copyFileSync) and update the "copy:wasm" script
to call that file; update package.json to add the new devDependency or script
entry and ensure turbo.json/task invocation still points to the "copy:wasm"
script name.

In `@apps/web/src/components/ui/checkbox.tsx`:
- Around line 8-26: The Checkbox component should forward its ref so consumers
can programmatically focus the underlying DOM node; update the Checkbox
declaration to use React.forwardRef (keep the same props shape) so it accepts a
ref param and pass that ref into <CheckboxPrimitive.Root ref={ref} ...>,
ensuring the ref type matches the primitive root element (use the appropriate
React/CheckboxPrimitive Root element/ref type); keep all existing props spread
and className handling and leave CheckboxPrimitive.Indicator and HugeiconsIcon
unchanged.

In `@apps/web/src/components/ui/sonner.tsx`:
- Around line 58-65: The inline style for the Sonner UI uses an invalid CSS
value for border-radius ("none"); update the style object in the component that
sets "--border-radius" (the style passed to the Sonner/toast root in
apps/web/src/components/ui/sonner.tsx) to use a valid CSS length such as "0" or
"0px" instead of "none" and keep the cast to React.CSSProperties intact so the
property remains typed correctly.

In `@apps/web/src/hooks/use-share.ts`:
- Around line 33-37: The effect in use-share currently resets processed.current
whenever either !ready or !share, which can cause re-processing; update the
React.useEffect in use-share to only clear processed.current when share is falsy
(e.g., after dropShareParam()) and avoid clearing on !ready — e.g., check share
first and if (!share) set processed.current = null and return, then separately
return early on !ready so the same share isn't nulled while navigation is
pending; modify the effect around processed.current, ready, and share in
useShare hook accordingly.

In `@apps/web/src/lib/file-tree-utils.ts`:
- Around line 18-21: The code performs a redundant runtime check `dir.kind !==
"dir"` after using find with `n.kind === "dir"`, so replace the find call with a
proper type predicate to satisfy TypeScript and then remove the second check:
change the predicate to something like `nodes.find((n): n is Dir => n.kind ===
"dir" && n.name === seg)` (use your Dir type), then you can safely remove
`dir.kind !== "dir'` and keep using `dir.children` and `nodes = dir.children ??
[];` referencing `segments`, `nodes`, and `dir`.

In `@apps/web/src/lib/share.ts`:
- Around line 15-23: In compressAndEncode, don't discard the promises from
stream.writable.getWriter(); await the writer.write(bytes) and await
writer.close() so write/close errors surface and ordering is correct; update the
same pattern in the other compression block in this file (the second occurrence
of writer.write/writer.close) to await those promises as well and keep using the
resulting Response(stream.readable).arrayBuffer() only after the writer has
resolved.

In `@apps/web/src/styles.css`:
- Around line 153-160: The CSS repeats font-sans on both the universal selector
(*) and body inside the `@layer` base block; remove the redundant font-sans from
one place—preferably delete font-sans from the * rule (or alternatively remove
it from body) so only a single declaration applies (refer to the `@layer` base
block and the selectors "*" and "body" to locate the change).

In `@packages/wasm/package.json`:
- Around line 4-7: Update the "build" script in package.json to ensure the dist
directory is created before running the Go build: modify the scripts.build entry
(paired with the existing "clean" script) so it creates the dist directory if
missing and then runs GOOS=js GOARCH=wasm go build -o dist/ballerina.wasm .;
this guarantees the output path exists and prevents build failures when "clean"
removed dist.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5e5a77c6-f237-4bf6-a53e-3cb683614424

📥 Commits

Reviewing files that changed from the base of the PR and between 60a8d52 and f91f6a9.

⛔ Files ignored due to path filters (3)
  • apps/web/public/ballerina.svg is excluded by !**/*.svg
  • bun.lock is excluded by !**/*.lock
  • packages/wasm/go.sum is excluded by !**/*.sum
📒 Files selected for processing (80)
  • .github/workflows/ci.yml
  • .github/workflows/deploy.yml
  • .gitignore
  • .gitmodules
  • apps/web/.gitignore
  • apps/web/biome.json
  • apps/web/components.json
  • apps/web/index.html
  • apps/web/package.json
  • apps/web/src/assets/examples.json
  • apps/web/src/components/ansi.tsx
  • apps/web/src/components/app-sidebar.tsx
  • apps/web/src/components/code-editor.tsx
  • apps/web/src/components/editor.tsx
  • apps/web/src/components/file-route-sync.tsx
  • apps/web/src/components/file-tree-dialog.tsx
  • apps/web/src/components/share-notice-dialog.tsx
  • apps/web/src/components/ui/button.tsx
  • apps/web/src/components/ui/checkbox.tsx
  • apps/web/src/components/ui/collapsible.tsx
  • apps/web/src/components/ui/dialog.tsx
  • apps/web/src/components/ui/dropdown-menu.tsx
  • apps/web/src/components/ui/input.tsx
  • apps/web/src/components/ui/progress.tsx
  • apps/web/src/components/ui/separator.tsx
  • apps/web/src/components/ui/sheet.tsx
  • apps/web/src/components/ui/sidebar.tsx
  • apps/web/src/components/ui/skeleton.tsx
  • apps/web/src/components/ui/sonner.tsx
  • apps/web/src/components/ui/tooltip.tsx
  • apps/web/src/hooks/use-ballerina.ts
  • apps/web/src/hooks/use-copy-share-link.ts
  • apps/web/src/hooks/use-mobile.ts
  • apps/web/src/hooks/use-share-notice.ts
  • apps/web/src/hooks/use-share.ts
  • apps/web/src/lib/file-tree-utils.ts
  • apps/web/src/lib/fs/core/abstract-fs.ts
  • apps/web/src/lib/fs/core/file-node-utils.ts
  • apps/web/src/lib/fs/core/file-node.types.ts
  • apps/web/src/lib/fs/core/fs.interface.ts
  • apps/web/src/lib/fs/core/path-utils.ts
  • apps/web/src/lib/fs/ephemeral-fs.ts
  • apps/web/src/lib/fs/fs-roots.ts
  • apps/web/src/lib/fs/layered-fs.ts
  • apps/web/src/lib/fs/local-storage-fs.ts
  • apps/web/src/lib/router-utils.ts
  • apps/web/src/lib/share.ts
  • apps/web/src/lib/utils.ts
  • apps/web/src/main.tsx
  • apps/web/src/providers/fs-provider.tsx
  • apps/web/src/routeTree.gen.ts
  • apps/web/src/routes/$.tsx
  • apps/web/src/routes/__root.tsx
  • apps/web/src/stores/editor-store.ts
  • apps/web/src/stores/file-tree-store.ts
  • apps/web/src/styles.css
  • apps/web/src/types/wasm-types.d.ts
  • apps/web/src/wasm_exec.js
  • apps/web/tsconfig.app.json
  • apps/web/tsconfig.json
  • apps/web/tsconfig.node.json
  • apps/web/turbo.json
  • apps/web/vite.config.ts
  • biome.json
  • package.json
  • packages/wasm/.gitignore
  • packages/wasm/ballerina-lang-go
  • packages/wasm/biome.json
  • packages/wasm/diagnostics_printer.go
  • packages/wasm/go.mod
  • packages/wasm/local_storage_fs.go
  • packages/wasm/main_wasm.go
  • packages/wasm/package.json
  • packages/wasm/turbo.json
  • scripts/example_gen/main.go
  • turbo.json
  • web/src/assets/examples.json
  • web/src/components/file-route-sync.tsx
  • web/src/lib/fs/core/path-utils.ts
  • web/src/routes/__root.tsx
💤 Files with no reviewable changes (5)
  • biome.json
  • web/src/routes/__root.tsx
  • web/src/assets/examples.json
  • web/src/components/file-route-sync.tsx
  • web/src/lib/fs/core/path-utils.ts

@snelusha snelusha force-pushed the fix/package-diagnostics branch 2 times, most recently from e46907a to c771f5f Compare March 29, 2026 18:13
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
apps/web/src/components/share-notice-dialog.tsx (1)

53-55: Avoid remounting Dialog via dynamic key.

key={String(open)} remounts the whole dialog on every toggle. Prefer resetting permanent explicitly when opening, while keeping the dialog instance stable.

♻️ Proposed refactor
 export function ShareNoticeDialog({
 	open,
 	onDismiss,
 	onDismissPermanently,
 }: ShareNoticeDialogProps) {
 	const [permanent, setPermanent] = React.useState(false);
+	React.useEffect(() => {
+		if (open) setPermanent(false);
+	}, [open]);

 	const handleDismiss = () =>
 		permanent ? onDismissPermanently() : onDismiss();

 	return (
 		<Dialog
-			key={String(open)}
 			open={open}
 			onOpenChange={(next) => !next && handleDismiss()}
 		>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/share-notice-dialog.tsx` around lines 53 - 55, Remove
the dynamic key on the Dialog (delete key={String(open)}) so the Dialog instance
isn't remounted on every toggle; instead, keep the Dialog stable and explicitly
reset your "permanent" state when the dialog is opened inside the existing
onOpenChange handler (use the setter for your permanent state—e.g.,
setPermanent(false) or setIsPermanent(false) when next is true), and keep the
existing onOpenChange={(next) => !next && handleDismiss()} logic for closing;
this preserves component instance stability while still resetting the permanent
flag when opening.
apps/web/src/lib/fs/core/file-node-utils.ts (1)

13-38: Consider using join() for path construction consistency.

Line 34 uses template literal ${path}/${entry.name} for path construction, while other parts of the codebase (including this file at lines 47, 54-55) use the join() utility. Using join() consistently ensures proper handling of edge cases like trailing slashes.

♻️ Suggested change
 	const children = (fs.readDir(path) ?? [])
-		.map((entry) => toFileNode(fs, `${path}/${entry.name}`))
+		.map((entry) => toFileNode(fs, join(path, entry.name)))
 		.filter((n): n is FileNode => n !== null);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/fs/core/file-node-utils.ts` around lines 13 - 38, The path
concatenation in toFileNode uses a template literal `${path}/${entry.name}`
which can mishandle trailing slashes; replace that construction with the
project's path join utility (e.g., join(path, entry.name)) when calling
toFileNode recursively, and add or reuse the join import if it's not already
imported so toFileNode consistently uses join for all path assembly.
apps/web/src/lib/share.ts (1)

81-106: Consider adding a maximum payload size check.

The decoder accepts arbitrarily large payloads. While toFileNode (used during encoding) has no size limits either, extremely large shared trees could:

  1. Exceed URL length limits (~2000 chars for some browsers)
  2. Cause memory pressure during decode

This is a minor concern since the playground likely has practical limits on file tree size, but a sanity check could prevent abuse.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/share.ts` around lines 81 - 106, The decoder
decodeSharePayload currently accepts arbitrarily large decoded JSON from
decodeAndDecompress; add a sanity check after decoding (inside
decodeSharePayload, right after const json = await decodeAndDecompress(encoded))
to enforce a maximum decoded payload size (e.g. define a
MAX_DECODED_PAYLOAD_BYTES constant) and return null if json.length exceeds it
(or trim/reject before JSON.parse); this protects against extremely large
payloads and memory/URL limits while preserving existing parsing logic in the
isFileNode / root handling paths.
apps/web/src/lib/fs/core/path-utils.ts (1)

24-33: The join() function doesn't normalize . or .. segments.

The join() function only filters out empty segments but doesn't handle . (current directory) or .. (parent directory) segments. While callers like toFileNode and isSafeRelativePath guard against these, any direct use of join() with user input containing these segments could produce unexpected paths.

This is acceptable if all callers validate inputs beforehand, which appears to be the case in this PR.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/fs/core/path-utils.ts` around lines 24 - 33, The join(...)
implementation should normalize "." and ".." segments so callers can't
accidentally get unexpected paths; update the join function to split segments,
iterate with a stack: ignore "." entries, pop the stack for ".." (but if stack
is empty and the path is absolute keep it at root, or for relative allow leading
".." entries), push normal segment names, and then rebuild the path preserving
the leading "/" if segments[0] started with "/" — adjust join to perform this
normalization so usages like toFileNode and isSafeRelativePath (and any direct
callers) receive normalized paths.
apps/web/src/components/ui/sonner.tsx (1)

11-11: Import CSSProperties explicitly for clarity and maintainability.

While React.CSSProperties works due to implicit type availability from Vite's React plugin, explicit imports make dependencies clear and improve maintainability. Additionally, "--border-radius": "none" is not a valid CSS value; border-radius expects length/percentage values or the keyword none, not the string "none". Use "0px" instead.

Suggested change
 import { Toaster as Sonner } from "sonner";
+import type { CSSProperties } from "react";
 import { HugeiconsIcon } from "@hugeicons/react";
@@
-			style={
-				{
-					"--normal-bg": "var(--popover)",
-					"--normal-text": "var(--popover-foreground)",
-					"--normal-border": "var(--border)",
-					"--border-radius": "none",
-				} as React.CSSProperties
-			}
+			style={
+				{
+					"--normal-bg": "var(--popover)",
+					"--normal-text": "var(--popover-foreground)",
+					"--normal-border": "var(--border)",
+					"--border-radius": "0px",
+				} as CSSProperties
+			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/sonner.tsx` at line 11, Import CSSProperties
explicitly from React and use it for any inline style typings instead of relying
on implicit React.CSSProperties (update the import list alongside ToasterProps
import), and change the invalid CSS value "--border-radius": "none" to a valid
value such as "0px" wherever the style object is defined (e.g. in the
Sonner/Toaster style object used by the component). Ensure the type annotations
reference CSSProperties and the style object uses "0px" for the custom property
so linting/type checks pass.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/ui/sonner.tsx`:
- Line 60: Remove the invalid CSS custom property "--border-radius": "none" from
the Sonner config in apps/web/src/components/ui/sonner.tsx; instead, control
toast border radius via Sonner's classNames (e.g., classNames: { toast:
'rounded-none' }), toastOptions.style (e.g., borderRadius: '0'), or by using the
unstyled prop—update the Toaster/sonner configuration (the object where
"--border-radius" is set) to delete that line and apply one of the supported
approaches (classNames or toastOptions.style) to achieve no border radius.

In `@apps/web/src/stores/file-tree-store.ts`:
- Around line 299-309: wrap the call to _fs().graftSharedTree inside
loadSharedFiles in a try/catch so exceptions from graftSharedTree/insertSubtree
are handled; move the set(...) that updates tempTree/localTree into the try
block so state is only mutated on success, and in the catch log the error (or
use the project logger) and return { loaded: false, openPath: null } so callers
(e.g. the use-share.ts .then() consumer) get a safe failure result instead of an
uncaught exception.

---

Nitpick comments:
In `@apps/web/src/components/share-notice-dialog.tsx`:
- Around line 53-55: Remove the dynamic key on the Dialog (delete
key={String(open)}) so the Dialog instance isn't remounted on every toggle;
instead, keep the Dialog stable and explicitly reset your "permanent" state when
the dialog is opened inside the existing onOpenChange handler (use the setter
for your permanent state—e.g., setPermanent(false) or setIsPermanent(false) when
next is true), and keep the existing onOpenChange={(next) => !next &&
handleDismiss()} logic for closing; this preserves component instance stability
while still resetting the permanent flag when opening.

In `@apps/web/src/components/ui/sonner.tsx`:
- Line 11: Import CSSProperties explicitly from React and use it for any inline
style typings instead of relying on implicit React.CSSProperties (update the
import list alongside ToasterProps import), and change the invalid CSS value
"--border-radius": "none" to a valid value such as "0px" wherever the style
object is defined (e.g. in the Sonner/Toaster style object used by the
component). Ensure the type annotations reference CSSProperties and the style
object uses "0px" for the custom property so linting/type checks pass.

In `@apps/web/src/lib/fs/core/file-node-utils.ts`:
- Around line 13-38: The path concatenation in toFileNode uses a template
literal `${path}/${entry.name}` which can mishandle trailing slashes; replace
that construction with the project's path join utility (e.g., join(path,
entry.name)) when calling toFileNode recursively, and add or reuse the join
import if it's not already imported so toFileNode consistently uses join for all
path assembly.

In `@apps/web/src/lib/fs/core/path-utils.ts`:
- Around line 24-33: The join(...) implementation should normalize "." and ".."
segments so callers can't accidentally get unexpected paths; update the join
function to split segments, iterate with a stack: ignore "." entries, pop the
stack for ".." (but if stack is empty and the path is absolute keep it at root,
or for relative allow leading ".." entries), push normal segment names, and then
rebuild the path preserving the leading "/" if segments[0] started with "/" —
adjust join to perform this normalization so usages like toFileNode and
isSafeRelativePath (and any direct callers) receive normalized paths.

In `@apps/web/src/lib/share.ts`:
- Around line 81-106: The decoder decodeSharePayload currently accepts
arbitrarily large decoded JSON from decodeAndDecompress; add a sanity check
after decoding (inside decodeSharePayload, right after const json = await
decodeAndDecompress(encoded)) to enforce a maximum decoded payload size (e.g.
define a MAX_DECODED_PAYLOAD_BYTES constant) and return null if json.length
exceeds it (or trim/reject before JSON.parse); this protects against extremely
large payloads and memory/URL limits while preserving existing parsing logic in
the isFileNode / root handling paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6628d4e1-4b20-465d-8656-76c4e166e1bb

📥 Commits

Reviewing files that changed from the base of the PR and between e46907a and c771f5f.

⛔ Files ignored due to path filters (3)
  • apps/web/public/ballerina.svg is excluded by !**/*.svg
  • bun.lock is excluded by !**/*.lock
  • packages/wasm/go.sum is excluded by !**/*.sum
📒 Files selected for processing (75)
  • .github/workflows/ci.yml
  • .github/workflows/deploy.yml
  • .gitignore
  • .gitmodules
  • apps/web/.gitignore
  • apps/web/biome.json
  • apps/web/components.json
  • apps/web/index.html
  • apps/web/package.json
  • apps/web/src/assets/examples.json
  • apps/web/src/components/ansi.tsx
  • apps/web/src/components/app-sidebar.tsx
  • apps/web/src/components/code-editor.tsx
  • apps/web/src/components/editor.tsx
  • apps/web/src/components/file-route-sync.tsx
  • apps/web/src/components/file-tree-dialog.tsx
  • apps/web/src/components/share-notice-dialog.tsx
  • apps/web/src/components/ui/button.tsx
  • apps/web/src/components/ui/checkbox.tsx
  • apps/web/src/components/ui/collapsible.tsx
  • apps/web/src/components/ui/dialog.tsx
  • apps/web/src/components/ui/dropdown-menu.tsx
  • apps/web/src/components/ui/input.tsx
  • apps/web/src/components/ui/progress.tsx
  • apps/web/src/components/ui/separator.tsx
  • apps/web/src/components/ui/sheet.tsx
  • apps/web/src/components/ui/sidebar.tsx
  • apps/web/src/components/ui/skeleton.tsx
  • apps/web/src/components/ui/sonner.tsx
  • apps/web/src/components/ui/tooltip.tsx
  • apps/web/src/hooks/use-ballerina.ts
  • apps/web/src/hooks/use-copy-share-link.ts
  • apps/web/src/hooks/use-mobile.ts
  • apps/web/src/hooks/use-share-notice.ts
  • apps/web/src/hooks/use-share.ts
  • apps/web/src/lib/file-tree-utils.ts
  • apps/web/src/lib/fs/core/abstract-fs.ts
  • apps/web/src/lib/fs/core/file-node-utils.ts
  • apps/web/src/lib/fs/core/file-node.types.ts
  • apps/web/src/lib/fs/core/fs.interface.ts
  • apps/web/src/lib/fs/core/path-utils.ts
  • apps/web/src/lib/fs/ephemeral-fs.ts
  • apps/web/src/lib/fs/fs-roots.ts
  • apps/web/src/lib/fs/layered-fs.ts
  • apps/web/src/lib/fs/local-storage-fs.ts
  • apps/web/src/lib/router-utils.ts
  • apps/web/src/lib/share.ts
  • apps/web/src/lib/utils.ts
  • apps/web/src/main.tsx
  • apps/web/src/providers/fs-provider.tsx
  • apps/web/src/routeTree.gen.ts
  • apps/web/src/routes/$.tsx
  • apps/web/src/routes/__root.tsx
  • apps/web/src/stores/editor-store.ts
  • apps/web/src/stores/file-tree-store.ts
  • apps/web/src/styles.css
  • apps/web/src/types/wasm-types.d.ts
  • apps/web/src/wasm_exec.js
  • apps/web/tsconfig.app.json
  • apps/web/tsconfig.json
  • apps/web/tsconfig.node.json
  • apps/web/turbo.json
  • apps/web/vite.config.ts
  • biome.json
  • package.json
  • packages/wasm/.gitignore
  • packages/wasm/ballerina-lang-go
  • packages/wasm/biome.json
  • packages/wasm/diagnostics_printer.go
  • packages/wasm/go.mod
  • packages/wasm/local_storage_fs.go
  • packages/wasm/main_wasm.go
  • packages/wasm/package.json
  • packages/wasm/turbo.json
  • turbo.json
💤 Files with no reviewable changes (1)
  • biome.json
✅ Files skipped from review due to trivial changes (13)
  • apps/web/src/main.tsx
  • packages/wasm/.gitignore
  • packages/wasm/biome.json
  • packages/wasm/package.json
  • packages/wasm/turbo.json
  • apps/web/biome.json
  • apps/web/src/styles.css
  • .gitmodules
  • .github/workflows/deploy.yml
  • apps/web/src/lib/fs/fs-roots.ts
  • apps/web/turbo.json
  • package.json
  • turbo.json
🚧 Files skipped from review as they are similar to previous changes (16)
  • .gitignore
  • apps/web/src/lib/fs/ephemeral-fs.ts
  • apps/web/src/routes/__root.tsx
  • packages/wasm/diagnostics_printer.go
  • apps/web/src/routes/$.tsx
  • .github/workflows/ci.yml
  • apps/web/src/hooks/use-copy-share-link.ts
  • apps/web/src/hooks/use-share-notice.ts
  • apps/web/package.json
  • apps/web/src/components/ui/checkbox.tsx
  • apps/web/src/components/file-route-sync.tsx
  • apps/web/src/hooks/use-share.ts
  • apps/web/src/lib/file-tree-utils.ts
  • apps/web/src/assets/examples.json
  • apps/web/src/lib/fs/layered-fs.ts
  • apps/web/src/components/app-sidebar.tsx

@snelusha snelusha force-pushed the fix/package-diagnostics branch from 9e6f05b to a180052 Compare March 30, 2026 04:51
@snelusha snelusha force-pushed the fix/package-diagnostics branch from a180052 to b24dd0e Compare March 31, 2026 04:39
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/web/src/stores/file-tree-store.ts (1)

310-311: Consider logging the caught error for debuggability.

On Line 310–Line 311, the exception is swallowed. Adding a structured log here would help diagnose malformed shared trees or grafting failures in production.

Suggested tweak
-				} catch (e) {
+				} catch (e) {
+					console.error("[FileTreeStore] loadSharedFiles failed", e);
 					return { loaded: false, openPath: null };
 				}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/stores/file-tree-store.ts` around lines 310 - 311, Add a
structured log inside the catch that currently returns { loaded: false,
openPath: null } so the swallowed exception is recorded; e.g., inside that catch
block call your logger (console.error or the app logger) with a clear message
like "file-tree-store: failed to load shared tree" and include the caught error
object (e) and any relevant context (such as the path or id being processed)
before returning the same { loaded: false, openPath: null } so failures in
parsing/grafting are discoverable in logs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/src/stores/file-tree-store.ts`:
- Around line 310-311: Add a structured log inside the catch that currently
returns { loaded: false, openPath: null } so the swallowed exception is
recorded; e.g., inside that catch block call your logger (console.error or the
app logger) with a clear message like "file-tree-store: failed to load shared
tree" and include the caught error object (e) and any relevant context (such as
the path or id being processed) before returning the same { loaded: false,
openPath: null } so failures in parsing/grafting are discoverable in logs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a2851f62-f766-40a3-91bd-3046aca57f92

📥 Commits

Reviewing files that changed from the base of the PR and between 9e6f05b and b24dd0e.

📒 Files selected for processing (2)
  • apps/web/src/stores/file-tree-store.ts
  • packages/wasm/diagnostics_printer.go

Copy link
Copy Markdown
Contributor

@warunalakshitha warunalakshitha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@warunalakshitha warunalakshitha merged commit 392cffc into ballerina-platform:main Apr 1, 2026
3 checks passed
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.

2 participants