-
-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove nodejs dependency #3
Comments
Based on the reddit conversation, I'm thinking through how to run a V8 sandbox to compile svelte. The most current + supported project seems to be https://github.com/rogchap/v8go (also doesn't require us to build v8 ourselves). Even though the actual script might not be faster than running node directly, I think it would speed up the build because we could cut out the V8go can't require/import other scripts (see rogchap/v8go#22) so we'll have to rethink a couple of things:
|
Watching for a Go API on esbuild: evanw/esbuild#152 |
Esbuild Go API docs: https://github.com/evanw/esbuild/blob/master/docs/go-api.md |
Proposal:
Things to keep in mind:
|
We can remove require-relative by just pulling the part we need into import Module from 'module';
let root = new Module();
let htmlWrapper = '/home/jimafisk/Desktop/my-site/layout/global/html.svelte';
let component = root.require(htmlWrapper).default; Where component is equal to let { html, css } = component.render(props); |
The current implementation using v8go is working for the client build, but it might be worth looking into QuickJS for speed improvements and module support:
Another conversation recommending QuickJS: https://www.reddit.com/r/golang/comments/cd5gja/does_anyone_have_experience_with_parsing/ List of embeddable javascript interpreters: https://gist.github.com/maxogden/c61a58498c1933ece598 Examples:
Tried otto but it took almost 4 minutes to compile the client SPA, vs about 300ms with v8go: |
Tried some rough benchmarking of QuickJS against v8go:
Code Usedpackage main
import (
"fmt"
"time"
"github.com/lithdew/quickjs"
"rogchap.com/v8go"
)
func main() {
start := time.Now()
rt := quickjs.NewRuntime()
ctx := rt.NewContext()
result, err := ctx.Eval(`
function factorial(n) {
return n === 1 ? n : n * factorial(--n);
}
var i = 0;
while (i++ < 1e6) {
factorial(10);
}
`)
if err != nil {
fmt.Printf("Eval error: %v", err)
}
fmt.Println(result.String())
elapsed := time.Since(start)
fmt.Println(elapsed)
start = time.Now()
vm, _ := v8go.NewIsolate()
ctx1, _ := v8go.NewContext(vm)
result1, err1 := ctx1.RunScript(`
function factorial(n) {
return n === 1 ? n : n * factorial(--n);
}
var i = 0;
while (i++ < 1e6) {
factorial(10);
}
`, "math.js")
if err1 != nil {
fmt.Printf("Eval error: %v", err1)
}
fmt.Println(result1)
elapsed = time.Since(start)
fmt.Println(elapsed)
} |
Wrapping my head around process for creating static html in Svelte. A simplified version of how we're getting static HTML using NodeJS looks something like this (click to reveal)Example staticBuildStr format[
{
"node": {
"path": "/blog/adding_pletiform",
"type": "blog",
"filename": "adding_pletiform.json",
"fields": {
"title": "Build sites with good form",
"body": [ "Need an easy webform solution?", "Try adding a <a href='https://plentiform.com' target='blank' rel='noopener noreferrer'>plentiform</a>! (Coming soon)" ],
"author": "Jim Fisk",
"date": "1/26/2020"
}
},
"componentPath": "layout/content/blog.svelte",
"destPath": "public//blog/adding_pletiform/index.html"
},
...
] import 'svelte/register.js';
let htmlWrapper = path.join(path.resolve(), 'layout/global/html.svelte')
let root = new Module();
let component = root.require(htmlWrapper).default;
let staticBuildStr = JSON.parse(args[0]); // See example format above
staticBuildStr.forEach(arg => {
const route = root.require(arg.componentPath).default;
let props = {
route: route,
node: arg.node
}
let { html, css } = component.render(props);
} What's actually happening behind the scenes is it looks like Manually running the svelte compiler with SSR as a test in plenti's ctx.RunScript("var { js, css } = svelte.compile(`"+componentStr+"`, {generate: 'ssr'});", "compile_svelte")
jsCode, _ := ctx.RunScript("js.code;", "compile_svelte")
fmt.Println(jsCode) Returns SSR components that look like this (click to reveal)layout/global/html.svelte/* generated by Svelte v3.23.2 */
import {
create_ssr_component,
missing_component,
validate_component
} from "svelte/internal";
import Head from "./head.svelte";
import Nav from "./nav.svelte";
import Footer from "./footer.svelte";
import { makeTitle } from "../scripts/make_title.svelte";
const css = {
code: "body.svelte-sk4nou{font-family:'Rubik', sans-serif;display:flex;flex-direction:column;margin:0}main.svelte-sk4nou{flex-grow:1}.container{max-width:1024px;margin:0 auto;flex-grow:1;padding:0 20px}:root{--primary:rgb(34, 166, 237);--primary-dark:rgb(16, 92, 133);--accent:rgb(254, 211, 48);--base:rgb(245, 245, 245);--base-dark:rgb(17, 17, 17)}main a{position:relative;text-decoration:none;color:var(--base-dark);padding-bottom:5px}main a:before{content:\"\";width:100%;height:100%;background-image:linear-gradient(to top, var(--accent) 25%, rgba(0, 0, 0, 0) 40%);position:absolute;left:0;bottom:2px;z-index:-1;will-change:width;transform:rotate(-2deg);transform-origin:left bottom;transition:width .1s ease-out}main a:hover:before{width:0;transition-duration:.15s}",
map: "{\"version\":3,\"file\":null,\"sources\":[null],\"sourcesContent\":[\"<script>\\n import Head from './head.svelte';\\n import Nav from './nav.svelte';\\n import Footer from './footer.svelte';\\n import { makeTitle } from '../scripts/make_title.svelte';\\n\\n export let route, node, allNodes;\\n</script>\\n\\n<html lang=\\\"en\\\">\\n<Head title={makeTitle(node.filename)} />\\n<body>\\n <Nav />\\n <main>\\n <div class=\\\"container\\\">\\n <svelte:component this={route} {...node.fields} {allNodes} />\\n <br />\\n </div>\\n </main>\\n <Footer {allNodes} />\\n</body>\\n</html>\\n\\n<style>\\n body {\\n font-family: 'Rubik', sans-serif;\\n display: flex;\\n flex-direction: column;\\n margin: 0;\\n }\\n main {\\n flex-grow: 1;\\n }\\n :global(.container) {\\n max-width: 1024px;\\n margin: 0 auto;\\n flex-grow: 1;\\n padding: 0 20px;\\n }\\n :global(:root) {\\n --primary: rgb(34, 166, 237);\\n --primary-dark: rgb(16, 92, 133);\\n --accent: rgb(254, 211, 48);\\n --base: rgb(245, 245, 245);\\n --base-dark: rgb(17, 17, 17);\\n }\\n :global(main a) {\\n position: relative;\\n text-decoration: none;\\n color: var(--base-dark);\\n padding-bottom: 5px;\\n }\\n :global(main a:before) {\\n content: \\\"\\\";\\n width: 100%;\\n height: 100%;\\n background-image: linear-gradient(to top, var(--accent) 25%, rgba(0, 0, 0, 0) 40%); \\n position: absolute;\\n left: 0;\\n bottom: 2px;\\n z-index: -1; \\n will-change: width;\\n transform: rotate(-2deg);\\n transform-origin: left bottom;\\n transition: width .1s ease-out;\\n }\\n :global(main a:hover:before) {\\n width: 0;\\n transition-duration: .15s;\\n }\\n</style>\\n\"],\"names\":[],\"mappings\":\"AAwBE,IAAI,cAAC,CAAC,AACJ,WAAW,CAAE,OAAO,CAAC,CAAC,UAAU,CAChC,OAAO,CAAE,IAAI,CACb,cAAc,CAAE,MAAM,CACtB,MAAM,CAAE,CAAC,AACX,CAAC,AACD,IAAI,cAAC,CAAC,AACJ,SAAS,CAAE,CAAC,AACd,CAAC,AACO,UAAU,AAAE,CAAC,AACnB,SAAS,CAAE,MAAM,CACjB,MAAM,CAAE,CAAC,CAAC,IAAI,CACd,SAAS,CAAE,CAAC,CACZ,OAAO,CAAE,CAAC,CAAC,IAAI,AACjB,CAAC,AACO,KAAK,AAAE,CAAC,AACd,SAAS,CAAE,iBAAiB,CAC5B,cAAc,CAAE,gBAAgB,CAChC,QAAQ,CAAE,iBAAiB,CAC3B,MAAM,CAAE,kBAAkB,CAC1B,WAAW,CAAE,eAAe,AAC9B,CAAC,AACO,MAAM,AAAE,CAAC,AACf,QAAQ,CAAE,QAAQ,CAClB,eAAe,CAAE,IAAI,CACrB,KAAK,CAAE,IAAI,WAAW,CAAC,CACvB,cAAc,CAAE,GAAG,AACrB,CAAC,AACO,aAAa,AAAE,CAAC,AACtB,OAAO,CAAE,EAAE,CACX,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CACZ,gBAAgB,CAAE,gBAAgB,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAClF,QAAQ,CAAE,QAAQ,CAClB,IAAI,CAAE,CAAC,CACP,MAAM,CAAE,GAAG,CACX,OAAO,CAAE,EAAE,CACX,WAAW,CAAE,KAAK,CAClB,SAAS,CAAE,OAAO,KAAK,CAAC,CACxB,gBAAgB,CAAE,IAAI,CAAC,MAAM,CAC7B,UAAU,CAAE,KAAK,CAAC,GAAG,CAAC,QAAQ,AAChC,CAAC,AACO,mBAAmB,AAAE,CAAC,AAC5B,KAAK,CAAE,CAAC,CACR,mBAAmB,CAAE,IAAI,AAC3B,CAAC\"}"
};
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
let { route } = $$props, { node } = $$props, { allNodes } = $$props;
if ($$props.route === void 0 && $$bindings.route && route !== void 0) $$bindings.route(route);
if ($$props.node === void 0 && $$bindings.node && node !== void 0) $$bindings.node(node);
if ($$props.allNodes === void 0 && $$bindings.allNodes && allNodes !== void 0) $$bindings.allNodes(allNodes);
$$result.css.add(css);
return `<html lang="${"en"}">${validate_component(Head, "Head").$$render($$result, { title: makeTitle(node.filename) }, {}, {})}
<body class="${"svelte-sk4nou"}">${validate_component(Nav, "Nav").$$render($$result, {}, {}, {})}
<main class="${"svelte-sk4nou"}"><div class="${"container"}">${validate_component(route || missing_component, "svelte:component").$$render($$result, Object.assign(node.fields, { allNodes }), {}, {})}
<br></div></main>
${validate_component(Footer, "Footer").$$render($$result, { allNodes }, {}, {})}</body>
</html>`;
});
export default Component; layout/content/pages.svelte/* generated by Svelte v3.23.2 */
import {
create_ssr_component,
each,
escape,
validate_component
} from "svelte/internal";
import Uses from "../components/template.svelte";
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
let { title } = $$props, { description } = $$props;
if ($$props.title === void 0 && $$bindings.title && title !== void 0) $$bindings.title(title);
if ($$props.description === void 0 && $$bindings.description && description !== void 0) $$bindings.description(description);
return `<h1>${escape(title)}</h1>
<div>${each(description, paragraph => `<p>${paragraph}</p>`)}</div>
${validate_component(Uses, "Uses").$$render($$result, { type: "pages" }, {}, {})}
<p><a href="${"/"}">Back home</a></p>`;
});
export default Component; layout/content/index.svelte/* generated by Svelte v3.23.2 */
import {
create_ssr_component,
each,
escape,
is_promise,
missing_component,
validate_component
} from "svelte/internal";
import Grid from "../components/grid.svelte";
import { loadComponent } from "../scripts/load_component.svelte";
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
let { title } = $$props,
{ intro } = $$props,
{ components } = $$props,
{ allNodes } = $$props;
if ($$props.title === void 0 && $$bindings.title && title !== void 0) $$bindings.title(title);
if ($$props.intro === void 0 && $$bindings.intro && intro !== void 0) $$bindings.intro(intro);
if ($$props.components === void 0 && $$bindings.components && components !== void 0) $$bindings.components(components);
if ($$props.allNodes === void 0 && $$bindings.allNodes && allNodes !== void 0) $$bindings.allNodes(allNodes);
return `<h1>${escape(title)}</h1>
<section id="${"intro"}"><p>${intro.slogan}</p></section>
<section id="${"intro"}">${each(intro.help, paragraph => `<p>${paragraph}</p>`)}</section>
<div><h3>Recent blog posts:</h3>
${validate_component(Grid, "Grid").$$render($$result, { items: allNodes, filter: "blog" }, {}, {})}
<br></div>
${components
? `${each(components, ({ component, fields }) => `${(function (__value) {
if (is_promise(__value)) return `
loading component...
`;
return (function (compClass) {
return `
${validate_component(compClass || missing_component, "svelte:component").$$render($$result, Object.assign(fields), {}, {})}
`;
})(__value);
})(loadComponent(component))}`)}`
: ``}`;
});
export default Component; layout/components/template.svelte/* generated by Svelte v3.23.2 */
import {
add_attribute,
create_ssr_component,
escape,
null_to_empty
} from "svelte/internal";
const css = {
code: ".template.svelte-kyi9jr{display:flex;align-items:center}pre.svelte-kyi9jr{display:flex;padding-left:5px}code.svelte-kyi9jr{background-color:var(--base);padding:5px 10px}code.copied.svelte-kyi9jr{background-color:var(--accent)}button.svelte-kyi9jr{border:1px solid rgba(0,0,0,.1);background:white;padding:4px;border-top-right-radius:5px;border-bottom-right-radius:5px;cursor:pointer}",
map: "{\"version\":3,\"file\":null,\"sources\":[null],\"sourcesContent\":[\"<script>\\n export let type;\\n\\n let path;\\n let copyText = \\\"Copy\\\";\\n const copy = async () => {\\n if (!navigator.clipboard) {\\n return\\n }\\n try {\\n copyText = \\\"Copied\\\";\\n await navigator.clipboard.writeText(path.innerHTML);\\n setTimeout(() => copyText = \\\"Copy\\\", 500);\\n } catch (err) {\\n console.error('Failed to copy!', err)\\n }\\n }\\n</script>\\n\\n<div class=\\\"template\\\">\\n <span>Template:</span>\\n <pre>\\n <code bind:this={path} class=\\\"{copyText}\\\">layout/content/{type}.svelte</code>\\n <button on:click={copy}>{copyText}</button>\\n </pre>\\n</div>\\n\\n<style>\\n .template {\\n display: flex;\\n align-items: center;\\n }\\n pre {\\n display: flex;\\n padding-left: 5px;\\n }\\n code {\\n background-color: var(--base);\\n padding: 5px 10px;\\n }\\n code.copied {\\n background-color: var(--accent);\\n }\\n button {\\n border: 1px solid rgba(0,0,0,.1);\\n background: white;\\n padding: 4px;\\n border-top-right-radius: 5px;\\n border-bottom-right-radius: 5px;\\n cursor: pointer;\\n }\\n</style>\"],\"names\":[],\"mappings\":\"AA4BE,SAAS,cAAC,CAAC,AACT,OAAO,CAAE,IAAI,CACb,WAAW,CAAE,MAAM,AACrB,CAAC,AACD,GAAG,cAAC,CAAC,AACH,OAAO,CAAE,IAAI,CACb,YAAY,CAAE,GAAG,AACnB,CAAC,AACD,IAAI,cAAC,CAAC,AACF,gBAAgB,CAAE,IAAI,MAAM,CAAC,CAC7B,OAAO,CAAE,GAAG,CAAC,IAAI,AACrB,CAAC,AACD,IAAI,OAAO,cAAC,CAAC,AACT,gBAAgB,CAAE,IAAI,QAAQ,CAAC,AACnC,CAAC,AACD,MAAM,cAAC,CAAC,AACN,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAChC,UAAU,CAAE,KAAK,CACjB,OAAO,CAAE,GAAG,CACZ,uBAAuB,CAAE,GAAG,CAC5B,0BAA0B,CAAE,GAAG,CAC/B,MAAM,CAAE,OAAO,AACjB,CAAC\"}"
};
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
let { type } = $$props;
let path;
let copyText = "Copy";
const copy = async () => {
if (!navigator.clipboard) {
return;
}
try {
copyText = "Copied";
await navigator.clipboard.writeText(path.innerHTML);
setTimeout(() => copyText = "Copy", 500);
} catch(err) {
console.error("Failed to copy!", err);
}
};
if ($$props.type === void 0 && $$bindings.type && type !== void 0) $$bindings.type(type);
$$result.css.add(css);
return `<div class="${"template svelte-kyi9jr"}"><span>Template:</span>
<pre class="${"svelte-kyi9jr"}"><code class="${escape(null_to_empty(copyText)) + " svelte-kyi9jr"}"${add_attribute("this", path, 1)}>layout/content/${escape(type)}.svelte</code>
<button class="${"svelte-kyi9jr"}">${escape(copyText)}</button></pre>
</div>`;
});
export default Component; layout/ejected/router.svelte/* generated by Svelte v3.23.2 */
import { create_ssr_component, validate_component } from "svelte/internal";
import Navaid from "navaid";
import nodes from "./nodes.js";
import Html from "../global/html.svelte";
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
let route, node, allNodes;
const getNode = (uri, trailingSlash = "") => {
return nodes.find(node => node.path + trailingSlash == uri);
};
let uri = location.pathname;
node = getNode(uri);
if (node === undefined) {
node = getNode(uri, "/");
}
allNodes = nodes;
function draw(m) {
node = getNode(uri);
if (node === undefined) {
// Check if there is a 404 data source.
node = getNode("/404");
if (node === undefined) {
// If no 404.json data source exists, pass placeholder values.
node = {
"path": "/404",
"type": "404",
"filename": "404.json",
"fields": {}
};
}
}
route = m.default;
window.scrollTo(0, 0);
}
function track(obj) {
uri = obj.state || obj.uri;
}
addEventListener("replacestate", track);
addEventListener("pushstate", track);
addEventListener("popstate", track);
const handle404 = () => {
import("../content/404.js").then(draw).catch(err => {
console.log("Add a '/layout/content/404.svelte' file to handle Page Not Found errors.");
console.log("If you want to pass data to your 404 component, you can also add a '/content/404.json' file.");
console.log(err);
});
};
const router = Navaid("/", handle404);
allNodes.forEach(node => {
router.on(node.path, () => {
// Check if the url visited ends in a trailing slash (besides the homepage).
if (uri.length > 1 && uri.slice(-1) == "/") {
// Redirect to the same path without the trailing slash.
router.route(node.path, false);
} else {
import("../content/" + node.type + ".js").then(draw).catch(handle404);
}
});
});
router.listen();
return `${validate_component(Html, "Html").$$render($$result, { route, node, allNodes }, {}, {})}`;
});
export default Component; layout/global/head.svelte/* generated by Svelte v3.23.2 */
import { create_ssr_component, escape } from "svelte/internal";
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
let { title } = $$props;
if ($$props.title === void 0 && $$bindings.title && title !== void 0) $$bindings.title(title);
return `<head><meta charset="${"utf-8"}">
<meta name="${"viewport"}" content="${"width=device-width,initial-scale=1"}">
<title>${escape(title)}</title>
<link href="${"https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300;0,700;1,300&display=swap"}" rel="${"stylesheet"}">
<link rel="${"icon"}" type="${"image/svg+xml"}" href="${"/assets/favicon.svg"}">
<link rel="${"stylesheet"}" href="${"/spa/bundle.css"}"></head>`;
});
export default Component; layout/scripts/sort_by_date.svelte/* generated by Svelte v3.23.2 */
import { create_ssr_component } from "svelte/internal";
const sortByDate = (items, order) => {
items.sort((a, b) => {
// Must have a field specifically named "date" to work.
// Feel free to extend to other custom named date fields.
if (a.fields.hasOwnProperty("date") && b.fields.hasOwnProperty("date")) {
let aDate = new Date(a.fields.date);
let bDate = new Date(b.fields.date);
if (order == "oldest") {
return aDate - bDate;
}
return bDate - aDate;
}
});
return items;
};
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
return ``;
});
export default Component;
export { sortByDate }; Notably each starts with an If you look at let component = root.require(htmlWrapper).default;
console.log(component); Yields the following value:
|
Using v8go instead of Node is getting a lot closer, but there are persistent issues. One of those is importing components with custom names. Right now we add SSR compiled JS to the ctx for each component. We don't have a smart way of doing this so we're simply naming based on the filename (e.g. nav.svelte becomes Nav, pages.svelte becomes Pages, etc). This does not account for importing a component with a custom name, for instance the default starter does something like this: One solution is when reading the layout file, check for imports and when found assign the aliased name to the base component in the ctx. So using the above example: SSRctx.RunScript("var Uses = Template", "create_ssr") The downside is you would have a naming collision if different pages used the same alias for different components. A more robust solution might be isolating the ctx to the current component and its specific imports using a custom gopack-like lookup. Would need to consider the performance implications of setting something like this up. Currently the v8go implementation is roughly as fast as the node implementation. I'll do some benchmarking once this is a little more stable. |
New thought: give each component a signature based on its path and use that as the variable name where it's declared and everywhere it's referenced, e.g. When reading the file we will have to analyze relative paths (e.g. Before converting/* generated by Svelte v3.23.2 */
import {
create_ssr_component,
each,
escape,
validate_component
} from "svelte/internal";
import Uses from "../components/template.svelte";
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
let { title } = $$props, { description } = $$props;
if ($$props.title === void 0 && $$bindings.title && title !== void 0) $$bindings.title(title);
if ($$props.description === void 0 && $$bindings.description && description !== void 0) $$bindings.description(description);
return `<h1>${escape(title)}</h1>
<div>${each(description, paragraph => `<p>${paragraph}</p>`)}</div>
${validate_component(Uses, "Uses").$$render($$result, { type: "pages" }, {}, {})}
<p><a href="${"/"}">Back home</a></p>`;
});
export default Component; After converting/* generated by Svelte v3.23.2 */
/*import {
create_ssr_component,
each,
escape,
validate_component
} from "svelte/internal";*/
/*import Uses from "../components/template.svelte";*/
var layout_content_pages = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
let { title } = $$props, { description } = $$props;
if ($$props.title === void 0 && $$bindings.title && title !== void 0) $$bindings.title(title);
if ($$props.description === void 0 && $$bindings.description && description !== void 0) $$bindings.description(description);
return `<h1>${escape(title)}</h1>
<div>${each(description, paragraph => `<p>${paragraph}</p>`)}</div>
${validate_component(layout_components_template, "layout_component_template").$$render($$result, { type: "pages" }, {}, {})}
<p><a href="${"/"}">Back home</a></p>`;
});
/*export default Component;*/ Note we comment out the import/export statements in the SSR'd JS because v8go can't handle it. We essentially bundle the dependencies together manually in the vm ctx. For the same example above, here is the Template component that is being imported: Before being converted/* generated by Svelte v3.23.2 */
import {
add_attribute,
create_ssr_component,
escape,
null_to_empty
} from "svelte/internal";
const css = {
code: ".template.svelte-kyi9jr{display:flex;align-items:center}pre.svelte-kyi9jr{display:flex;padding-left:5px}code.svelte-kyi9jr{background-color:var(--base);padding:5px 10px}code.copied.svelte-kyi9jr{background-color:var(--accent)}button.svelte-kyi9jr{border:1px solid rgba(0,0,0,.1);background:white;padding:4px;border-top-right-radius:5px;border-bottom-right-radius:5px;cursor:pointer}",
map: "{\"version\":3,\"file\":null,\"sources\":[null],\"sourcesContent\":[\"<script>\\n export let type;\\n\\n let path;\\n let copyText = \\\"Copy\\\";\\n var copy = async () => {\\n if (!navigator.clipboard) {\\n return\\n }\\n try {\\n copyText = \\\"Copied\\\";\\n await navigator.clipboard.writeText(path.innerHTML);\\n setTimeout(() => copyText = \\\"Copy\\\", 500);\\n } catch (err) {\\n console.error('Failed to copy!', err)\\n }\\n }\\n</script>\\n\\n<div class=\\\"template\\\">\\n <span>Template:</span>\\n <pre>\\n <code bind:this={path} class=\\\"{copyText}\\\">layout/content/{type}.svelte</code>\\n <button on:click={copy}>{copyText}</button>\\n </pre>\\n</div>\\n\\n<style>\\n .template {\\n display: flex;\\n align-items: center;\\n }\\n pre {\\n display: flex;\\n padding-left: 5px;\\n }\\n code {\\n background-color: var(--base);\\n padding: 5px 10px;\\n }\\n code.copied {\\n background-color: var(--accent);\\n }\\n button {\\n border: 1px solid rgba(0,0,0,.1);\\n background: white;\\n padding: 4px;\\n border-top-right-radius: 5px;\\n border-bottom-right-radius: 5px;\\n cursor: pointer;\\n }\\n</style>\"],\"names\":[],\"mappings\":\"AA4BE,SAAS,cAAC,CAAC,AACT,OAAO,CAAE,IAAI,CACb,WAAW,CAAE,MAAM,AACrB,CAAC,AACD,GAAG,cAAC,CAAC,AACH,OAAO,CAAE,IAAI,CACb,YAAY,CAAE,GAAG,AACnB,CAAC,AACD,IAAI,cAAC,CAAC,AACF,gBAAgB,CAAE,IAAI,MAAM,CAAC,CAC7B,OAAO,CAAE,GAAG,CAAC,IAAI,AACrB,CAAC,AACD,IAAI,OAAO,cAAC,CAAC,AACT,gBAAgB,CAAE,IAAI,QAAQ,CAAC,AACnC,CAAC,AACD,MAAM,cAAC,CAAC,AACN,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAChC,UAAU,CAAE,KAAK,CACjB,OAAO,CAAE,GAAG,CACZ,uBAAuB,CAAE,GAAG,CAC5B,0BAA0B,CAAE,GAAG,CAC/B,MAAM,CAAE,OAAO,AACjB,CAAC\"}"
};
const Component = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
let { type } = $$props;
let path;
let copyText = "Copy";
var copy = async () => {
if (!navigator.clipboard) {
return;
}
try {
copyText = "Copied";
await navigator.clipboard.writeText(path.innerHTML);
setTimeout(() => copyText = "Copy", 500);
} catch(err) {
console.error("Failed to copy!", err);
}
};
if ($$props.type === void 0 && $$bindings.type && type !== void 0) $$bindings.type(type);
$$result.css.add(css);
return `<div class="${"template svelte-kyi9jr"}"><span>Template:</span>
<pre class="${"svelte-kyi9jr"}"><code class="${escape(null_to_empty(copyText)) + " svelte-kyi9jr"}"${add_attribute("this", path, 1)}>layout/content/${escape(type)}.svelte</code>
<button class="${"svelte-kyi9jr"}">${escape(copyText)}</button></pre>
</div>`;
});
export default Component; After being converted/* generated by Svelte v3.23.2 */
/*import {
add_attribute,
create_ssr_component,
escape,
null_to_empty
} from "svelte/internal";*/
var css = {
code: ".template.svelte-kyi9jr{display:flex;align-items:center}pre.svelte-kyi9jr{display:flex;padding-left:5px}code.svelte-kyi9jr{background-color:var(--base);padding:5px 10px}code.copied.svelte-kyi9jr{background-color:var(--accent)}button.svelte-kyi9jr{border:1px solid rgba(0,0,0,.1);background:white;padding:4px;border-top-right-radius:5px;border-bottom-right-radius:5px;cursor:pointer}",
map: "{\"version\":3,\"file\":null,\"sources\":[null],\"sourcesContent\":[\"<script>\\n export let type;\\n\\n let path;\\n let copyText = \\\"Copy\\\";\\n var copy = async () => {\\n if (!navigator.clipboard) {\\n return\\n }\\n try {\\n copyText = \\\"Copied\\\";\\n await navigator.clipboard.writeText(path.innerHTML);\\n setTimeout(() => copyText = \\\"Copy\\\", 500);\\n } catch (err) {\\n console.error('Failed to copy!', err)\\n }\\n }\\n</script>\\n\\n<div class=\\\"template\\\">\\n <span>Template:</span>\\n <pre>\\n <code bind:this={path} class=\\\"{copyText}\\\">layout/content/{type}.svelte</code>\\n <button on:click={copy}>{copyText}</button>\\n </pre>\\n</div>\\n\\n<style>\\n .template {\\n display: flex;\\n align-items: center;\\n }\\n pre {\\n display: flex;\\n padding-left: 5px;\\n }\\n code {\\n background-color: var(--base);\\n padding: 5px 10px;\\n }\\n code.copied {\\n background-color: var(--accent);\\n }\\n button {\\n border: 1px solid rgba(0,0,0,.1);\\n background: white;\\n padding: 4px;\\n border-top-right-radius: 5px;\\n border-bottom-right-radius: 5px;\\n cursor: pointer;\\n }\\n</style>\"],\"names\":[],\"mappings\":\"AA4BE,SAAS,cAAC,CAAC,AACT,OAAO,CAAE,IAAI,CACb,WAAW,CAAE,MAAM,AACrB,CAAC,AACD,GAAG,cAAC,CAAC,AACH,OAAO,CAAE,IAAI,CACb,YAAY,CAAE,GAAG,AACnB,CAAC,AACD,IAAI,cAAC,CAAC,AACF,gBAAgB,CAAE,IAAI,MAAM,CAAC,CAC7B,OAAO,CAAE,GAAG,CAAC,IAAI,AACrB,CAAC,AACD,IAAI,OAAO,cAAC,CAAC,AACT,gBAAgB,CAAE,IAAI,QAAQ,CAAC,AACnC,CAAC,AACD,MAAM,cAAC,CAAC,AACN,MAAM,CAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAChC,UAAU,CAAE,KAAK,CACjB,OAAO,CAAE,GAAG,CACZ,uBAAuB,CAAE,GAAG,CAC5B,0BAA0B,CAAE,GAAG,CAC/B,MAAM,CAAE,OAAO,AACjB,CAAC\"}"
};
var layout_components_template = create_ssr_component(($$result, $$props, $$bindings, $$slots) => {
let { type } = $$props;
let path;
let copyText = "Copy";
var copy = async () => {
if (!navigator.clipboard) {
return;
}
try {
copyText = "Copied";
await navigator.clipboard.writeText(path.innerHTML);
setTimeout(() => copyText = "Copy", 500);
} catch(err) {
console.error("Failed to copy!", err);
}
};
if ($$props.type === void 0 && $$bindings.type && type !== void 0) $$bindings.type(type);
$$result.css.add(css);
return `<div class="${"template svelte-kyi9jr"}"><span>Template:</span>
<pre class="${"svelte-kyi9jr"}"><code class="${escape(null_to_empty(copyText)) + " svelte-kyi9jr"}"${add_attribute("this", path, 1)}>layout/content/${escape(type)}.svelte</code>
<button class="${"svelte-kyi9jr"}">${escape(copyText)}</button></pre>
</div>`;
});
/*export default Component;*/ |
When client and static builds were separate (c40c1d5):
The go build compiled components and static html individually: |
Benchmarking:
|
In this hacker news thread leaveyou wrote:
batisteo responded with:
I remember looking into SWC early on, but doesn't seem like it made it onto this thread for some reason. I have some concerns about the amount of processing we're having to do to run everything in v8, but probably not going to make any switches in the short term. If there was a Svelte compiler written in Go with an API it would make things faster and more consistent for this project. It would also be nice not to require CGO so we could start cross compiling for Windows again. |
@jimafisk I just caught your talk on creating a Go SSG for Svelte: https://www.youtube.com/watch?v=4tD3Jz7JUfk, I’m about halfway through. I thought I’d comment here because you and I seem to be interested in the same problem space, I even tried making a SSG for React and just gave up and decided Svelte is the future. This is as far as I got: https://github.com/zaydek/retro. I figured out how to solve for these problems:
All that being said, I realized React is actually very problematic as the frontend target because it’s so reliant on Babel / ESLint, etc. that even if you solve the backend problem, you still have the whole DX problem which is significant and I don’t care enough about to solve simultaneously. So I thought I’d leave a comment here to get your attention and see if you want to connect. 🙂 Feel free to email me at zaydekdotcom [at] gmail or DM me on Twitter @username_ZAYDEK. I couldn’t easily find your email so I thought I’d just comment here. 😬 Solid work you got going on here. |
Retro looks cool @zaydek! I'd love to connect, just followed you on twitter and sent you an email :) |
How the svelte compiler works: https://dev.to/joshnuss/svelte-compiler-under-the-hood-4j20 + simplified example: https://github.com/joshnuss/micro-svelte-compiler |
I took another look at a pure Golang javascript interpreter to see if it was feasible to drop the complexities that come with a cgo dependency. Currently it does seem like that would come at a significant performance cost. Rough benchmarking of Goja against v8go:
Benchmarking codepackage main
import (
"fmt"
"time"
"github.com/dop251/goja"
"rogchap.com/v8go"
)
func main() {
start := time.Now()
gojavm := goja.New()
result, err := gojavm.RunString(`
function factorial(n) {
return n === 1 ? n : n * factorial(--n);
}
var i = 0;
while (i++ < 1e6) {
factorial(10);
}
`)
if err != nil {
fmt.Printf("Goja error: %v", err)
}
if result.Export().(int64) == 3628800 {
elapsed := time.Since(start)
fmt.Println(elapsed)
}
start = time.Now()
vm, _ := v8go.NewIsolate()
ctx1, _ := v8go.NewContext(vm)
result1, err1 := ctx1.RunScript(`
function factorial(n) {
return n === 1 ? n : n * factorial(--n);
}
var i = 0;
while (i++ < 1e6) {
factorial(10);
}
`, "math.js")
if err1 != nil {
fmt.Printf("Eval error: %v", err1)
}
if result1.String() == "3628800" {
elapsed := time.Since(start)
fmt.Println(elapsed)
}
} From Goja's README:
Running a factorial like the example used for the benchmark is a heavy computation:
The typical Plenti site might be lighter on computational requirements. If down the road we start moving Svelte compiling features into Go, there's a chance the performance between these projects would even out a bit:
Another consideration would be full ES6 compatibility: https://github.com/dop251/goja/milestone/1?closed=1 |
Also took a quick look at https://github.com/gojisvm/gojis, but the docs are still WIP (https://gojisvm.github.io/api.html) so it would need to mature before evaluating more seriously. Tried running the Svelte compiler.js inside Goja with es6 support: Test Goja codepackage main
import (
"fmt"
"io/ioutil"
"github.com/dop251/goja"
)
func main() {
compiler, err := ioutil.ReadFile("compiler.js")
if err != nil {
fmt.Println("Can't read compiler.js")
}
vm := goja.New()
result, err := vm.RunScript("test", string(compiler))
if err != nil {
fmt.Printf("Goja error: %v", err)
}
println(result.Export())
} Error message
In case it's useful, here's what you currently get out of the box with Plenti for build times: Default "learner" starter with v8go build benchmarks (ran 15 times individually)
|
It seems like Goja has come a long way since I last tested it. The ES6 milestone has been completed and I ran some performance tests against v8go again and the results were very different from last time:
🤏 = within 5ns So the numbers are much closer, in some cases goja even comes out ahead. My next step is to try using it to actually compile some svelte components and see how that goes. Benchmarking codepackage main
import (
"fmt"
"time"
"github.com/dop251/goja"
"rogchap.com/v8go"
)
func main() {
js1 := `
function factorial(n) {
return n === 1 ? n : n * factorial(--n);
}
var i = 0;
while (i++ < 1e6) {
factorial(10);
}`
js2 := `
function factorial(n) {
return n === 1 ? n : n * factorial(--n);
}
var i = 0;
while (i++ < 10) {
factorial(10);
}`
runV8go(js1)
runGoja(js1)
runV8go(js2)
runGoja(js2)
}
func runGoja(js string) {
elapsed := time.Since(time.Now())
vm := goja.New()
v, err := vm.RunString(js)
if err != nil {
fmt.Println(err)
}
fmt.Print(v)
fmt.Printf(" goja " + elapsed.String() + "\n")
}
func runV8go(js string) {
elapsed := time.Since(time.Now())
vm, _ := v8go.NewContext(nil)
v, err := vm.RunScript(js, "")
if err != nil {
fmt.Println(err)
}
fmt.Print(v)
fmt.Printf(" v8 " + elapsed.String() + "\n")
} |
https://github.com/zeit/pkg
Should:
npm install
)web_modules
for ESMThe text was updated successfully, but these errors were encountered: