diff --git a/src/livecodes/compiler/compile.worker.ts b/src/livecodes/compiler/compile.worker.ts index 6dc87c0e64..34180d33f4 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,13 +105,16 @@ 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: unknown) { // eslint-disable-next-line no-console console.error('Failed compiling: ' + language, err); - value = content; + value = { + code: '', + info: { errors: [getErrorMessage(err)] }, + }; } return value || ''; }; diff --git a/src/livecodes/core.ts b/src/livecodes/core.ts index 7bcc96f616..84cd3fb019 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, @@ -1027,10 +1026,12 @@ const getResultPage = async ({ markup: { ...contentConfig.markup, compiled: compiledMarkup, + modified: compiledMarkup, }, style: { ...contentConfig.style, compiled: compiledStyle, + modified: compiledStyle, }, script: { ...contentConfig.script, @@ -1045,6 +1046,7 @@ const getResultPage = async ({ compiled: compiledTests, }, }; + compiledCode.script.modified = compiledCode.script.compiled; if (scriptType != null && scriptType !== 'module') { singleFile = true; @@ -1063,6 +1065,14 @@ const getResultPage = async ({ const styleOnlyUpdate = sourceEditor === 'style' && !compileInfo.cssModules; + 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({ ...getCache(), @@ -1124,17 +1134,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: { diff --git a/src/livecodes/languages/gleam/lang-gleam-compiler.ts b/src/livecodes/languages/gleam/lang-gleam-compiler.ts index 0169309262..6210085474 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(); } diff --git a/src/livecodes/languages/svelte/lang-svelte-compiler.ts b/src/livecodes/languages/svelte/lang-svelte-compiler.ts index ae2f44200c..8151f59fa9 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,32 @@ 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) { + errors.push(getErrorMessage(err)); + return { + code: '', + 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 }, }; }; diff --git a/src/livecodes/languages/vue/lang-vue-compiler.ts b/src/livecodes/languages/vue/lang-vue-compiler.ts index a7c5f8e5c0..212947a217 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 ''; }; }; diff --git a/src/livecodes/utils/utils.ts b/src/livecodes/utils/utils.ts index 518bca4a05..9dc8480396 100644 --- a/src/livecodes/utils/utils.ts +++ b/src/livecodes/utils/utils.ts @@ -643,6 +643,15 @@ export const compareObjects = /* @__PURE__ */ ( return 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 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 e6bd21a015..50695a9bdd 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 {