From 3076b5985d1ac7f6df7a2a06398f7a1737e19d98 Mon Sep 17 00:00:00 2001 From: Hatem Hosny Date: Sat, 13 Sep 2025 03:53:51 +0300 Subject: [PATCH 1/8] fix(App): fix persistent loading message in compiled code viewer --- src/livecodes/core.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/livecodes/core.ts b/src/livecodes/core.ts index 7bcc96f61..efff919a2 100644 --- a/src/livecodes/core.ts +++ b/src/livecodes/core.ts @@ -1070,6 +1070,9 @@ const getResultPage = async ({ result: cleanResultFromDev(result), styleOnlyUpdate, }); + updateCache('markup', markupLanguage, compiledMarkup); + updateCache('style', styleLanguage, compiledStyle); + updateCache('script', scriptLanguage, compiledScript); if (broadcastInfo.isBroadcasting) { broadcast(); @@ -1124,17 +1127,9 @@ const flushResult = () => { wat: ';; loading', }; - updateCache( - 'markup', - compiledLanguages.markup, - loadingComments[compiledLanguages.markup] || 'html', - ); - updateCache('style', compiledLanguages.style, loadingComments[compiledLanguages.style] || 'css'); - updateCache( - 'script', - compiledLanguages.script, - loadingComments[compiledLanguages.script] || 'javascript', - ); + updateCache('markup', compiledLanguages.markup, loadingComments[compiledLanguages.markup] ?? ''); + updateCache('style', compiledLanguages.style, loadingComments[compiledLanguages.style] ?? ''); + updateCache('script', compiledLanguages.script, loadingComments[compiledLanguages.script] ?? ''); setCache({ ...getCache(), tests: { From dd3514a31d7a6a0bb213b849c935ea000be42b82 Mon Sep 17 00:00:00 2001 From: Hatem Hosny Date: Sat, 13 Sep 2025 04:14:10 +0300 Subject: [PATCH 2/8] feat(Compilers): show compiler error messages in console --- src/livecodes/compiler/compile.worker.ts | 22 ++++++++++++++++------ src/livecodes/core.ts | 13 ++++++++++--- src/livecodes/utils/utils.ts | 7 +++++++ src/sdk/models.ts | 1 + 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/livecodes/compiler/compile.worker.ts b/src/livecodes/compiler/compile.worker.ts index 6dc87c0e6..3fe853021 100644 --- a/src/livecodes/compiler/compile.worker.ts +++ b/src/livecodes/compiler/compile.worker.ts @@ -1,8 +1,15 @@ import type TS from 'typescript'; import { getCompilerOptions } from '../editor/ts-compiler-options'; import { languages, processors } from '../languages'; -import type { CompileOptions, Compilers, Config, EditorLibrary, Language } from '../models'; -import { doOnce, objectFilter } from '../utils/utils'; +import type { + CompileOptions, + CompileResult, + Compilers, + Config, + EditorLibrary, + Language, +} from '../models'; +import { doOnce, getErrorMessage, objectFilter } from '../utils/utils'; import { codeMirrorBaseUrl, comlinkBaseUrl, vendorsBaseUrl } from '../vendors'; import { getAllCompilers } from './get-all-compilers'; import type { CompilerMessage, CompilerMessageEvent, LanguageOrProcessor } from './models'; @@ -98,15 +105,18 @@ const compile = async ( throw new Error('Failed to load compiler for: ' + language); } - let value; + let value: string | CompileResult = ''; try { value = await compiler(content, { config, language, baseUrl, options }); - } catch (err) { + } catch (err: any) { // eslint-disable-next-line no-console console.error('Failed compiling: ' + language, err); - value = content; + value = { + code: '', + info: { errors: [getErrorMessage(err)] }, + }; } - return value || ''; + return value; }; const loadTypeScript = async () => { diff --git a/src/livecodes/core.ts b/src/livecodes/core.ts index efff919a2..fdb69bf80 100644 --- a/src/livecodes/core.ts +++ b/src/livecodes/core.ts @@ -996,7 +996,7 @@ const getResultPage = async ({ }, }; - const compileResults = await Promise.all([ + const [styleCompileResult, testsCompileResult] = await Promise.all([ compiler.compile(styleContent, styleLanguage, config, { html: `${compiledMarkup} `, @@ -1008,8 +1008,7 @@ const getResultPage = async ({ : compiler.compile(testsContent, testsLanguage, config, {}) : Promise.resolve(getCompileResult(getCache().tests?.compiled || '')), ]); - - const [compiledStyle, compiledTests] = compileResults.map((result) => { + const [compiledStyle, compiledTests] = [styleCompileResult, testsCompileResult].map((result) => { const { code, info } = getCompileResult(result); compileInfo = { ...compileInfo, @@ -1063,6 +1062,14 @@ const getResultPage = async ({ const styleOnlyUpdate = sourceEditor === 'style' && !compileInfo.cssModules; + const compileErrors = [ + ...(markupCompileResult.info?.errors ?? []), + ...(styleCompileResult.info?.errors ?? []), + ...(scriptCompileResult.info?.errors ?? []), + ...(getCompileResult(testsCompileResult).info?.errors ?? []), + ]; + compileErrors.forEach((err) => toolsPane?.console?.error(err)); + if (singleFile) { setCache({ ...getCache(), diff --git a/src/livecodes/utils/utils.ts b/src/livecodes/utils/utils.ts index 518bca4a0..b23f3e073 100644 --- a/src/livecodes/utils/utils.ts +++ b/src/livecodes/utils/utils.ts @@ -643,6 +643,13 @@ export const compareObjects = /* @__PURE__ */ ( return diff; }; +export const getErrorMessage = /* @__PURE__ */ (err: unknown) => { + if (err && typeof err === 'object' && 'message' in err && typeof err.message === 'string') { + return err.message; + } + return String(err); +}; + export const predefinedValues = { APP_VERSION: process.env.VERSION || '', SDK_VERSION: process.env.SDK_VERSION || '', diff --git a/src/sdk/models.ts b/src/sdk/models.ts index e6bd21a01..50695a9bd 100644 --- a/src/sdk/models.ts +++ b/src/sdk/models.ts @@ -1291,6 +1291,7 @@ export interface CompileInfo { modifiedHTML?: string; importedContent?: string; imports?: Record; + errors?: string[]; } export interface CompileResult { From 4a9885264686c7712cbdbb30809a6d7434ee8003 Mon Sep 17 00:00:00 2001 From: Hatem Hosny Date: Sat, 13 Sep 2025 04:15:37 +0300 Subject: [PATCH 3/8] gleam compiler - return error message --- src/livecodes/languages/gleam/lang-gleam-compiler.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/livecodes/languages/gleam/lang-gleam-compiler.ts b/src/livecodes/languages/gleam/lang-gleam-compiler.ts index 016930926..621008547 100644 --- a/src/livecodes/languages/gleam/lang-gleam-compiler.ts +++ b/src/livecodes/languages/gleam/lang-gleam-compiler.ts @@ -2,6 +2,7 @@ /* eslint-disable max-classes-per-file */ import type { CompilerFunction } from '../../models'; +import { getErrorMessage } from '../../utils/utils'; import { gleamBaseUrl } from '../../vendors'; import { getLanguageCustomSettings } from '../utils'; import { modules, type Modules } from './gleam-modules'; @@ -193,7 +194,10 @@ import { modules, type Modules } from './gleam-modules'; } catch (error) { // eslint-disable-next-line no-console console.warn(error); - return ''; + return { + code: '', + info: { errors: [getErrorMessage(error)] }, + }; } finally { project.delete(); } From e258c5283e38d36b865717aefd3c570b04ccf4ff Mon Sep 17 00:00:00 2001 From: Hatem Hosny Date: Sat, 13 Sep 2025 05:01:56 +0300 Subject: [PATCH 4/8] svelte compiler - return error message --- .../languages/svelte/lang-svelte-compiler.ts | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/livecodes/languages/svelte/lang-svelte-compiler.ts b/src/livecodes/languages/svelte/lang-svelte-compiler.ts index ae2f44200..c8af9380f 100644 --- a/src/livecodes/languages/svelte/lang-svelte-compiler.ts +++ b/src/livecodes/languages/svelte/lang-svelte-compiler.ts @@ -2,6 +2,7 @@ import { compileAllBlocks } from '../../compiler/compile-blocks'; import { createImportMap, replaceSFCImports } from '../../compiler/import-map'; import { getCompileResult } from '../../compiler/utils'; import type { CompilerFunction, Config, Language } from '../../models'; +import { getErrorMessage } from '../../utils/utils'; import { getLanguageByAlias, getLanguageCustomSettings } from '../utils'; (self as any).createSvelteCompiler = (): CompilerFunction => { @@ -9,6 +10,7 @@ import { getLanguageByAlias, getLanguageCustomSettings } from '../utils'; const SECONDARY_FILE = '__LiveCodes_Component__.svelte'; let importedContent = ''; let imports: Record = {}; + let errors: string[] = []; const compileSvelteSFC = async ( code: string, @@ -42,21 +44,33 @@ import { getLanguageByAlias, getLanguageCustomSettings } from '../utils'; }); const customSettings = getLanguageCustomSettings('svelte', config); - const { js } = (window as any).svelte.compile(processedCode, { - css: 'injected', - filename, - ...customSettings, - }); - if (filename === MAIN_FILE || filename === SECONDARY_FILE) { imports = createImportMap(importedContent, config); + errors = []; + } + + 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' ? `` : empty, + info: { errors }, + }; } const compiledCode = filename === MAIN_FILE ? getMountCode(js.code) : js.code; return { code: language === 'svelte-app' ? `` : compiledCode, - info: { importedContent, imports }, + info: { importedContent, imports, errors }, }; }; From 9135d4ba0c59f9c00332a51389e9dedf4431b587 Mon Sep 17 00:00:00 2001 From: Hatem Hosny Date: Sat, 13 Sep 2025 05:21:30 +0300 Subject: [PATCH 5/8] vue compiler - return error message --- .../languages/vue/lang-vue-compiler.ts | 53 ++++++++++++------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/src/livecodes/languages/vue/lang-vue-compiler.ts b/src/livecodes/languages/vue/lang-vue-compiler.ts index a7c5f8e5c..212947a21 100644 --- a/src/livecodes/languages/vue/lang-vue-compiler.ts +++ b/src/livecodes/languages/vue/lang-vue-compiler.ts @@ -3,7 +3,7 @@ import { compileInCompiler } from '../../compiler/compile-in-compiler'; import { createImportMap, replaceSFCImports } from '../../compiler/import-map'; import type { LanguageOrProcessor } from '../../compiler/models'; import type { CompilerFunction, Config } from '../../models'; -import { getRandomString, replaceAsync } from '../../utils/utils'; +import { getErrorMessage, getRandomString, replaceAsync } from '../../utils/utils'; import { getLanguageByAlias } from '../utils'; // based on: @@ -363,35 +363,48 @@ import { getLanguageByAlias } from '../utils'; } return async (code, { config, language }) => { - const isMainFile = config.markup.language !== 'vue-app' || language === 'vue-app'; - const filename = isMainFile ? MAIN_FILE : SECONDARY_FILE; - const result = await compileVueSFC(code, { config, filename }); + try { + const isMainFile = config.markup.language !== 'vue-app' || language === 'vue-app'; + const filename = isMainFile ? MAIN_FILE : SECONDARY_FILE; + const result = await compileVueSFC(code, { config, filename }); - if (result) { - const { css, js } = result; + if (result) { + const { css, js } = result; - const injectCSS = !css.trim() - ? '' - : ` + const injectCSS = !css.trim() + ? '' + : ` document.head.insertBefore( Object.assign(document.createElement('style'), { textContent: ${JSON.stringify(css)} }), document.head.getElementsByTagName('style')[0] ); `; - const compiledCode = js + injectCSS; + const compiledCode = js + injectCSS; + + return { + code: + language === 'vue-app' + ? `` + : compiledCode, + info: { importedContent, imports: createImportMap(importedContent, config), errors }, + }; + } + + if (errors.length) { + // eslint-disable-next-line no-console + console.error(...errors); + } + const empty = `export default () => {}`; return { - code: - language === 'vue-app' ? `` : compiledCode, - info: { importedContent, imports: createImportMap(importedContent, config) }, + code: language === 'vue-app' ? `` : empty, + info: { errors }, + }; + } catch (err) { + return { + code: '', + info: { errors: [...errors, getErrorMessage(err)] }, }; } - - if (errors.length) { - // eslint-disable-next-line no-console - console.error(...errors); - } - - return ''; }; }; From 3f71897105b158aab7e350f69a68c2066b9ff979 Mon Sep 17 00:00:00 2001 From: Hatem Hosny Date: Sat, 13 Sep 2025 18:22:11 +0300 Subject: [PATCH 6/8] fixes --- src/livecodes/compiler/compile.worker.ts | 2 +- src/livecodes/core.ts | 6 +++--- src/livecodes/utils/utils.ts | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/livecodes/compiler/compile.worker.ts b/src/livecodes/compiler/compile.worker.ts index 3fe853021..6e1004dfa 100644 --- a/src/livecodes/compiler/compile.worker.ts +++ b/src/livecodes/compiler/compile.worker.ts @@ -108,7 +108,7 @@ const compile = async ( let value: string | CompileResult = ''; try { value = await compiler(content, { config, language, baseUrl, options }); - } catch (err: any) { + } catch (err: unknown) { // eslint-disable-next-line no-console console.error('Failed compiling: ' + language, err); value = { diff --git a/src/livecodes/core.ts b/src/livecodes/core.ts index fdb69bf80..8a56c838f 100644 --- a/src/livecodes/core.ts +++ b/src/livecodes/core.ts @@ -1026,10 +1026,12 @@ const getResultPage = async ({ markup: { ...contentConfig.markup, compiled: compiledMarkup, + modified: compiledMarkup, }, style: { ...contentConfig.style, compiled: compiledStyle, + modified: compiledStyle, }, script: { ...contentConfig.script, @@ -1044,6 +1046,7 @@ const getResultPage = async ({ compiled: compiledTests, }, }; + compiledCode.script.modified = compiledCode.script.compiled; if (scriptType != null && scriptType !== 'module') { singleFile = true; @@ -1077,9 +1080,6 @@ const getResultPage = async ({ result: cleanResultFromDev(result), styleOnlyUpdate, }); - updateCache('markup', markupLanguage, compiledMarkup); - updateCache('style', styleLanguage, compiledStyle); - updateCache('script', scriptLanguage, compiledScript); if (broadcastInfo.isBroadcasting) { broadcast(); diff --git a/src/livecodes/utils/utils.ts b/src/livecodes/utils/utils.ts index b23f3e073..9dc848039 100644 --- a/src/livecodes/utils/utils.ts +++ b/src/livecodes/utils/utils.ts @@ -643,7 +643,9 @@ export const compareObjects = /* @__PURE__ */ ( return diff; }; -export const getErrorMessage = /* @__PURE__ */ (err: unknown) => { +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; } From 17511a9b0979a05a085b4dae79c6ee4526e2e51c Mon Sep 17 00:00:00 2001 From: Hatem Hosny Date: Sat, 13 Sep 2025 18:25:07 +0300 Subject: [PATCH 7/8] fix --- src/livecodes/compiler/compile.worker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/livecodes/compiler/compile.worker.ts b/src/livecodes/compiler/compile.worker.ts index 6e1004dfa..34180d33f 100644 --- a/src/livecodes/compiler/compile.worker.ts +++ b/src/livecodes/compiler/compile.worker.ts @@ -116,7 +116,7 @@ const compile = async ( info: { errors: [getErrorMessage(err)] }, }; } - return value; + return value || ''; }; const loadTypeScript = async () => { From 2df2a7c8bd63149b911e29cb148a99b8f35093d3 Mon Sep 17 00:00:00 2001 From: Hatem Hosny Date: Sat, 13 Sep 2025 18:55:12 +0300 Subject: [PATCH 8/8] show language title in error message --- src/livecodes/core.ts | 14 +++++++------- .../languages/svelte/lang-svelte-compiler.ts | 3 +-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/livecodes/core.ts b/src/livecodes/core.ts index 8a56c838f..84cd3fb01 100644 --- a/src/livecodes/core.ts +++ b/src/livecodes/core.ts @@ -1065,13 +1065,13 @@ const getResultPage = async ({ const styleOnlyUpdate = sourceEditor === 'style' && !compileInfo.cssModules; - const compileErrors = [ - ...(markupCompileResult.info?.errors ?? []), - ...(styleCompileResult.info?.errors ?? []), - ...(scriptCompileResult.info?.errors ?? []), - ...(getCompileResult(testsCompileResult).info?.errors ?? []), - ]; - compileErrors.forEach((err) => toolsPane?.console?.error(err)); + const logError = (language: Language, errors: string[] = []) => { + errors.forEach((err) => toolsPane?.console?.error(`[${getLanguageTitle(language)}] ${err}`)); + }; + logError(markupLanguage, markupCompileResult.info?.errors); + logError(styleLanguage, styleCompileResult.info?.errors); + logError(scriptLanguage, scriptCompileResult.info?.errors); + logError(testsLanguage, getCompileResult(testsCompileResult).info?.errors); if (singleFile) { setCache({ diff --git a/src/livecodes/languages/svelte/lang-svelte-compiler.ts b/src/livecodes/languages/svelte/lang-svelte-compiler.ts index c8af9380f..8151f59fa 100644 --- a/src/livecodes/languages/svelte/lang-svelte-compiler.ts +++ b/src/livecodes/languages/svelte/lang-svelte-compiler.ts @@ -58,10 +58,9 @@ import { getLanguageByAlias, getLanguageCustomSettings } from '../utils'; }); js = result.js; } catch (err) { - const empty = `export default () => {}`; errors.push(getErrorMessage(err)); return { - code: language === 'svelte-app' ? `` : empty, + code: '', info: { errors }, }; }