diff --git a/deno.json b/deno.json index 0ba05bd..6e6bafd 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@mxdvl/mononykus", - "version": "0.7.5", + "version": "0.7.6", "exports": "./src/build.ts", "tasks": { "build": "deno run -A src/build.ts --site_dir src/_site", diff --git a/src/_site/components/Error.svelte b/src/_site/components/Error.svelte new file mode 100644 index 0000000..8ab41e8 --- /dev/null +++ b/src/_site/components/Error.svelte @@ -0,0 +1,3 @@ + diff --git a/src/_site/components/errors/InvalidJavaScript.island.svelte b/src/_site/components/errors/InvalidJavaScript.island.svelte new file mode 100644 index 0000000..af53780 --- /dev/null +++ b/src/_site/components/errors/InvalidJavaScript.island.svelte @@ -0,0 +1,4 @@ + diff --git a/src/_site/components/errors/InvalidStyles.island.svelte b/src/_site/components/errors/InvalidStyles.island.svelte new file mode 100644 index 0000000..a8263e5 --- /dev/null +++ b/src/_site/components/errors/InvalidStyles.island.svelte @@ -0,0 +1,3 @@ + diff --git a/src/_site/components/errors/Throw.island.svelte b/src/_site/components/errors/Throw.island.svelte new file mode 100644 index 0000000..f29538a --- /dev/null +++ b/src/_site/components/errors/Throw.island.svelte @@ -0,0 +1,3 @@ + + import Error from "../components/Error.svelte"; + + +This will never render diff --git a/src/build.ts b/src/build.ts index cb45564..aa4c43f 100644 --- a/src/build.ts +++ b/src/build.ts @@ -110,11 +110,13 @@ export const rebuild = async ({ ...baseESBuildConfig, }; - await Promise.all([ + const results = await Promise.allSettled([ esbuild.build(routesESBuildConfig), esbuild.build(islandsESBuildConfig), copy_assets({ site_dir, out_dir }), ]); + const issues = results.filter(({ status }) => status === "rejected").length; + if (issues > 0) console.warn(`Encoutered ${issues} issues`); }; export const build = async ( diff --git a/src/esbuild_plugins/build_routes.ts b/src/esbuild_plugins/build_routes.ts index 9f3b563..901f9fb 100644 --- a/src/esbuild_plugins/build_routes.ts +++ b/src/esbuild_plugins/build_routes.ts @@ -17,7 +17,7 @@ export const build_routes: Plugin = { const routes = result.outputFiles ?? []; - await Promise.all(routes.map(async (route) => { + await Promise.allSettled(routes.map(async (route) => { const module = await import( "data:application/javascript," + encodeURIComponent(route.text) ) as { @@ -50,7 +50,12 @@ export const build_routes: Plugin = { dist_path, await get_route_html({ html, css, head: deduped_head }), ); - })); + })).then((results) => { + const { length } = results.filter(({ status }) => + status === "rejected" + ); + if (length > 0) console.log(`Failed to build ${length} routes.`); + }); console.log( `Built ${routes.length} routes in ${ diff --git a/src/esbuild_plugins/svelte_components.ts b/src/esbuild_plugins/svelte_components.ts index d04ca3d..a3c18b6 100644 --- a/src/esbuild_plugins/svelte_components.ts +++ b/src/esbuild_plugins/svelte_components.ts @@ -38,6 +38,29 @@ const SVELTE_IMPORTS = /(from|import) ['"](?:svelte)(\/?[\w\/-]*)['"]/g; const specifiers = (code: string) => code.replaceAll(SVELTE_IMPORTS, `$1 'npm:svelte@${VERSION}$2'`); +/** From https://esbuild.github.io/plugins/#svelte-plugin */ +const convertMessage = ( + path: string, + source: string, + { message, start, end }: ReturnType["warnings"][number], +) => { + if (!start || !end) { + return { text: message }; + } + const lineText = source.split(/\r\n|\r|\n/g)[start.line - 1]; + const lineEnd = start.line === end.line ? end.column : lineText?.length ?? 0; + return { + text: message, + location: { + file: path, + line: start.line, + column: start.column, + length: lineEnd - start.column, + lineText, + }, + }; +}; + export const svelte_components = ( site_dir: string, base_path: string, @@ -96,52 +119,66 @@ export const svelte_components = ( ? OneClaw({ path, name, module_src }) : await Deno.readTextFile(path); - const { js: { code } } = compile(source, { - generate, - css: "external", - cssHash: ({ hash, css }) => `◖${hash(css)}◗`, - hydratable: generate === "dom", - enableSourcemap: false, - filename: basename(path), - }); - - if (generate === "dom" && path.endsWith(".island.svelte")) { - /** Dynamic function to be inlined in the output. */ - const hydrator = (name: string, Component: ComponentType) => { - try { - document.querySelectorAll( - `one-claw[name='${name}']:not(one-claw one-claw)`, - ).forEach((target) => { - const load = performance.now(); - console.groupCollapsed( - `Hydrating %c${name}%c`, - "color: orange", - "color: reset", - ); - console.log(target); - const props = JSON.parse(target.getAttribute("props") ?? "{}"); - new Component({ target, props, hydrate: true }); - console.log( - `Done in %c${ - Math.round((performance.now() - load) * 1000) / 1000 - }ms`, - "color: orange", - ); - console.groupEnd(); - }); - } catch (error) { - console.error(error); - } - }; - - return ({ - contents: `${ - specifiers(code) - };(${hydrator.toString()})("${name}", ${name}_island)`, + try { + const { js: { code }, warnings } = compile(source, { + generate, + css: "external", + cssHash: ({ hash, css }) => `◖${hash(css)}◗`, + hydratable: generate === "dom", + enableSourcemap: false, + filename: basename(path), }); - } - return ({ contents: specifiers(code) }); + if (generate === "dom" && path.endsWith(".island.svelte")) { + /** Dynamic function to be inlined in the output. */ + const hydrator = (name: string, Component: ComponentType) => { + try { + document.querySelectorAll( + `one-claw[name='${name}']:not(one-claw one-claw)`, + ).forEach((target) => { + const load = performance.now(); + console.groupCollapsed( + `Hydrating %c${name}%c`, + "color: orange", + "color: reset", + ); + console.log(target); + const props = JSON.parse(target.getAttribute("props") ?? "{}"); + new Component({ target, props, hydrate: true }); + console.log( + `Done in %c${ + Math.round((performance.now() - load) * 1000) / 1000 + }ms`, + "color: orange", + ); + console.groupEnd(); + }); + } catch (error) { + console.error(error); + } + }; + + return ({ + contents: `${ + specifiers(code) + };(${hydrator.toString()})("${name}", ${name}_island)`, + warnings: warnings.map( + (warning) => convertMessage(path, source, warning), + ), + }); + } + + return ({ contents: specifiers(code) }); + } catch (error) { + return { + errors: [ + convertMessage(path, source, { + message: String(error), + code: "???", + }), + ], + }; + } }); }, });