Skip to content

Show compiler errors#869

Merged
hatemhosny merged 8 commits intodevelopfrom
show-compiler-errors
Sep 13, 2025
Merged

Show compiler errors#869
hatemhosny merged 8 commits intodevelopfrom
show-compiler-errors

Conversation

@hatemhosny
Copy link
Collaborator

@hatemhosny hatemhosny commented Sep 13, 2025

This PR shows compiler error messages in integrated console

Summary by CodeRabbit

  • New Features

    • Compile-time errors are now captured as structured messages and surfaced to the UI/console across languages.
    • Compile results now include an optional errors field; modified vs. compiled content is explicitly exposed for markup, style, and script.
  • Bug Fixes

    • Compilers fail gracefully with stable fallbacks, preventing runtime crashes on compilation errors.
  • Refactor

    • Result assembly and caching updated for consistent defaults and clearer separation of modified vs. compiled content.

@coderabbitai
Copy link

coderabbitai bot commented Sep 13, 2025

Walkthrough

Centralizes error extraction with getErrorMessage, surfaces structured compile errors from worker and language compilers, extends CompileInfo with optional errors, and refactors core result assembly to separate compiled vs. modified outputs and propagate those through caching and logging.

Changes

Cohort / File(s) Summary of changes
Compiler worker error result
src/livecodes/compiler/compile.worker.ts
Import CompileResult and getErrorMessage; expand compile return to `string
Core result assembly, errors, cache
src/livecodes/core.ts, src/cache.ts
Split Promise.all into explicit styleCompileResult and testsCompileResult; introduce *.modified fields for markup/style/script and set script.modified = script.compiled; override compiled markup when compileInfo.modifiedHTML present; aggregate/log compile errors to toolsPane.console.error; update cache writes using ?? ''; extend cache interface with modified for markup/style/script.
Language compilers: standardized error propagation
src/livecodes/languages/gleam/lang-gleam-compiler.ts, src/livecodes/languages/svelte/lang-svelte-compiler.ts, src/livecodes/languages/vue/lang-vue-compiler.ts
Import getErrorMessage; wrap compile steps with try/catch; on failure return { code: '', info: { errors: [getErrorMessage(...)] } } or include errors in info; Svelte adds guarded compile path, collects errors array and exposes it in info; Vue unifies info shape and removes direct console.error in favor of info.errors.
Utilities: error message helper
src/livecodes/utils/utils.ts
Add export const getErrorMessage(err: unknown): string to normalize error text extraction from various error shapes.
SDK model: CompileInfo extension
src/sdk/models.ts
Add optional errors?: string[] to CompileInfo (after imports?: Record<string,string>;).

Sequence Diagram(s)

sequenceDiagram
  participant UI as UI
  participant Core as core.ts
  participant Comp as Compiler (worker/lang)
  participant Cache as Cache
  participant Console as Tools Console

  UI->>Core: getResultPage()
  Core->>Comp: compile(markup/style/script/tests)
  par Compile sections
    Comp-->>Core: { code, info{ imports?, errors? } } per section
  end
  Core->>Core: derive compiled vs modified (markup/style/script)
  alt compileInfo.modifiedHTML present
    Core->>Core: override compiledMarkup
  end
  Core->>Console: log each error from info.errors
  Core->>Cache: updateCache(markup?.modified ?? '', style?.modified ?? '', script?.modified ?? '')
  Core-->>UI: compiledCode { compiled, modified, info.errors? }
Loading
sequenceDiagram
  participant Lang as Language Compiler (Svelte/Vue/Gleam)
  participant Util as getErrorMessage

  Lang->>Lang: try compile()
  alt success
    Lang-->>Caller: { code, info: { imports?, errors? } }
  else failure
    Lang->>Util: getErrorMessage(err)
    Util-->>Lang: string
    Lang-->>Caller: { code: '', info: { errors: [msg], ... } }
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I thump my paws at errors’ rise,
Now tamed by tidy, centralized cries.
Modified burrows, compiled trails,
Cache nibbles nulls, no brittle tails.
Svelte, Vue, Gleam—we hop in line,
With messages clear, our builds align. 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Show compiler errors" is concise and accurately reflects the PR’s primary intent—surfacing compiler error messages to the UI/console—as evidenced by changes to compiler workers, language compilers, utils.getErrorMessage, and the CompileInfo model. It is specific enough for a reviewer to understand the main change without enumerating files. The phrasing is clear, focused, and suitable for commit/PR history.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch show-compiler-errors

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.

@netlify
Copy link

netlify bot commented Sep 13, 2025

Deploy Preview for livecodes ready!

Name Link
🔨 Latest commit 2df2a7c
🔍 Latest deploy log https://app.netlify.com/projects/livecodes/deploys/68c593eba93f8a0008f704d3
😎 Deploy Preview https://deploy-preview-869--livecodes.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link
Contributor

github-actions bot commented Sep 13, 2025

Size Change: +382 B (+0.04%)

Total Size: 949 kB

ℹ️ View Unchanged
Filename Size Change
./build/404.html 1 kB 0 B
./build/app.html 250 B 0 B
./build/index.html 2.46 kB +1 B (+0.04%)
./build/livecodes/app.css 22.4 kB 0 B
./build/livecodes/app.js 111 kB +57 B (+0.05%)
./build/livecodes/assets.js 8.65 kB +21 B (+0.24%)
./build/livecodes/assets/noop.js 18 B 0 B
./build/livecodes/assets/templates/diagrams-starter.html 2.19 kB 0 B
./build/livecodes/backup.js 3.73 kB +1 B (+0.03%)
./build/livecodes/blockly.js 13.2 kB -4 B (-0.03%)
./build/livecodes/broadcast.js 1.19 kB +2 B (+0.17%)
./build/livecodes/bundle-types.js 4.37 kB 0 B
./build/livecodes/code-to-image.js 9.1 kB -17 B (-0.19%)
./build/livecodes/codejar.js 17.6 kB 0 B
./build/livecodes/codemirror.js 6.33 kB 0 B
./build/livecodes/compile.page.js 2.39 kB 0 B
./build/livecodes/compile.worker.js 14.2 kB +84 B (+0.59%)
./build/livecodes/compiler-utils.js 3.16 kB -2 B (-0.06%)
./build/livecodes/custom-editor-utils.js 198 B 0 B
./build/livecodes/deploy.js 6.89 kB 0 B
./build/livecodes/editor-settings.js 17.7 kB +15 B (+0.08%)
./build/livecodes/embed-ui.js 5.54 kB -5 B (-0.09%)
./build/livecodes/embed.js 89.1 kB +35 B (+0.04%)
./build/livecodes/export.js 3.89 kB +1 B (+0.03%)
./build/livecodes/firebase.js 22.7 kB 0 B
./build/livecodes/format.worker.js 13.3 kB +15 B (+0.11%)
./build/livecodes/google-fonts.js 7.12 kB 0 B
./build/livecodes/headless.js 77.9 kB -75 B (-0.1%)
./build/livecodes/i18n-ar-language-info.json 5.17 kB 0 B
./build/livecodes/i18n-ar-translation.json 9.32 kB 0 B
./build/livecodes/i18n-de-language-info.json 5.21 kB 0 B
./build/livecodes/i18n-de-translation.json 9.43 kB 0 B
./build/livecodes/i18n-en-language-info.json 4.5 kB 0 B
./build/livecodes/i18n-en-translation.json 8.02 kB 0 B
./build/livecodes/i18n-es-language-info.json 4.95 kB 0 B
./build/livecodes/i18n-es-translation.json 9.16 kB 0 B
./build/livecodes/i18n-fa-language-info.json 5.28 kB 0 B
./build/livecodes/i18n-fa-translation.json 9.46 kB 0 B
./build/livecodes/i18n-fr-language-info.json 5.13 kB 0 B
./build/livecodes/i18n-fr-translation.json 9.4 kB 0 B
./build/livecodes/i18n-hi-language-info.json 5.71 kB 0 B
./build/livecodes/i18n-hi-translation.json 9.94 kB 0 B
./build/livecodes/i18n-it-language-info.json 5 kB 0 B
./build/livecodes/i18n-it-translation.json 9.25 kB 0 B
./build/livecodes/i18n-ja-language-info.json 5.51 kB 0 B
./build/livecodes/i18n-ja-translation.json 9.6 kB 0 B
./build/livecodes/i18n-pt-language-info.json 4.99 kB 0 B
./build/livecodes/i18n-pt-translation.json 9.33 kB 0 B
./build/livecodes/i18n-ru-language-info.json 5.52 kB 0 B
./build/livecodes/i18n-ru-translation.json 10.3 kB 0 B
./build/livecodes/i18n-ur-language-info.json 5.73 kB 0 B
./build/livecodes/i18n-ur-translation.json 9.77 kB 0 B
./build/livecodes/i18n-zh-CN-language-info.json 4.87 kB 0 B
./build/livecodes/i18n-zh-CN-translation.json 8.63 kB 0 B
./build/livecodes/i18n.js 20.2 kB -25 B (-0.12%)
./build/livecodes/import-src.js 15.8 kB +15 B (+0.09%)
./build/livecodes/import.js 14.4 kB -2 B (-0.01%)
./build/livecodes/index.js 5.34 kB -23 B (-0.43%)
./build/livecodes/lang-art-template-compiler.js 1.65 kB 0 B
./build/livecodes/lang-assemblyscript-compiler.js 290 B 0 B
./build/livecodes/lang-assemblyscript-script.js 386 B 0 B
./build/livecodes/lang-astro-compiler.js 2.34 kB 0 B
./build/livecodes/lang-clio-compiler.js 1.55 kB 0 B
./build/livecodes/lang-commonlisp-script.js 123 B 0 B
./build/livecodes/lang-cpp-script.js 1.75 kB 0 B
./build/livecodes/lang-cpp-wasm-script.js 2.84 kB 0 B
./build/livecodes/lang-csharp-wasm-script.js 2.18 kB 0 B
./build/livecodes/lang-diagrams-compiler-esm.js 5.1 kB 0 B
./build/livecodes/lang-dot-compiler.js 1.66 kB 0 B
./build/livecodes/lang-ejs-compiler.js 1.63 kB 0 B
./build/livecodes/lang-eta-compiler.js 1.65 kB 0 B
./build/livecodes/lang-fennel-compiler.js 1.61 kB 0 B
./build/livecodes/lang-gleam-compiler.js 3.08 kB +56 B (+1.85%)
./build/livecodes/lang-haml-compiler.js 1.65 kB 0 B
./build/livecodes/lang-handlebars-compiler.js 1.92 kB 0 B
./build/livecodes/lang-imba-compiler.js 147 B 0 B
./build/livecodes/lang-java-script.js 4.05 kB 0 B
./build/livecodes/lang-jinja-compiler.js 1.65 kB +9 B (+0.55%)
./build/livecodes/lang-julia-script.js 3.3 kB 0 B
./build/livecodes/lang-liquid-compiler.js 1.68 kB 0 B
./build/livecodes/lang-lua-wasm-script.js 205 B 0 B
./build/livecodes/lang-malina-compiler.js 2.94 kB 0 B
./build/livecodes/lang-mustache-compiler.js 1.65 kB 0 B
./build/livecodes/lang-nunjucks-compiler.js 1.93 kB 0 B
./build/livecodes/lang-perl-script.js 268 B 0 B
./build/livecodes/lang-php-wasm-script.js 347 B 0 B
./build/livecodes/lang-postgresql-compiler-esm.js 1.73 kB 0 B
./build/livecodes/lang-prolog-script.js 204 B 0 B
./build/livecodes/lang-pug-compiler.js 371 B 0 B
./build/livecodes/lang-python-wasm-script.js 1.92 kB 0 B
./build/livecodes/lang-r-script-esm.js 2.44 kB 0 B
./build/livecodes/lang-rescript-compiler-esm.js 2.15 kB 0 B
./build/livecodes/lang-rescript-formatter.js 1.52 kB 0 B
./build/livecodes/lang-riot-compiler.js 2.79 kB +1 B (+0.04%)
./build/livecodes/lang-ruby-wasm-script.js 1.71 kB 0 B
./build/livecodes/lang-scss-compiler.js 1.71 kB 0 B
./build/livecodes/lang-solid-compiler.js 263 B 0 B
./build/livecodes/lang-sql-compiler.js 1.63 kB 0 B
./build/livecodes/lang-sql-script.js 1.96 kB -2 B (-0.1%)
./build/livecodes/lang-svelte-compiler.js 4.67 kB +55 B (+1.19%)
./build/livecodes/lang-tcl-script.js 1.82 kB 0 B
./build/livecodes/lang-teal-compiler.js 1.72 kB 0 B
./build/livecodes/lang-twig-compiler.js 1.64 kB 0 B
./build/livecodes/lang-vento-compiler.js 1.68 kB 0 B
./build/livecodes/lang-vue-compiler.js 6.09 kB +119 B (+1.99%)
./build/livecodes/lang-vue2-compiler.js 3.45 kB 0 B
./build/livecodes/lang-wat-compiler.js 348 B 0 B
./build/livecodes/lang-wat-script.js 1.58 kB -1 B (-0.06%)
./build/livecodes/language-info.js 7.64 kB +1 B (+0.01%)
./build/livecodes/monaco-lang-astro.js 947 B 0 B
./build/livecodes/monaco-lang-clio.js 639 B 0 B
./build/livecodes/monaco-lang-imba.js 7.35 kB 0 B
./build/livecodes/monaco-lang-wat.js 2.46 kB 0 B
./build/livecodes/monaco.js 10.1 kB 0 B
./build/livecodes/open.js 6.2 kB +4 B (+0.06%)
./build/livecodes/processor-lightningcss-compiler.js 1.88 kB 0 B
./build/livecodes/processor-postcss-compiler.js 2.02 kB +1 B (+0.05%)
./build/livecodes/processor-tailwindcss-compiler.js 5.05 kB 0 B
./build/livecodes/processor-unocss-compiler.js 355 B 0 B
./build/livecodes/processor-windicss-compiler.js 450 B 0 B
./build/livecodes/quill.css 697 B 0 B
./build/livecodes/quill.js 5.74 kB 0 B
./build/livecodes/resources.js 3.43 kB +1 B (+0.03%)
./build/livecodes/result-utils.js 1.17 kB 0 B
./build/livecodes/share.js 3.81 kB +2 B (+0.05%)
./build/livecodes/snippets.js 6.05 kB +3 B (+0.05%)
./build/livecodes/sync-ui.js 3.25 kB -1 B (-0.03%)
./build/livecodes/sync.js 3.54 kB +16 B (+0.45%)
./build/livecodes/sync.worker.js 29.7 kB +24 B (+0.08%)
./build/livecodes/templates.js 24.6 kB 0 B
./build/sdk/livecodes.js 3.95 kB 0 B
./build/sdk/livecodes.umd.js 4.03 kB 0 B
./build/sdk/package.json 293 B 0 B
./build/sdk/react.js 4.25 kB 0 B
./build/sdk/vue.js 4.34 kB 0 B

compressed-size-action

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Sep 13, 2025

Deploying livecodes with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2df2a7c
Status: ✅  Deploy successful!
Preview URL: https://cb74c668.livecodes.pages.dev
Branch Preview URL: https://show-compiler-errors.livecodes.pages.dev

View logs

Copy link

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/livecodes/compiler/compile.worker.ts (1)

64-90: Retry recursion resolves too early; missing return/await on recursive call.

load() calls itself without returning/awaiting, so the first await load() may resolve before retries finish. Wrap retries in a loop or return the recursive promise.

Apply:

-      let tries = 3;
-      const load = async () => {
-        try {
-          if (languageCompiler.url) {
-            importScripts(languageCompiler.url);
-          }
-          languageCompiler.fn = await languageCompiler.factory(config, baseUrl);
-          if (languageCompiler.aliasTo) {
-            compilers[languageCompiler.aliasTo].fn = languageCompiler.fn;
-          }
-        } catch (err) {
-          // throw err;
-          tries -= 1;
-          if (tries > 0) {
-            // eslint-disable-next-line no-console
-            console.warn(`Failed to load compiler for: ${language}. Retrying...`);
-            load();
-          } else {
-            // eslint-disable-next-line no-console
-            console.warn(`Failed to load compiler for: ${language}. Reloading...`);
-            worker.postMessage({ type: 'load-failed', payload: language });
-          }
-        }
-      };
-
-      await load();
+      let tries = 3;
+      const load = async (): Promise<void> => {
+        try {
+          if (languageCompiler.url) {
+            importScripts(languageCompiler.url);
+          }
+          languageCompiler.fn = await languageCompiler.factory(config, baseUrl);
+          if (languageCompiler.aliasTo) {
+            compilers[languageCompiler.aliasTo].fn = languageCompiler.fn;
+          }
+        } catch (err) {
+          tries -= 1;
+          if (tries > 0) {
+            // eslint-disable-next-line no-console
+            console.warn(`Failed to load compiler for: ${language}. Retrying...`);
+            return load();
+          }
+          // eslint-disable-next-line no-console
+          console.warn(`Failed to load compiler for: ${language}. Reloading...`);
+          worker.postMessage({ type: 'load-failed', payload: language });
+        }
+      };
+      await load();
src/livecodes/core.ts (1)

967-1018: Regression: using .code/.info on possibly-string compile results. Wrap with getCompileResult.

compiler.compile may return string | CompileResult. Direct access to .code/.info for markup/script will break on string. Also keep style/tests results to reuse their info/errors.

Apply:

-  const markupCompileResult = await compiler.compile(markupContent, markupLanguage, config, {
+  const markupCompileResult = await compiler.compile(markupContent, markupLanguage, config, {
     forceCompile: forceCompileSFC,
   });
-  let compiledMarkup = markupCompileResult.code;
+  const markupRes = getCompileResult(markupCompileResult);
+  let compiledMarkup = markupRes.code;
@@
-  const scriptCompileResult = await compiler.compile(scriptContent, scriptLanguage, config, {
+  const scriptCompileResult = await compiler.compile(scriptContent, scriptLanguage, config, {
     forceCompile: forceCompileStyles || forceCompileSFC,
@@
   });
-  const compiledScript = scriptCompileResult.code;
+  const scriptRes = getCompileResult(scriptCompileResult);
+  const compiledScript = scriptRes.code;
@@
-  let compileInfo: CompileInfo = {
-    ...markupCompileResult.info,
-    ...scriptCompileResult.info,
+  let compileInfo: CompileInfo = {
+    ...markupRes.info,
+    ...scriptRes.info,
     importedContent:
-      (markupCompileResult.info.importedContent || '') +
-      (scriptCompileResult.info.importedContent || ''),
+      (markupRes.info.importedContent || '') + (scriptRes.info.importedContent || ''),
     imports:
-      {
-      ...scriptCompileResult.info.imports,
-      ...markupCompileResult.info.imports,
-    },
+      {
+        ...scriptRes.info.imports,
+        ...markupRes.info.imports,
+      },
   };
@@
-  const [styleCompileResult, testsCompileResult] = await Promise.all([
+  const [styleCompileResult, testsCompileResult] = await Promise.all([
     compiler.compile(styleContent, styleLanguage, config, {
       html: `${compiledMarkup}<script type="script-for-styles">${compiledScript}</script>
         <script type="script-for-styles">${compileInfo.importedContent}</script>`,
       forceCompile: forceCompileStyles,
     }),
     runTests
       ? testsNotChanged
         ? Promise.resolve(getCache().tests?.compiled || '')
         : compiler.compile(testsContent, testsLanguage, config, {})
       : Promise.resolve(getCompileResult(getCache().tests?.compiled || '')),
   ]);
-  const [compiledStyle, compiledTests] = [styleCompileResult, testsCompileResult].map((result) => {
-    const { code, info } = getCompileResult(result);
-    compileInfo = {
-      ...compileInfo,
-      ...info,
-    };
-    return code;
-  });
+  const styleRes = getCompileResult(styleCompileResult);
+  const testsRes = getCompileResult(testsCompileResult);
+  const compiledStyle = styleRes.code;
+  const compiledTests = testsRes.code;
+  compileInfo = { ...compileInfo, ...styleRes.info, ...testsRes.info };
🧹 Nitpick comments (7)
src/sdk/models.ts (1)

1294-1295: Add errors to CompileInfo — good addition; consider documenting intent.

Optional: add a brief JSDoc on errors to clarify they are human-readable compiler messages intended for the console UI.

src/livecodes/languages/gleam/lang-gleam-compiler.ts (1)

197-200: Ensure non-empty error message is surfaced.

Guard against empty strings from getErrorMessage for nullish/opaque errors.

Apply this diff:

-      return {
-        code: '',
-        info: { errors: [getErrorMessage(error)] },
-      };
+      const msg = getErrorMessage(error) || 'Unknown compile error';
+      return {
+        code: '',
+        info: { errors: [msg] },
+      };
src/livecodes/utils/utils.ts (1)

646-654: Harden getErrorMessage for empty/opaque cases.

Fallback to stack, JSON, or a default message to avoid empty strings or “[object Object]”.

Apply this diff:

-export const getErrorMessage = /* @__PURE__ */ (err: unknown): string => {
-  if (err == null) return '';
-  if (err instanceof Error) return err.message;
-  if (err && typeof err === 'object' && 'message' in err && typeof err.message === 'string') {
-    return err.message;
-  }
-  return String(err);
-};
+export const getErrorMessage = /* @__PURE__ */ (err: unknown): string => {
+  if (err == null) return '';
+  if (err instanceof Error) return err.message || err.stack || 'Unknown error';
+  if (err && typeof err === 'object' && 'message' in err && typeof (err as any).message === 'string') {
+    return (err as any).message || JSON.stringify(err);
+  }
+  try {
+    return String(err);
+  } catch {
+    try {
+      return JSON.stringify(err);
+    } catch {
+      return 'Unknown error';
+    }
+  }
+};
src/livecodes/languages/svelte/lang-svelte-compiler.ts (2)

13-13: Scope of errors array may race under concurrent compiles.

errors is closure-scoped; concurrent invocations could interleave. Prefer function-local accumulation returned via info, or pass errors explicitly to recursive compileSFC.


52-67: Return consistent info in error path and avoid empty messages.

Also include importedContent/imports for parity with success path; filter empty messages.

Apply this diff:

-    let js: { code: string };
+    let js: { code: string };
     try {
       const result = (window as any).svelte.compile(processedCode, {
         css: 'injected',
         filename,
         ...customSettings,
       });
       js = result.js;
     } catch (err) {
-      const empty = `export default () => {}`;
-      errors.push(getErrorMessage(err));
-      return {
-        code: language === 'svelte-app' ? `<script type="module">${empty}</script>` : empty,
-        info: { errors },
-      };
+      const empty = `export default () => {}`;
+      const msg = getErrorMessage(err);
+      if (msg) errors.push(msg);
+      return {
+        code: language === 'svelte-app' ? `<script type="module">${empty}</script>` : empty,
+        info: { importedContent, imports, errors },
+      };
     }
src/livecodes/languages/vue/lang-vue-compiler.ts (1)

393-397: Avoid logging non-serializable errors and duplicate reporting.

Either remove console.error or log mapped strings only; UI will display info.errors anyway.

Apply this diff:

-      if (errors.length) {
-        // eslint-disable-next-line no-console
-        console.error(...errors);
-      }
+      // optional: surface via UI only
+      // if (errors.length) console.error(...errors.map(getErrorMessage));
src/livecodes/compiler/compile.worker.ts (1)

108-118: Return a typed union and ensure non-empty error message.

  • Explicitly type compile()’s return to Promise<string | CompileResult>.
  • Fallback to a default error message when getErrorMessage(err) is empty.

Apply:

-const compile = async (
+const compile = async (
   content: string,
   language: LanguageOrProcessor,
   config: Config,
   options: CompileOptions,
-) => {
+): Promise<string | CompileResult> => {
@@
-  } catch (err: unknown) {
+  } catch (err: unknown) {
@@
-    value = {
+    const message = getErrorMessage(err) || 'Unknown compile error';
+    value = {
       code: '',
-      info: { errors: [getErrorMessage(err)] },
+      info: { errors: [message] },
     };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e14dcce and 17511a9.

📒 Files selected for processing (7)
  • src/livecodes/compiler/compile.worker.ts (2 hunks)
  • src/livecodes/core.ts (6 hunks)
  • src/livecodes/languages/gleam/lang-gleam-compiler.ts (2 hunks)
  • src/livecodes/languages/svelte/lang-svelte-compiler.ts (2 hunks)
  • src/livecodes/languages/vue/lang-vue-compiler.ts (2 hunks)
  • src/livecodes/utils/utils.ts (1 hunks)
  • src/sdk/models.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/livecodes/languages/gleam/lang-gleam-compiler.ts (1)
src/livecodes/utils/utils.ts (1)
  • getErrorMessage (646-653)
src/livecodes/languages/svelte/lang-svelte-compiler.ts (2)
src/sdk/models.ts (1)
  • CompilerFunction (1302-1317)
src/livecodes/utils/utils.ts (1)
  • getErrorMessage (646-653)
src/livecodes/compiler/compile.worker.ts (2)
src/sdk/models.ts (1)
  • CompileResult (1297-1300)
src/livecodes/utils/utils.ts (1)
  • getErrorMessage (646-653)
src/livecodes/languages/vue/lang-vue-compiler.ts (2)
src/livecodes/compiler/import-map.ts (1)
  • createImportMap (67-93)
src/livecodes/utils/utils.ts (1)
  • getErrorMessage (646-653)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: Redirect rules - livecodes
  • GitHub Check: Header rules - livecodes
  • GitHub Check: Pages changed - livecodes
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: tests (18.x, 5)
  • GitHub Check: tests (18.x, 3)
  • GitHub Check: tests (18.x, 4)
  • GitHub Check: tests (18.x, 2)
  • GitHub Check: tests (18.x, 1)
  • GitHub Check: type-check (18.x)
  • GitHub Check: build
  • GitHub Check: build (18.x)
  • GitHub Check: build (18.x)
  • GitHub Check: type-check (18.x)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (7)
src/livecodes/languages/gleam/lang-gleam-compiler.ts (1)

5-5: Centralized error formatting import — OK.

src/livecodes/languages/svelte/lang-svelte-compiler.ts (3)

5-5: Centralized error formatting import — OK.


49-51: Resetting errors per top-level file — OK.

Keeps error state clean between compilations.


73-74: Expose errors in success info — OK.

src/livecodes/compiler/compile.worker.ts (1)

4-12: Imports update looks correct.

Brings in CompileResult and getErrorMessage for structured errors; no issues.

src/livecodes/core.ts (2)

1024-1050: Modified fields propagation looks good.

Cache now tracks compiled vs. modified; updateCompiledCode already prefers modified. No changes needed.


1137-1139: LGTM: safer loading placeholders.

Using nullish coalescing avoids accidental fallbacks; good.

Comment on lines +1068 to +1075
const compileErrors = [
...(markupCompileResult.info?.errors ?? []),
...(styleCompileResult.info?.errors ?? []),
...(scriptCompileResult.info?.errors ?? []),
...(getCompileResult(testsCompileResult).info?.errors ?? []),
];
compileErrors.forEach((err) => toolsPane?.console?.error(err));

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Aggregate errors from normalized results to avoid undefined access.

Use markupRes/scriptRes/styleRes/testsRes computed above; current code may read .info on strings.

Apply:

-  const compileErrors = [
-    ...(markupCompileResult.info?.errors ?? []),
-    ...(styleCompileResult.info?.errors ?? []),
-    ...(scriptCompileResult.info?.errors ?? []),
-    ...(getCompileResult(testsCompileResult).info?.errors ?? []),
-  ];
+  const compileErrors = [
+    ...(markupRes.info?.errors ?? []),
+    ...(styleRes.info?.errors ?? []),
+    ...(scriptRes.info?.errors ?? []),
+    ...(testsRes.info?.errors ?? []),
+  ];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const compileErrors = [
...(markupCompileResult.info?.errors ?? []),
...(styleCompileResult.info?.errors ?? []),
...(scriptCompileResult.info?.errors ?? []),
...(getCompileResult(testsCompileResult).info?.errors ?? []),
];
compileErrors.forEach((err) => toolsPane?.console?.error(err));
const compileErrors = [
...(markupRes.info?.errors ?? []),
...(styleRes.info?.errors ?? []),
...(scriptRes.info?.errors ?? []),
...(testsRes.info?.errors ?? []),
];
compileErrors.forEach((err) => toolsPane?.console?.error(err));
🤖 Prompt for AI Agents
In src/livecodes/core.ts around lines 1068 to 1075, the code currently spreads
.info?.errors directly from raw compile result variables which may be strings;
replace those with the previously computed normalized result variables
(markupRes, styleRes, scriptRes, testsRes) and aggregate errors from their
.info?.errors properties (using optional chaining and empty-array defaults) so
you never access .info on a string or undefined; then iterate and log each error
to toolsPane?.console?.error as before.

Comment on lines +384 to +391
return {
code:
language === 'vue-app'
? `<script type="module">${compiledCode}</script>`
: compiledCode,
info: { importedContent, imports: createImportMap(importedContent, config), errors },
};
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

errors contains non-strings; violates CompileInfo contract and breaks UI rendering.

errors gathers objects from Vue compiler (parse/style/template). Map to strings before returning.

Apply this diff:

-        return {
-          code:
-            language === 'vue-app'
-              ? `<script type="module">${compiledCode}</script>`
-              : compiledCode,
-          info: { importedContent, imports: createImportMap(importedContent, config), errors },
-        };
+        const errorMessages = errors.map(getErrorMessage).filter(Boolean);
+        return {
+          code:
+            language === 'vue-app'
+              ? `<script type="module">${compiledCode}</script>`
+              : compiledCode,
+          info: {
+            importedContent,
+            imports: createImportMap(importedContent, config),
+            errors: errorMessages,
+          },
+        };

Additionally, change the declaration to enforce string accumulation (outside this hunk):

// before
let errors: any[] = [];
// after
let errors: string[] = [];

And when pushing compiler errors elsewhere in this file, convert to messages:

// parse errors
errors.push(...err.map(getErrorMessage));
// style errors
errors.push(...styleResult.errors.map(getErrorMessage));
// template errors
errors.push(...templateResult.errors.map(getErrorMessage));
🤖 Prompt for AI Agents
In src/livecodes/languages/vue/lang-vue-compiler.ts around lines 384 to 391, the
CompileInfo.errors array currently contains non-string objects from the Vue
compiler which breaks UI rendering; change the errors declaration to let errors:
string[] = []; then ensure every place pushing compiler errors converts them to
strings (e.g., push mapped messages using a getErrorMessage helper) — for
example replace pushes of raw error objects with
errors.push(...err.map(getErrorMessage)),
errors.push(...styleResult.errors.map(getErrorMessage)), and
errors.push(...templateResult.errors.map(getErrorMessage)); finally return the
CompileInfo with the errors array of strings so the UI contract is respected.

Comment on lines +398 to 407
const empty = `export default () => {}`;
return {
code:
language === 'vue-app' ? `<script type="module">${compiledCode}</script>` : compiledCode,
info: { importedContent, imports: createImportMap(importedContent, config) },
code: language === 'vue-app' ? `<script type="module">${empty}</script>` : empty,
info: { errors },
};
} catch (err) {
return {
code: '',
info: { errors: [...errors, getErrorMessage(err)] },
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Normalize errors in fallback/catch paths.

Ensure strings only; include prior errors.

Apply this diff:

-      const empty = `export default () => {}`;
-      return {
-        code: language === 'vue-app' ? `<script type="module">${empty}</script>` : empty,
-        info: { errors },
-      };
+      const empty = `export default () => {}`;
+      return {
+        code: language === 'vue-app' ? `<script type="module">${empty}</script>` : empty,
+        info: { errors: errors.map(getErrorMessage).filter(Boolean) },
+      };
     } catch (err) {
-      return {
-        code: '',
-        info: { errors: [...errors, getErrorMessage(err)] },
-      };
+      return {
+        code: '',
+        info: {
+          errors: [...errors.map(getErrorMessage), getErrorMessage(err)].filter(Boolean),
+        },
+      };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const empty = `export default () => {}`;
return {
code:
language === 'vue-app' ? `<script type="module">${compiledCode}</script>` : compiledCode,
info: { importedContent, imports: createImportMap(importedContent, config) },
code: language === 'vue-app' ? `<script type="module">${empty}</script>` : empty,
info: { errors },
};
} catch (err) {
return {
code: '',
info: { errors: [...errors, getErrorMessage(err)] },
};
const empty = `export default () => {}`;
return {
code: language === 'vue-app' ? `<script type="module">${empty}</script>` : empty,
info: { errors: errors.map(getErrorMessage).filter(Boolean) },
};
} catch (err) {
return {
code: '',
info: {
errors: [...errors.map(getErrorMessage), getErrorMessage(err)].filter(Boolean),
},
};
🤖 Prompt for AI Agents
In src/livecodes/languages/vue/lang-vue-compiler.ts around lines 398 to 407, the
catch/fallback returns an info.errors array that can contain non-string values
and may not include prior errors consistently; normalize all errors to strings
and include previous errors. Update both the successful-fallback and catch
branches so info.errors is an array of strings by mapping existing errors
through String or getErrorMessage (e.g., const normalized = errors.map(e =>
typeof e === 'string' ? e : getErrorMessage(e))), then return info: { errors:
[...normalized, getErrorMessage(err)] } in the catch, and in the other fallback
return info: { errors: normalized } (for vue-app keep the same code value).
Ensure no non-string values are left in info.errors.

@sonarqubecloud
Copy link

Copy link

@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: 3

♻️ Duplicate comments (1)
src/livecodes/core.ts (1)

1068-1075: Normalize results before logging errors (fixes TS union access).

Accessing .info on possibly-string results is unsafe; wrap with getCompileResult.

-  logError(markupLanguage, markupCompileResult.info?.errors);
-  logError(styleLanguage, styleCompileResult.info?.errors);
-  logError(scriptLanguage, scriptCompileResult.info?.errors);
-  logError(testsLanguage, getCompileResult(testsCompileResult).info?.errors);
+  logError(markupLanguage, getCompileResult(markupCompileResult).info?.errors);
+  logError(styleLanguage, getCompileResult(styleCompileResult).info?.errors);
+  logError(scriptLanguage, getCompileResult(scriptCompileResult).info?.errors);
+  logError(testsLanguage, getCompileResult(testsCompileResult).info?.errors);
🧹 Nitpick comments (1)
src/livecodes/core.ts (1)

999-1011: Avoid overwriting compileInfo fields while folding style/tests info.

Limit merging to errors; current spread may clobber imports/importedContent.

-  const [compiledStyle, compiledTests] = [styleCompileResult, testsCompileResult].map((result) => {
-    const { code, info } = getCompileResult(result);
-    compileInfo = {
-      ...compileInfo,
-      ...info,
-    };
-    return code;
-  });
+  const [compiledStyle, compiledTests] = [styleCompileResult, testsCompileResult].map((result) => {
+    const { code, info } = getCompileResult(result);
+    if (info?.errors?.length) {
+      compileInfo = {
+        ...compileInfo,
+        errors: [...(compileInfo.errors ?? []), ...info.errors],
+      };
+    }
+    return code;
+  });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17511a9 and 2df2a7c.

📒 Files selected for processing (2)
  • src/livecodes/core.ts (6 hunks)
  • src/livecodes/languages/svelte/lang-svelte-compiler.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/livecodes/core.ts (3)
src/sdk/models.ts (1)
  • Language (884-1086)
src/sdk/index.ts (1)
  • Language (16-16)
src/livecodes/languages/utils.ts (1)
  • getLanguageTitle (16-19)
src/livecodes/languages/svelte/lang-svelte-compiler.ts (2)
src/sdk/models.ts (1)
  • CompilerFunction (1302-1317)
src/livecodes/utils/utils.ts (1)
  • getErrorMessage (646-653)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: Redirect rules - livecodes
  • GitHub Check: Header rules - livecodes
  • GitHub Check: Pages changed - livecodes
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build (18.x)
  • GitHub Check: type-check (18.x)
  • GitHub Check: build
  • GitHub Check: tests (18.x, 5)
  • GitHub Check: tests (18.x, 3)
  • GitHub Check: tests (18.x, 2)
  • GitHub Check: tests (18.x, 1)
  • GitHub Check: tests (18.x, 4)
  • GitHub Check: type-check (18.x)
  • GitHub Check: build (18.x)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (4)
src/livecodes/languages/svelte/lang-svelte-compiler.ts (1)

72-73: LGTM: surfacing errors in info.

Exposing info.errors aligns with the PR objective to show compiler errors.

src/livecodes/core.ts (3)

1029-1035: LGTM: initialize modified = compiled for markup/style.

This matches the new modified/compiled separation.


1049-1049: LGTM: script.modified mirrors compiled.

Keeps cache consistent.


1137-1139: LGTM: language-aware loading placeholders in flushResult.

Keeps caches valid per compiled language.

Comment on lines 47 to +51
imports = createImportMap(importedContent, config);
errors = [];
}

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Reset errors at the root entry, not here (prevents losing nested SFC errors) and also reset imports for SECONDARY_FILE.

Errors collected while compiling nested SFCs via replaceSFCImports are wiped here. Move the reset to the root-file preamble (for BOTH MAIN_FILE and SECONDARY_FILE) and keep this block for import map creation only.

Apply within this hunk:

       imports = createImportMap(importedContent, config);
-      errors = [];

Additionally (outside this hunk), ensure root entry resets state and handles empty code:

// right after detecting root file
if (filename === MAIN_FILE || filename === SECONDARY_FILE) {
  importedContent = '';
  imports = {};
  errors = [];
}
// ensure empty-root clears errors too
if (!code) {
  if (filename === MAIN_FILE || filename === SECONDARY_FILE) errors = [];
  return getCompileResult('');
}
🤖 Prompt for AI Agents
In src/livecodes/languages/svelte/lang-svelte-compiler.ts around lines 47-51, do
not reset errors inside this block—only build the import map for
MAIN_FILE/SECONDARY_FILE here (keep imports = createImportMap(importedContent,
config); remove errors = [] from this hunk). Instead, at the root-file preamble
(right after detecting filename === MAIN_FILE || filename === SECONDARY_FILE)
initialize/reset shared state by setting importedContent = '', imports = {}, and
errors = []; also add the empty-root guard: if (!code) { if (filename ===
MAIN_FILE || filename === SECONDARY_FILE) errors = []; return
getCompileResult(''); } so nested SFC compile errors aren’t wiped and root files
still reset state and handle empty code.

Comment on lines +54 to +58
const result = (window as any).svelte.compile(processedCode, {
css: 'injected',
filename,
...customSettings,
});
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Stabilize component name for mount().

Pass name to svelte.compile so getMountCode’s LiveCodes_App symbol is guaranteed across Svelte versions.

       const result = (window as any).svelte.compile(processedCode, {
         css: 'injected',
         filename,
+        name: filename.replace(/\.svelte$/,'').replace(/[^A-Za-z0-9_$]/g, '_'),
         ...customSettings,
       });

🌐 Web query:

Does the current Svelte compiler support compile options { css: 'injected', name } and a `mount` API via `import { mount } from 'svelte'`?

💡 Result:

Yes.

  • The compiler supports compile options including css: 'injected' (and 'external' etc.) and related name/cssHash options. [1]
  • Svelte exposes an imperative mount/hydrate/unmount API (import { mount } from 'svelte'), introduced for the Svelte 5 component API. [2][3]

Sources:
[1] svelte/compiler docs (CompileOptions).
[2] Imperative component API (mount/hydrate/unmount).
[3] Svelte 5 migration guide (notes on mount/hydrate).


Stabilize component name for mount().

Svelte supports the compile option name and the imperative mount API — pass a deterministic name so getMountCode’s LiveCodes_App symbol is stable across Svelte versions.

File: src/livecodes/languages/svelte/lang-svelte-compiler.ts — Lines: 54-58

       const result = (window as any).svelte.compile(processedCode, {
         css: 'injected',
         filename,
+        name: filename.replace(/\.svelte$/,'').replace(/[^A-Za-z0-9_$]/g, '_'),
         ...customSettings,
       });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const result = (window as any).svelte.compile(processedCode, {
css: 'injected',
filename,
...customSettings,
});
const result = (window as any).svelte.compile(processedCode, {
css: 'injected',
filename,
name: filename.replace(/\.svelte$/,'').replace(/[^A-Za-z0-9_$]/g, '_'),
...customSettings,
});
🤖 Prompt for AI Agents
In src/livecodes/languages/svelte/lang-svelte-compiler.ts around lines 54-58,
the compile call needs a deterministic component name so getMountCode's
__LiveCodes_App__ symbol remains stable; add a name property with a fixed string
(e.g. "__LiveCodes_App__") to the compile options and ensure it overrides any
customSettings.name by placing it after the spread (or explicitly set
customSettings.name = "__LiveCodes_App__" before calling). Keep other options
(css, filename, customSettings) intact.

Comment on lines +62 to +65
return {
code: '',
info: { errors },
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Keep info shape consistent on error path.

Return importedContent and imports alongside errors.

-      return {
-        code: '',
-        info: { errors },
-      };
+      return {
+        code: '',
+        info: { importedContent, imports, errors },
+      };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return {
code: '',
info: { errors },
};
return {
code: '',
info: { importedContent, imports, errors },
};
🤖 Prompt for AI Agents
In src/livecodes/languages/svelte/lang-svelte-compiler.ts around lines 62 to 65,
the error return currently only sets info: { errors } which breaks the expected
info shape; update the error path to include the existing importedContent and
imports properties alongside errors (e.g., info: { errors, importedContent,
imports }) so callers always receive a consistent info object.

@hatemhosny hatemhosny merged commit f46a5c6 into develop Sep 13, 2025
22 checks passed
@hatemhosny hatemhosny deleted the show-compiler-errors branch September 13, 2025 18:19
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