diff --git a/.gitignore b/.gitignore
index 092a493..7fbd70c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,4 @@
/node_modules/
/src/node_modules/@sapper/
yarn-error.log
-/__sapper__/
/static/main.css
\ No newline at end of file
diff --git a/__sapper__/build/build.json b/__sapper__/build/build.json
new file mode 100644
index 0000000..243db50
--- /dev/null
+++ b/__sapper__/build/build.json
@@ -0,0 +1,36 @@
+{
+ "bundler": "rollup",
+ "shimport": "2.0.4",
+ "assets": {
+ "main": "client.c2670d50.js"
+ },
+ "css": {
+ "main": [
+ "client-e118e612.css"
+ ]
+ },
+ "dependencies": {
+ "index.svelte": [
+ "index.ade356b9.js",
+ "inject_styles.5607aec6.js",
+ "index-39716d32.css"
+ ],
+ "about.svelte": [
+ "about.e97c8bf2.js",
+ "inject_styles.5607aec6.js"
+ ],
+ "blog/index.svelte": [
+ "index.39882a62.js",
+ "inject_styles.5607aec6.js",
+ "index-7ed37c94.css"
+ ],
+ "blog/[slug].svelte": [
+ "[slug].fac92fe6.js",
+ "inject_styles.5607aec6.js",
+ "[slug]-5bc8f95f.css"
+ ]
+ },
+ "legacy_assets": {
+ "main": "client.eb14add1.js"
+ }
+}
\ No newline at end of file
diff --git a/__sapper__/build/client/465898c830bb9d2c.jpg b/__sapper__/build/client/465898c830bb9d2c.jpg
new file mode 100644
index 0000000..5ad19cc
Binary files /dev/null and b/__sapper__/build/client/465898c830bb9d2c.jpg differ
diff --git a/__sapper__/build/client/[slug]-5bc8f95f.css b/__sapper__/build/client/[slug]-5bc8f95f.css
new file mode 100644
index 0000000..7255deb
--- /dev/null
+++ b/__sapper__/build/client/[slug]-5bc8f95f.css
@@ -0,0 +1 @@
+.content.svelte-emm3f3 h2{font-size:1.4em;font-weight:500}.content.svelte-emm3f3 pre{background-color:#f9f9f9;box-shadow:inset 1px 1px 5px rgba(0, 0, 0, 0.05);padding:0.5em;border-radius:2px;overflow-x:auto}.content.svelte-emm3f3 pre code{background-color:transparent;padding:0}.content.svelte-emm3f3 ul{line-height:1.5}.content.svelte-emm3f3 li{margin:0 0 0.5em 0}
diff --git a/__sapper__/build/client/[slug].fac92fe6.js b/__sapper__/build/client/[slug].fac92fe6.js
new file mode 100644
index 0000000..a895691
--- /dev/null
+++ b/__sapper__/build/client/[slug].fac92fe6.js
@@ -0,0 +1 @@
+import{S as t,i as s,s as a,a as e,e as n,t as o,q as i,d as r,c,b as l,f as h,g as u,h as f,j as m,k as d,l as p,n as v}from"./client.c2670d50.js";function g(t){let s,a,g,j,x,E,H=t[0].title+"",$=t[0].html+"";return document.title=s=t[0].title,{c(){a=e(),g=n("h1"),j=o(H),x=e(),E=n("div"),this.h()},l(t){i('[data-svelte="svelte-1uty71u"]',document.head).forEach(r),a=c(t),g=l(t,"H1",{});var s=h(g);j=u(s,H),s.forEach(r),x=c(t),E=l(t,"DIV",{class:!0}),h(E).forEach(r),this.h()},h(){f(E,"class","content svelte-emm3f3")},m(t,s){m(t,a,s),m(t,g,s),d(g,j),m(t,x,s),m(t,E,s),E.innerHTML=$},p(t,[a]){1&a&&s!==(s=t[0].title)&&(document.title=s),1&a&&H!==(H=t[0].title+"")&&p(j,H),1&a&&$!==($=t[0].html+"")&&(E.innerHTML=$)},i:v,o:v,d(t){t&&r(a),t&&r(g),t&&r(x),t&&r(E)}}}async function j({params:t}){const s=await this.fetch(`blog/${t.slug}.json`),a=await s.json();if(200===s.status)return{post:a};this.error(s.status,a.message)}function x(t,s,a){let{post:e}=s;return t.$$set=t=>{"post"in t&&a(0,e=t.post)},[e]}export default class extends t{constructor(t){super(),s(this,t,x,g,a,{post:0})}}export{j as preload};
diff --git a/__sapper__/build/client/about.e97c8bf2.js b/__sapper__/build/client/about.e97c8bf2.js
new file mode 100644
index 0000000..fe32a2d
--- /dev/null
+++ b/__sapper__/build/client/about.e97c8bf2.js
@@ -0,0 +1 @@
+import{S as s,i as t,s as e,a,e as h,t as o,q as i,d as r,c,b as u,f as n,g as l,j as d,k as f,n as m}from"./client.c2670d50.js";function p(s){let t,e,p,b,v,T;return{c(){t=a(),e=h("h1"),p=o("About this site"),b=a(),v=h("p"),T=o("This is the 'about' page. There's not much here."),this.h()},l(s){i('[data-svelte="svelte-1ine71f"]',document.head).forEach(r),t=c(s),e=u(s,"H1",{});var a=n(e);p=l(a,"About this site"),a.forEach(r),b=c(s),v=u(s,"P",{});var h=n(v);T=l(h,"This is the 'about' page. There's not much here."),h.forEach(r),this.h()},h(){document.title="About"},m(s,a){d(s,t,a),d(s,e,a),f(e,p),d(s,b,a),d(s,v,a),f(v,T)},p:m,i:m,o:m,d(s){s&&r(t),s&&r(e),s&&r(b),s&&r(v)}}}export default class extends s{constructor(s){super(),t(this,s,null,p,e,{})}}
diff --git a/__sapper__/build/client/client-e118e612.css b/__sapper__/build/client/client-e118e612.css
new file mode 100644
index 0000000..f6eb2fd
--- /dev/null
+++ b/__sapper__/build/client/client-e118e612.css
@@ -0,0 +1,3 @@
+nav.svelte-1dbd5up{border-bottom:1px solid rgba(255,62,0,0.1);font-weight:300;padding:0 1em}ul.svelte-1dbd5up{margin:0;padding:0}ul.svelte-1dbd5up::after{content:'';display:block;clear:both}li.svelte-1dbd5up{display:block;float:left}[aria-current].svelte-1dbd5up{position:relative;display:inline-block}[aria-current].svelte-1dbd5up::after{position:absolute;content:'';width:calc(100% - 1em);height:2px;background-color:rgb(255,62,0);display:block;bottom:-1px}a.svelte-1dbd5up{text-decoration:none;padding:1em 0.5em;display:block}
+main.svelte-1uhnsl8{position:relative;max-width:56em;background-color:white;padding:2em;margin:0 auto;box-sizing:border-box}
+h1.svelte-8od9u6,p.svelte-8od9u6{margin:0 auto}h1.svelte-8od9u6{font-size:2.8em;font-weight:700;margin:0 0 0.5em 0}p.svelte-8od9u6{margin:1em auto}@media(min-width: 480px){h1.svelte-8od9u6{font-size:4em}}
diff --git a/__sapper__/build/client/client.c2670d50.js b/__sapper__/build/client/client.c2670d50.js
new file mode 100644
index 0000000..4287ba9
--- /dev/null
+++ b/__sapper__/build/client/client.c2670d50.js
@@ -0,0 +1,18 @@
+function t(){}function e(t,e){for(const n in e)t[n]=e[n];return t}function n(t){return t()}function r(){return Object.create(null)}function o(t){t.forEach(n)}function s(t){return"function"==typeof t}function c(t,e){return t!=t?e==e:t!==e||t&&"object"==typeof t||"function"==typeof t}function a(t,n,r,o){return t[1]&&o?e(r.ctx.slice(),t[1](o(n))):r.ctx}function i(t,e,n,r,o,s,c){const i=function(t,e,n,r){if(t[2]&&r){const o=t[2](r(n));if(void 0===e.dirty)return o;if("object"==typeof o){const t=[],n=Math.max(e.dirty.length,o.length);for(let r=0;r
Sapper is a Next.js-style framework (more on that here) built around Svelte. It makes it embarrassingly easy to create extremely high performance web apps. Out of the box, you get:
+ +It's implemented as Express middleware. Everything is set up and waiting for you to get started, but you keep complete control over the server, service worker, webpack config and everything else, so it's as flexible as you need it to be.
+ ` + }, + + { + title: 'How to use Sapper', + slug: 'how-to-use-sapper', + html: ` +Create a new project, using degit:
+ +npx degit "sveltejs/sapper-template#rollup" my-app
+ cd my-app
+ npm install # or yarn!
+ npm run dev
+
+
+ Go to localhost:3000. Open my-app
in your editor. Edit the files in the src/routes
directory or add new ones.
...
+ +Resist overdone joke formats.
+ ` + }, + + { + title: 'Why the name?', + slug: 'why-the-name', + html: ` +In war, the soldiers who build bridges, repair roads, clear minefields and conduct demolitions — all under combat conditions — are known as sappers.
+ +For web developers, the stakes are generally lower than those for combat engineers. But we face our own hostile environment: underpowered devices, poor network connections, and the complexity inherent in front-end engineering. Sapper, which is short for Svelte app maker, is your courageous and dutiful ally.
+ ` + }, + + { + title: 'How is Sapper different from Next.js?', + slug: 'how-is-sapper-different-from-next', + html: ` +Next.js is a React framework from Vercel, and is the inspiration for Sapper. There are a few notable differences, however:
+ +src/routes/blog/[slug].svelte
routes
directory. These are just .js
files that export functions corresponding to HTTP methods, and receive Express request
and response
objects as arguments. This makes it very easy to, for example, add a JSON API such as the one powering this very page<a>
elements, rather than framework-specific <Link>
components. That means, for example, that this link right here, despite being inside a blob of HTML, works with the router as you'd expect.We're so glad you asked! Come on over to the Svelte and Sapper repos, and join us in the Discord chatroom. Everyone is welcome, especially you!
+ ` + } +]; + +posts.forEach(post => { + post.html = post.html.replace(/^\t{3}/gm, ''); +}); + +const contents = JSON.stringify(posts.map(post => { + return { + title: post.title, + slug: post.slug + }; +})); + +function get$1(req, res) { + res.writeHead(200, { + 'Content-Type': 'application/json' + }); + + res.end(contents); +} + +var route_0 = /*#__PURE__*/Object.freeze({ + __proto__: null, + get: get$1 +}); + +const lookup = new Map(); +posts.forEach(post => { + lookup.set(post.slug, JSON.stringify(post)); +}); + +function get(req, res, next) { + // the `slug` parameter is available because + // this file is called [slug].json.js + const { slug } = req.params; + + if (lookup.has(slug)) { + res.writeHead(200, { + 'Content-Type': 'application/json' + }); + + res.end(lookup.get(slug)); + } else { + res.writeHead(404, { + 'Content-Type': 'application/json' + }); + + res.end(JSON.stringify({ + message: `Not found` + })); + } +} + +var route_1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + get: get +}); + +function noop$1() { } +function run(fn) { + return fn(); +} +function blank_object() { + return Object.create(null); +} +function run_all(fns) { + fns.forEach(run); +} +function safe_not_equal(a, b) { + return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function'); +} + +let current_component; +function set_current_component(component) { + current_component = component; +} +function get_current_component() { + if (!current_component) + throw new Error('Function called outside component initialization'); + return current_component; +} +function afterUpdate(fn) { + get_current_component().$$.after_update.push(fn); +} +function setContext(key, context) { + get_current_component().$$.context.set(key, context); +} +const escaped$1 = { + '"': '"', + "'": ''', + '&': '&', + '<': '<', + '>': '>' +}; +function escape(html) { + return String(html).replace(/["'&<>]/g, match => escaped$1[match]); +} +function each(items, fn) { + let str = ''; + for (let i = 0; i < items.length; i += 1) { + str += fn(items[i], i); + } + return str; +} +const missing_component = { + $$render: () => '' +}; +function validate_component(component, name) { + if (!component || !component.$$render) { + if (name === 'svelte:component') + name += ' this={...}'; + throw new Error(`<${name}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules`); + } + return component; +} +let on_destroy; +function create_ssr_component(fn) { + function $$render(result, props, bindings, slots, context) { + const parent_component = current_component; + const $$ = { + on_destroy, + context: new Map(parent_component ? parent_component.$$.context : context || []), + // these will be immediately discarded + on_mount: [], + before_update: [], + after_update: [], + callbacks: blank_object() + }; + set_current_component({ $$ }); + const html = fn(result, props, bindings, slots); + set_current_component(parent_component); + return html; + } + return { + render: (props = {}, { $$slots = {}, context = new Map() } = {}) => { + on_destroy = []; + const result = { title: '', head: '', css: new Set() }; + const html = $$render(result, props, {}, $$slots, context); + run_all(on_destroy); + return { + html, + css: { + code: Array.from(result.css).map(css => css.code).join('\n'), + map: null // TODO + }, + head: result.title + result.head + }; + }, + $$render + }; +} +function add_attribute(name, value, boolean) { + if (value == null || (boolean && !value)) + return ''; + return ` ${name}${value === true ? '' : `=${typeof value === 'string' ? JSON.stringify(escape(value)) : `"${value}"`}`}`; +} + +var successkid = "/client/465898c830bb9d2c.jpg"; + +/* src/routes/index.svelte generated by Svelte v3.37.0 */ + +const css$5 = { + code: "h1.svelte-1kk9opm,figure.svelte-1kk9opm,p.svelte-1kk9opm{text-align:center;margin:0 auto}h1.svelte-1kk9opm{font-size:2.8em;text-transform:uppercase;font-weight:700;margin:0 0 0.5em 0}figure.svelte-1kk9opm{margin:0 0 1em 0}img.svelte-1kk9opm{width:100%;max-width:400px;margin:0 0 1em 0}p.svelte-1kk9opm{margin:1em auto}@media(min-width: 480px){h1.svelte-1kk9opm{font-size:4em}}", + map: "{\"version\":3,\"file\":\"index.svelte\",\"sources\":[\"index.svelte\"],\"sourcesContent\":[\"\\n\\n\\n\\nTry editing this file (src/routes/index.svelte) to test live reloading.
\\n\"],\"names\":[],\"mappings\":\"AAKC,iBAAE,CAAE,qBAAM,CAAE,CAAC,eAAC,CAAC,AACd,UAAU,CAAE,MAAM,CAClB,MAAM,CAAE,CAAC,CAAC,IAAI,AACf,CAAC,AAED,EAAE,eAAC,CAAC,AACH,SAAS,CAAE,KAAK,CAChB,cAAc,CAAE,SAAS,CACzB,WAAW,CAAE,GAAG,CAChB,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,AACpB,CAAC,AAED,MAAM,eAAC,CAAC,AACP,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,AAClB,CAAC,AAED,GAAG,eAAC,CAAC,AACJ,KAAK,CAAE,IAAI,CACX,SAAS,CAAE,KAAK,CAChB,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,AAClB,CAAC,AAED,CAAC,eAAC,CAAC,AACF,MAAM,CAAE,GAAG,CAAC,IAAI,AACjB,CAAC,AAED,MAAM,AAAC,YAAY,KAAK,CAAC,AAAC,CAAC,AAC1B,EAAE,eAAC,CAAC,AACH,SAAS,CAAE,GAAG,AACf,CAAC,AACF,CAAC\"}" +}; + +const Routes = create_ssr_component(($$result, $$props, $$bindings, slots) => { + $$result.css.add(css$5); + + return `${($$result.head += `${($$result.title = `Try editing this file (src/routes/index.svelte) to test live reloading.
`; +}); + +var component_0 = /*#__PURE__*/Object.freeze({ + __proto__: null, + 'default': Routes +}); + +/* src/routes/about.svelte generated by Svelte v3.37.0 */ + +const About = create_ssr_component(($$result, $$props, $$bindings, slots) => { + return `${($$result.head += `${($$result.title = `This is the 'about' page. There's not much here.
`; +}); + +var component_1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + 'default': About +}); + +/* src/routes/blog/index.svelte generated by Svelte v3.37.0 */ + +const css$4 = { + code: "ul.svelte-1frg2tf{margin:0 0 1em 0;line-height:1.5}", + map: "{\"version\":3,\"file\":\"index.svelte\",\"sources\":[\"index.svelte\"],\"sourcesContent\":[\"\\n\\n\\n\\n\\n\\n{error.message}
\\n\\n{#if dev && error.stack}\\n\\t{error.stack}\\n{/if}\\n\"],\"names\":[],\"mappings\":\"AAQC,gBAAE,CAAE,CAAC,cAAC,CAAC,AACN,MAAM,CAAE,CAAC,CAAC,IAAI,AACf,CAAC,AAED,EAAE,cAAC,CAAC,AACH,SAAS,CAAE,KAAK,CAChB,WAAW,CAAE,GAAG,CAChB,MAAM,CAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,AACpB,CAAC,AAED,CAAC,cAAC,CAAC,AACF,MAAM,CAAE,GAAG,CAAC,IAAI,AACjB,CAAC,AAED,MAAM,AAAC,YAAY,KAAK,CAAC,AAAC,CAAC,AAC1B,EAAE,cAAC,CAAC,AACH,SAAS,CAAE,GAAG,AACf,CAAC,AACF,CAAC\"}" +}; + +const Error$1 = create_ssr_component(($$result, $$props, $$bindings, slots) => { + let { status } = $$props; + let { error } = $$props; + if ($$props.status === void 0 && $$bindings.status && status !== void 0) $$bindings.status(status); + if ($$props.error === void 0 && $$bindings.error && error !== void 0) $$bindings.error(error); + $$result.css.add(css); + + return `${($$result.head += `${($$result.title = `
${escape(error.message)}
+ +${``}`; +}); + +// This file is generated by Sapper — do not edit it! + +const d = decodeURIComponent; + +const manifest = { + server_routes: [ + { + // blog/index.json.js + pattern: /^\/blog\.json$/, + handlers: route_0, + params: () => ({}) + }, + + { + // blog/[slug].json.js + pattern: /^\/blog\/([^/]+?)\.json$/, + handlers: route_1, + params: match => ({ slug: d(match[1]) }) + } + ], + + pages: [ + { + // index.svelte + pattern: /^\/$/, + parts: [ + { name: "index", file: "index.svelte", component: component_0 } + ] + }, + + { + // about.svelte + pattern: /^\/about\/?$/, + parts: [ + { name: "about", file: "about.svelte", component: component_1 } + ] + }, + + { + // blog/index.svelte + pattern: /^\/blog\/?$/, + parts: [ + { name: "blog", file: "blog/index.svelte", component: component_2 } + ] + }, + + { + // blog/[slug].svelte + pattern: /^\/blog\/([^/]+?)\/?$/, + parts: [ + null, + { name: "blog_$slug", file: "blog/[slug].svelte", component: component_3, params: match => ({ slug: d(match[1]) }) } + ] + } + ], + + root_comp, + error: Error$1 +}; + +const build_dir = "__sapper__/build"; + +const subscriber_queue = []; +/** + * Create a `Writable` store that allows both updating and reading by subscription. + * @param {*=}value initial value + * @param {StartStopNotifier=}start start and stop notifications for subscriptions + */ +function writable(value, start = noop$1) { + let stop; + const subscribers = []; + function set(new_value) { + if (safe_not_equal(value, new_value)) { + value = new_value; + if (stop) { // store is ready + const run_queue = !subscriber_queue.length; + for (let i = 0; i < subscribers.length; i += 1) { + const s = subscribers[i]; + s[1](); + subscriber_queue.push(s, value); + } + if (run_queue) { + for (let i = 0; i < subscriber_queue.length; i += 2) { + subscriber_queue[i][0](subscriber_queue[i + 1]); + } + subscriber_queue.length = 0; + } + } + } + } + function update(fn) { + set(fn(value)); + } + function subscribe(run, invalidate = noop$1) { + const subscriber = [run, invalidate]; + subscribers.push(subscriber); + if (subscribers.length === 1) { + stop = start(set) || noop$1; + } + run(value); + return () => { + const index = subscribers.indexOf(subscriber); + if (index !== -1) { + subscribers.splice(index, 1); + } + if (subscribers.length === 0) { + stop(); + stop = null; + } + }; + } + return { set, update, subscribe }; +} + +const CONTEXT_KEY = {}; + +/* src/node_modules/@sapper/internal/App.svelte generated by Svelte v3.37.0 */ + +const App = create_ssr_component(($$result, $$props, $$bindings, slots) => { + let { stores } = $$props; + let { error } = $$props; + let { status } = $$props; + let { segments } = $$props; + let { level0 } = $$props; + let { level1 = null } = $$props; + let { notify } = $$props; + afterUpdate(notify); + setContext(CONTEXT_KEY, stores); + if ($$props.stores === void 0 && $$bindings.stores && stores !== void 0) $$bindings.stores(stores); + if ($$props.error === void 0 && $$bindings.error && error !== void 0) $$bindings.error(error); + if ($$props.status === void 0 && $$bindings.status && status !== void 0) $$bindings.status(status); + if ($$props.segments === void 0 && $$bindings.segments && segments !== void 0) $$bindings.segments(segments); + if ($$props.level0 === void 0 && $$bindings.level0 && level0 !== void 0) $$bindings.level0(level0); + if ($$props.level1 === void 0 && $$bindings.level1 && level1 !== void 0) $$bindings.level1(level1); + if ($$props.notify === void 0 && $$bindings.notify && notify !== void 0) $$bindings.notify(notify); + + return ` + + +${validate_component(Layout, "Layout").$$render($$result, Object.assign({ segment: segments[0] }, level0.props), {}, { + default: () => `${error + ? `${validate_component(Error$1, "Error").$$render($$result, { error, status }, {}, {})}` + : `${validate_component(level1.component || missing_component, "svelte:component").$$render($$result, Object.assign(level1.props), {}, {})}`}` + })}`; +}); + +/** + * @param typeMap [Object] Map of MIME type -> Array[extensions] + * @param ... + */ +function Mime() { + this._types = Object.create(null); + this._extensions = Object.create(null); + + for (var i = 0; i < arguments.length; i++) { + this.define(arguments[i]); + } + + this.define = this.define.bind(this); + this.getType = this.getType.bind(this); + this.getExtension = this.getExtension.bind(this); +} + +/** + * Define mimetype -> extension mappings. Each key is a mime-type that maps + * to an array of extensions associated with the type. The first extension is + * used as the default extension for the type. + * + * e.g. mime.define({'audio/ogg', ['oga', 'ogg', 'spx']}); + * + * If a type declares an extension that has already been defined, an error will + * be thrown. To suppress this error and force the extension to be associated + * with the new type, pass `force`=true. Alternatively, you may prefix the + * extension with "*" to map the type to extension, without mapping the + * extension to the type. + * + * e.g. mime.define({'audio/wav', ['wav']}, {'audio/x-wav', ['*wav']}); + * + * + * @param map (Object) type definitions + * @param force (Boolean) if true, force overriding of existing definitions + */ +Mime.prototype.define = function(typeMap, force) { + for (var type in typeMap) { + var extensions = typeMap[type].map(function(t) {return t.toLowerCase()}); + type = type.toLowerCase(); + + for (var i = 0; i < extensions.length; i++) { + var ext = extensions[i]; + + // '*' prefix = not the preferred type for this extension. So fixup the + // extension, and skip it. + if (ext[0] == '*') { + continue; + } + + if (!force && (ext in this._types)) { + throw new Error( + 'Attempt to change mapping for "' + ext + + '" extension from "' + this._types[ext] + '" to "' + type + + '". Pass `force=true` to allow this, otherwise remove "' + ext + + '" from the list of extensions for "' + type + '".' + ); + } + + this._types[ext] = type; + } + + // Use first extension as default + if (force || !this._extensions[type]) { + var ext = extensions[0]; + this._extensions[type] = (ext[0] != '*') ? ext : ext.substr(1); + } + } +}; + +/** + * Lookup a mime type based on extension + */ +Mime.prototype.getType = function(path) { + path = String(path); + var last = path.replace(/^.*[/\\]/, '').toLowerCase(); + var ext = last.replace(/^.*\./, '').toLowerCase(); + + var hasPath = last.length < path.length; + var hasDot = ext.length < last.length - 1; + + return (hasDot || !hasPath) && this._types[ext] || null; +}; + +/** + * Return file extension associated with a mime type + */ +Mime.prototype.getExtension = function(type) { + type = /^\s*([^;\s]*)/.test(type) && RegExp.$1; + return type && this._extensions[type.toLowerCase()] || null; +}; + +var Mime_1 = Mime; + +var standard = {"application/andrew-inset":["ez"],"application/applixware":["aw"],"application/atom+xml":["atom"],"application/atomcat+xml":["atomcat"],"application/atomdeleted+xml":["atomdeleted"],"application/atomsvc+xml":["atomsvc"],"application/atsc-dwd+xml":["dwd"],"application/atsc-held+xml":["held"],"application/atsc-rsat+xml":["rsat"],"application/bdoc":["bdoc"],"application/calendar+xml":["xcs"],"application/ccxml+xml":["ccxml"],"application/cdfx+xml":["cdfx"],"application/cdmi-capability":["cdmia"],"application/cdmi-container":["cdmic"],"application/cdmi-domain":["cdmid"],"application/cdmi-object":["cdmio"],"application/cdmi-queue":["cdmiq"],"application/cu-seeme":["cu"],"application/dash+xml":["mpd"],"application/davmount+xml":["davmount"],"application/docbook+xml":["dbk"],"application/dssc+der":["dssc"],"application/dssc+xml":["xdssc"],"application/ecmascript":["ecma","es"],"application/emma+xml":["emma"],"application/emotionml+xml":["emotionml"],"application/epub+zip":["epub"],"application/exi":["exi"],"application/fdt+xml":["fdt"],"application/font-tdpfr":["pfr"],"application/geo+json":["geojson"],"application/gml+xml":["gml"],"application/gpx+xml":["gpx"],"application/gxf":["gxf"],"application/gzip":["gz"],"application/hjson":["hjson"],"application/hyperstudio":["stk"],"application/inkml+xml":["ink","inkml"],"application/ipfix":["ipfix"],"application/its+xml":["its"],"application/java-archive":["jar","war","ear"],"application/java-serialized-object":["ser"],"application/java-vm":["class"],"application/javascript":["js","mjs"],"application/json":["json","map"],"application/json5":["json5"],"application/jsonml+json":["jsonml"],"application/ld+json":["jsonld"],"application/lgr+xml":["lgr"],"application/lost+xml":["lostxml"],"application/mac-binhex40":["hqx"],"application/mac-compactpro":["cpt"],"application/mads+xml":["mads"],"application/manifest+json":["webmanifest"],"application/marc":["mrc"],"application/marcxml+xml":["mrcx"],"application/mathematica":["ma","nb","mb"],"application/mathml+xml":["mathml"],"application/mbox":["mbox"],"application/mediaservercontrol+xml":["mscml"],"application/metalink+xml":["metalink"],"application/metalink4+xml":["meta4"],"application/mets+xml":["mets"],"application/mmt-aei+xml":["maei"],"application/mmt-usd+xml":["musd"],"application/mods+xml":["mods"],"application/mp21":["m21","mp21"],"application/mp4":["mp4s","m4p"],"application/mrb-consumer+xml":["*xdf"],"application/mrb-publish+xml":["*xdf"],"application/msword":["doc","dot"],"application/mxf":["mxf"],"application/n-quads":["nq"],"application/n-triples":["nt"],"application/node":["cjs"],"application/octet-stream":["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","exe","dll","deb","dmg","iso","img","msi","msp","msm","buffer"],"application/oda":["oda"],"application/oebps-package+xml":["opf"],"application/ogg":["ogx"],"application/omdoc+xml":["omdoc"],"application/onenote":["onetoc","onetoc2","onetmp","onepkg"],"application/oxps":["oxps"],"application/p2p-overlay+xml":["relo"],"application/patch-ops-error+xml":["*xer"],"application/pdf":["pdf"],"application/pgp-encrypted":["pgp"],"application/pgp-signature":["asc","sig"],"application/pics-rules":["prf"],"application/pkcs10":["p10"],"application/pkcs7-mime":["p7m","p7c"],"application/pkcs7-signature":["p7s"],"application/pkcs8":["p8"],"application/pkix-attr-cert":["ac"],"application/pkix-cert":["cer"],"application/pkix-crl":["crl"],"application/pkix-pkipath":["pkipath"],"application/pkixcmp":["pki"],"application/pls+xml":["pls"],"application/postscript":["ai","eps","ps"],"application/provenance+xml":["provx"],"application/pskc+xml":["pskcxml"],"application/raml+yaml":["raml"],"application/rdf+xml":["rdf","owl"],"application/reginfo+xml":["rif"],"application/relax-ng-compact-syntax":["rnc"],"application/resource-lists+xml":["rl"],"application/resource-lists-diff+xml":["rld"],"application/rls-services+xml":["rs"],"application/route-apd+xml":["rapd"],"application/route-s-tsid+xml":["sls"],"application/route-usd+xml":["rusd"],"application/rpki-ghostbusters":["gbr"],"application/rpki-manifest":["mft"],"application/rpki-roa":["roa"],"application/rsd+xml":["rsd"],"application/rss+xml":["rss"],"application/rtf":["rtf"],"application/sbml+xml":["sbml"],"application/scvp-cv-request":["scq"],"application/scvp-cv-response":["scs"],"application/scvp-vp-request":["spq"],"application/scvp-vp-response":["spp"],"application/sdp":["sdp"],"application/senml+xml":["senmlx"],"application/sensml+xml":["sensmlx"],"application/set-payment-initiation":["setpay"],"application/set-registration-initiation":["setreg"],"application/shf+xml":["shf"],"application/sieve":["siv","sieve"],"application/smil+xml":["smi","smil"],"application/sparql-query":["rq"],"application/sparql-results+xml":["srx"],"application/srgs":["gram"],"application/srgs+xml":["grxml"],"application/sru+xml":["sru"],"application/ssdl+xml":["ssdl"],"application/ssml+xml":["ssml"],"application/swid+xml":["swidtag"],"application/tei+xml":["tei","teicorpus"],"application/thraud+xml":["tfi"],"application/timestamped-data":["tsd"],"application/toml":["toml"],"application/ttml+xml":["ttml"],"application/urc-ressheet+xml":["rsheet"],"application/voicexml+xml":["vxml"],"application/wasm":["wasm"],"application/widget":["wgt"],"application/winhlp":["hlp"],"application/wsdl+xml":["wsdl"],"application/wspolicy+xml":["wspolicy"],"application/xaml+xml":["xaml"],"application/xcap-att+xml":["xav"],"application/xcap-caps+xml":["xca"],"application/xcap-diff+xml":["xdf"],"application/xcap-el+xml":["xel"],"application/xcap-error+xml":["xer"],"application/xcap-ns+xml":["xns"],"application/xenc+xml":["xenc"],"application/xhtml+xml":["xhtml","xht"],"application/xliff+xml":["xlf"],"application/xml":["xml","xsl","xsd","rng"],"application/xml-dtd":["dtd"],"application/xop+xml":["xop"],"application/xproc+xml":["xpl"],"application/xslt+xml":["xslt"],"application/xspf+xml":["xspf"],"application/xv+xml":["mxml","xhvml","xvml","xvm"],"application/yang":["yang"],"application/yin+xml":["yin"],"application/zip":["zip"],"audio/3gpp":["*3gpp"],"audio/adpcm":["adp"],"audio/basic":["au","snd"],"audio/midi":["mid","midi","kar","rmi"],"audio/mobile-xmf":["mxmf"],"audio/mp3":["*mp3"],"audio/mp4":["m4a","mp4a"],"audio/mpeg":["mpga","mp2","mp2a","mp3","m2a","m3a"],"audio/ogg":["oga","ogg","spx"],"audio/s3m":["s3m"],"audio/silk":["sil"],"audio/wav":["wav"],"audio/wave":["*wav"],"audio/webm":["weba"],"audio/xm":["xm"],"font/collection":["ttc"],"font/otf":["otf"],"font/ttf":["ttf"],"font/woff":["woff"],"font/woff2":["woff2"],"image/aces":["exr"],"image/apng":["apng"],"image/bmp":["bmp"],"image/cgm":["cgm"],"image/dicom-rle":["drle"],"image/emf":["emf"],"image/fits":["fits"],"image/g3fax":["g3"],"image/gif":["gif"],"image/heic":["heic"],"image/heic-sequence":["heics"],"image/heif":["heif"],"image/heif-sequence":["heifs"],"image/hej2k":["hej2"],"image/hsj2":["hsj2"],"image/ief":["ief"],"image/jls":["jls"],"image/jp2":["jp2","jpg2"],"image/jpeg":["jpeg","jpg","jpe"],"image/jph":["jph"],"image/jphc":["jhc"],"image/jpm":["jpm"],"image/jpx":["jpx","jpf"],"image/jxr":["jxr"],"image/jxra":["jxra"],"image/jxrs":["jxrs"],"image/jxs":["jxs"],"image/jxsc":["jxsc"],"image/jxsi":["jxsi"],"image/jxss":["jxss"],"image/ktx":["ktx"],"image/png":["png"],"image/sgi":["sgi"],"image/svg+xml":["svg","svgz"],"image/t38":["t38"],"image/tiff":["tif","tiff"],"image/tiff-fx":["tfx"],"image/webp":["webp"],"image/wmf":["wmf"],"message/disposition-notification":["disposition-notification"],"message/global":["u8msg"],"message/global-delivery-status":["u8dsn"],"message/global-disposition-notification":["u8mdn"],"message/global-headers":["u8hdr"],"message/rfc822":["eml","mime"],"model/3mf":["3mf"],"model/gltf+json":["gltf"],"model/gltf-binary":["glb"],"model/iges":["igs","iges"],"model/mesh":["msh","mesh","silo"],"model/mtl":["mtl"],"model/obj":["obj"],"model/stl":["stl"],"model/vrml":["wrl","vrml"],"model/x3d+binary":["*x3db","x3dbz"],"model/x3d+fastinfoset":["x3db"],"model/x3d+vrml":["*x3dv","x3dvz"],"model/x3d+xml":["x3d","x3dz"],"model/x3d-vrml":["x3dv"],"text/cache-manifest":["appcache","manifest"],"text/calendar":["ics","ifb"],"text/coffeescript":["coffee","litcoffee"],"text/css":["css"],"text/csv":["csv"],"text/html":["html","htm","shtml"],"text/jade":["jade"],"text/jsx":["jsx"],"text/less":["less"],"text/markdown":["markdown","md"],"text/mathml":["mml"],"text/mdx":["mdx"],"text/n3":["n3"],"text/plain":["txt","text","conf","def","list","log","in","ini"],"text/richtext":["rtx"],"text/rtf":["*rtf"],"text/sgml":["sgml","sgm"],"text/shex":["shex"],"text/slim":["slim","slm"],"text/stylus":["stylus","styl"],"text/tab-separated-values":["tsv"],"text/troff":["t","tr","roff","man","me","ms"],"text/turtle":["ttl"],"text/uri-list":["uri","uris","urls"],"text/vcard":["vcard"],"text/vtt":["vtt"],"text/xml":["*xml"],"text/yaml":["yaml","yml"],"video/3gpp":["3gp","3gpp"],"video/3gpp2":["3g2"],"video/h261":["h261"],"video/h263":["h263"],"video/h264":["h264"],"video/jpeg":["jpgv"],"video/jpm":["*jpm","jpgm"],"video/mj2":["mj2","mjp2"],"video/mp2t":["ts"],"video/mp4":["mp4","mp4v","mpg4"],"video/mpeg":["mpeg","mpg","mpe","m1v","m2v"],"video/ogg":["ogv"],"video/quicktime":["qt","mov"],"video/webm":["webm"]}; + +var lite = new Mime_1(standard); + +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ + +function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +} + +function get_server_route_handler(routes) { + function handle_route(route, req, res, next) { + return __awaiter(this, void 0, void 0, function* () { + req.params = route.params(route.pattern.exec(req.path)); + const method = req.method.toLowerCase(); + // 'delete' cannot be exported from a module because it is a keyword, + // so check for 'del' instead + const method_export = method === 'delete' ? 'del' : method; + const handle_method = route.handlers[method_export]; + if (handle_method) { + if (process.env.SAPPER_EXPORT) { + const { write, end, setHeader } = res; + const chunks = []; + const headers = {}; + // intercept data so that it can be exported + res.write = function (chunk) { + chunks.push(Buffer.from(chunk)); + return write.apply(res, [chunk]); + }; + res.setHeader = function (name, value) { + headers[name.toLowerCase()] = value; + setHeader.apply(res, [name, value]); + }; + res.end = function (chunk) { + if (chunk) + chunks.push(Buffer.from(chunk)); + end.apply(res, [chunk]); + process.send({ + __sapper__: true, + event: 'file', + url: req.url, + method: req.method, + status: res.statusCode, + type: headers['content-type'], + body: Buffer.concat(chunks) + }); + }; + } + const handle_next = (err) => { + if (err) { + res.statusCode = 500; + res.end(err.message); + } + else { + process.nextTick(next); + } + }; + try { + yield handle_method(req, res, handle_next); + } + catch (err) { + console.error(err); + handle_next(err); + } + } + else { + // no matching handler for method + process.nextTick(next); + } + }); + } + return function find_route(req, res, next) { + for (const route of routes) { + if (route.pattern.test(req.path)) { + handle_route(route, req, res, next); + return; + } + } + next(); + }; +} + +/*! + * cookie + * Copyright(c) 2012-2014 Roman Shtylman + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + +/** + * Module exports. + * @public + */ + +var parse_1 = parse; + +/** + * Module variables. + * @private + */ + +var decode = decodeURIComponent; +var pairSplitRegExp = /; */; + +/** + * Parse a cookie header. + * + * Parse the given cookie header string into an object + * The object has the various cookies as keys(names) => values + * + * @param {string} str + * @param {object} [options] + * @return {object} + * @public + */ + +function parse(str, options) { + if (typeof str !== 'string') { + throw new TypeError('argument str must be a string'); + } + + var obj = {}; + var opt = options || {}; + var pairs = str.split(pairSplitRegExp); + var dec = opt.decode || decode; + + for (var i = 0; i < pairs.length; i++) { + var pair = pairs[i]; + var eq_idx = pair.indexOf('='); + + // skip things that don't look like key=value + if (eq_idx < 0) { + continue; + } + + var key = pair.substr(0, eq_idx).trim(); + var val = pair.substr(++eq_idx, pair.length).trim(); + + // quoted values + if ('"' == val[0]) { + val = val.slice(1, -1); + } + + // only assign once + if (undefined == obj[key]) { + obj[key] = tryDecode(val, dec); + } + } + + return obj; +} + +/** + * Try decoding a string using a decoding function. + * + * @param {string} str + * @param {function} decode + * @private + */ + +function tryDecode(str, decode) { + try { + return decode(str); + } catch (e) { + return str; + } +} + +var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$'; +var unsafeChars = /[<>\b\f\n\r\t\0\u2028\u2029]/g; +var reserved = /^(?:do|if|in|for|int|let|new|try|var|byte|case|char|else|enum|goto|long|this|void|with|await|break|catch|class|const|final|float|short|super|throw|while|yield|delete|double|export|import|native|return|switch|throws|typeof|boolean|default|extends|finally|package|private|abstract|continue|debugger|function|volatile|interface|protected|transient|implements|instanceof|synchronized)$/; +var escaped = { + '<': '\\u003C', + '>': '\\u003E', + '/': '\\u002F', + '\\': '\\\\', + '\b': '\\b', + '\f': '\\f', + '\n': '\\n', + '\r': '\\r', + '\t': '\\t', + '\0': '\\0', + '\u2028': '\\u2028', + '\u2029': '\\u2029' +}; +var objectProtoOwnPropertyNames = Object.getOwnPropertyNames(Object.prototype).sort().join('\0'); +function devalue(value) { + var counts = new Map(); + function walk(thing) { + if (typeof thing === 'function') { + throw new Error("Cannot stringify a function"); + } + if (counts.has(thing)) { + counts.set(thing, counts.get(thing) + 1); + return; + } + counts.set(thing, 1); + if (!isPrimitive(thing)) { + var type = getType(thing); + switch (type) { + case 'Number': + case 'String': + case 'Boolean': + case 'Date': + case 'RegExp': + return; + case 'Array': + thing.forEach(walk); + break; + case 'Set': + case 'Map': + Array.from(thing).forEach(walk); + break; + default: + var proto = Object.getPrototypeOf(thing); + if (proto !== Object.prototype && + proto !== null && + Object.getOwnPropertyNames(proto).sort().join('\0') !== objectProtoOwnPropertyNames) { + throw new Error("Cannot stringify arbitrary non-POJOs"); + } + if (Object.getOwnPropertySymbols(thing).length > 0) { + throw new Error("Cannot stringify POJOs with symbolic keys"); + } + Object.keys(thing).forEach(function (key) { return walk(thing[key]); }); + } + } + } + walk(value); + var names = new Map(); + Array.from(counts) + .filter(function (entry) { return entry[1] > 1; }) + .sort(function (a, b) { return b[1] - a[1]; }) + .forEach(function (entry, i) { + names.set(entry[0], getName(i)); + }); + function stringify(thing) { + if (names.has(thing)) { + return names.get(thing); + } + if (isPrimitive(thing)) { + return stringifyPrimitive(thing); + } + var type = getType(thing); + switch (type) { + case 'Number': + case 'String': + case 'Boolean': + return "Object(" + stringify(thing.valueOf()) + ")"; + case 'RegExp': + return "new RegExp(" + stringifyString(thing.source) + ", \"" + thing.flags + "\")"; + case 'Date': + return "new Date(" + thing.getTime() + ")"; + case 'Array': + var members = thing.map(function (v, i) { return i in thing ? stringify(v) : ''; }); + var tail = thing.length === 0 || (thing.length - 1 in thing) ? '' : ','; + return "[" + members.join(',') + tail + "]"; + case 'Set': + case 'Map': + return "new " + type + "([" + Array.from(thing).map(stringify).join(',') + "])"; + default: + var obj = "{" + Object.keys(thing).map(function (key) { return safeKey(key) + ":" + stringify(thing[key]); }).join(',') + "}"; + var proto = Object.getPrototypeOf(thing); + if (proto === null) { + return Object.keys(thing).length > 0 + ? "Object.assign(Object.create(null)," + obj + ")" + : "Object.create(null)"; + } + return obj; + } + } + var str = stringify(value); + if (names.size) { + var params_1 = []; + var statements_1 = []; + var values_1 = []; + names.forEach(function (name, thing) { + params_1.push(name); + if (isPrimitive(thing)) { + values_1.push(stringifyPrimitive(thing)); + return; + } + var type = getType(thing); + switch (type) { + case 'Number': + case 'String': + case 'Boolean': + values_1.push("Object(" + stringify(thing.valueOf()) + ")"); + break; + case 'RegExp': + values_1.push(thing.toString()); + break; + case 'Date': + values_1.push("new Date(" + thing.getTime() + ")"); + break; + case 'Array': + values_1.push("Array(" + thing.length + ")"); + thing.forEach(function (v, i) { + statements_1.push(name + "[" + i + "]=" + stringify(v)); + }); + break; + case 'Set': + values_1.push("new Set"); + statements_1.push(name + "." + Array.from(thing).map(function (v) { return "add(" + stringify(v) + ")"; }).join('.')); + break; + case 'Map': + values_1.push("new Map"); + statements_1.push(name + "." + Array.from(thing).map(function (_a) { + var k = _a[0], v = _a[1]; + return "set(" + stringify(k) + ", " + stringify(v) + ")"; + }).join('.')); + break; + default: + values_1.push(Object.getPrototypeOf(thing) === null ? 'Object.create(null)' : '{}'); + Object.keys(thing).forEach(function (key) { + statements_1.push("" + name + safeProp(key) + "=" + stringify(thing[key])); + }); + } + }); + statements_1.push("return " + str); + return "(function(" + params_1.join(',') + "){" + statements_1.join(';') + "}(" + values_1.join(',') + "))"; + } + else { + return str; + } +} +function getName(num) { + var name = ''; + do { + name = chars[num % chars.length] + name; + num = ~~(num / chars.length) - 1; + } while (num >= 0); + return reserved.test(name) ? name + "_" : name; +} +function isPrimitive(thing) { + return Object(thing) !== thing; +} +function stringifyPrimitive(thing) { + if (typeof thing === 'string') + return stringifyString(thing); + if (thing === void 0) + return 'void 0'; + if (thing === 0 && 1 / thing < 0) + return '-0'; + var str = String(thing); + if (typeof thing === 'number') + return str.replace(/^(-)?0\./, '$1.'); + return str; +} +function getType(thing) { + return Object.prototype.toString.call(thing).slice(8, -1); +} +function escapeUnsafeChar(c) { + return escaped[c] || c; +} +function escapeUnsafeChars(str) { + return str.replace(unsafeChars, escapeUnsafeChar); +} +function safeKey(key) { + return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key) ? key : escapeUnsafeChars(JSON.stringify(key)); +} +function safeProp(key) { + return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key) ? "." + key : "[" + escapeUnsafeChars(JSON.stringify(key)) + "]"; +} +function stringifyString(str) { + var result = '"'; + for (var i = 0; i < str.length; i += 1) { + var char = str.charAt(i); + var code = char.charCodeAt(0); + if (char === '"') { + result += '\\"'; + } + else if (char in escaped) { + result += escaped[char]; + } + else if (code >= 0xd800 && code <= 0xdfff) { + var next = str.charCodeAt(i + 1); + // If this is the beginning of a [high, low] surrogate pair, + // add the next two characters, otherwise escape + if (code <= 0xdbff && (next >= 0xdc00 && next <= 0xdfff)) { + result += char + str[++i]; + } + else { + result += "\\u" + code.toString(16).toUpperCase(); + } + } + else { + result += char; + } + } + result += '"'; + return result; +} + +// Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js + +// fix for "Readable" isn't a named export issue +const Readable = Stream__default['default'].Readable; + +const BUFFER = Symbol('buffer'); +const TYPE = Symbol('type'); + +class Blob { + constructor() { + this[TYPE] = ''; + + const blobParts = arguments[0]; + const options = arguments[1]; + + const buffers = []; + let size = 0; + + if (blobParts) { + const a = blobParts; + const length = Number(a.length); + for (let i = 0; i < length; i++) { + const element = a[i]; + let buffer; + if (element instanceof Buffer) { + buffer = element; + } else if (ArrayBuffer.isView(element)) { + buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength); + } else if (element instanceof ArrayBuffer) { + buffer = Buffer.from(element); + } else if (element instanceof Blob) { + buffer = element[BUFFER]; + } else { + buffer = Buffer.from(typeof element === 'string' ? element : String(element)); + } + size += buffer.length; + buffers.push(buffer); + } + } + + this[BUFFER] = Buffer.concat(buffers); + + let type = options && options.type !== undefined && String(options.type).toLowerCase(); + if (type && !/[^\u0020-\u007E]/.test(type)) { + this[TYPE] = type; + } + } + get size() { + return this[BUFFER].length; + } + get type() { + return this[TYPE]; + } + text() { + return Promise.resolve(this[BUFFER].toString()); + } + arrayBuffer() { + const buf = this[BUFFER]; + const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); + return Promise.resolve(ab); + } + stream() { + const readable = new Readable(); + readable._read = function () {}; + readable.push(this[BUFFER]); + readable.push(null); + return readable; + } + toString() { + return '[object Blob]'; + } + slice() { + const size = this.size; + + const start = arguments[0]; + const end = arguments[1]; + let relativeStart, relativeEnd; + if (start === undefined) { + relativeStart = 0; + } else if (start < 0) { + relativeStart = Math.max(size + start, 0); + } else { + relativeStart = Math.min(start, size); + } + if (end === undefined) { + relativeEnd = size; + } else if (end < 0) { + relativeEnd = Math.max(size + end, 0); + } else { + relativeEnd = Math.min(end, size); + } + const span = Math.max(relativeEnd - relativeStart, 0); + + const buffer = this[BUFFER]; + const slicedBuffer = buffer.slice(relativeStart, relativeStart + span); + const blob = new Blob([], { type: arguments[2] }); + blob[BUFFER] = slicedBuffer; + return blob; + } +} + +Object.defineProperties(Blob.prototype, { + size: { enumerable: true }, + type: { enumerable: true }, + slice: { enumerable: true } +}); + +Object.defineProperty(Blob.prototype, Symbol.toStringTag, { + value: 'Blob', + writable: false, + enumerable: false, + configurable: true +}); + +/** + * fetch-error.js + * + * FetchError interface for operational errors + */ + +/** + * Create FetchError instance + * + * @param String message Error message for human + * @param String type Error type for machine + * @param String systemError For Node.js system error + * @return FetchError + */ +function FetchError(message, type, systemError) { + Error.call(this, message); + + this.message = message; + this.type = type; + + // when err.type is `system`, err.code contains system error code + if (systemError) { + this.code = this.errno = systemError.code; + } + + // hide custom error implementation details from end-users + Error.captureStackTrace(this, this.constructor); +} + +FetchError.prototype = Object.create(Error.prototype); +FetchError.prototype.constructor = FetchError; +FetchError.prototype.name = 'FetchError'; + +let convert; +try { + convert = require('encoding').convert; +} catch (e) {} + +const INTERNALS = Symbol('Body internals'); + +// fix an issue where "PassThrough" isn't a named export for node <10 +const PassThrough = Stream__default['default'].PassThrough; + +/** + * Body mixin + * + * Ref: https://fetch.spec.whatwg.org/#body + * + * @param Stream body Readable stream + * @param Object opts Response options + * @return Void + */ +function Body(body) { + var _this = this; + + var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + _ref$size = _ref.size; + + let size = _ref$size === undefined ? 0 : _ref$size; + var _ref$timeout = _ref.timeout; + let timeout = _ref$timeout === undefined ? 0 : _ref$timeout; + + if (body == null) { + // body is undefined or null + body = null; + } else if (isURLSearchParams(body)) { + // body is a URLSearchParams + body = Buffer.from(body.toString()); + } else if (isBlob(body)) ; else if (Buffer.isBuffer(body)) ; else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') { + // body is ArrayBuffer + body = Buffer.from(body); + } else if (ArrayBuffer.isView(body)) { + // body is ArrayBufferView + body = Buffer.from(body.buffer, body.byteOffset, body.byteLength); + } else if (body instanceof Stream__default['default']) ; else { + // none of the above + // coerce to string then buffer + body = Buffer.from(String(body)); + } + this[INTERNALS] = { + body, + disturbed: false, + error: null + }; + this.size = size; + this.timeout = timeout; + + if (body instanceof Stream__default['default']) { + body.on('error', function (err) { + const error = err.name === 'AbortError' ? err : new FetchError(`Invalid response body while trying to fetch ${_this.url}: ${err.message}`, 'system', err); + _this[INTERNALS].error = error; + }); + } +} + +Body.prototype = { + get body() { + return this[INTERNALS].body; + }, + + get bodyUsed() { + return this[INTERNALS].disturbed; + }, + + /** + * Decode response as ArrayBuffer + * + * @return Promise + */ + arrayBuffer() { + return consumeBody.call(this).then(function (buf) { + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); + }); + }, + + /** + * Return raw response as Blob + * + * @return Promise + */ + blob() { + let ct = this.headers && this.headers.get('content-type') || ''; + return consumeBody.call(this).then(function (buf) { + return Object.assign( + // Prevent copying + new Blob([], { + type: ct.toLowerCase() + }), { + [BUFFER]: buf + }); + }); + }, + + /** + * Decode response as json + * + * @return Promise + */ + json() { + var _this2 = this; + + return consumeBody.call(this).then(function (buffer) { + try { + return JSON.parse(buffer.toString()); + } catch (err) { + return Body.Promise.reject(new FetchError(`invalid json response body at ${_this2.url} reason: ${err.message}`, 'invalid-json')); + } + }); + }, + + /** + * Decode response as text + * + * @return Promise + */ + text() { + return consumeBody.call(this).then(function (buffer) { + return buffer.toString(); + }); + }, + + /** + * Decode response as buffer (non-spec api) + * + * @return Promise + */ + buffer() { + return consumeBody.call(this); + }, + + /** + * Decode response as text, while automatically detecting the encoding and + * trying to decode to UTF-8 (non-spec api) + * + * @return Promise + */ + textConverted() { + var _this3 = this; + + return consumeBody.call(this).then(function (buffer) { + return convertBody(buffer, _this3.headers); + }); + } +}; + +// In browsers, all properties are enumerable. +Object.defineProperties(Body.prototype, { + body: { enumerable: true }, + bodyUsed: { enumerable: true }, + arrayBuffer: { enumerable: true }, + blob: { enumerable: true }, + json: { enumerable: true }, + text: { enumerable: true } +}); + +Body.mixIn = function (proto) { + for (const name of Object.getOwnPropertyNames(Body.prototype)) { + // istanbul ignore else: future proof + if (!(name in proto)) { + const desc = Object.getOwnPropertyDescriptor(Body.prototype, name); + Object.defineProperty(proto, name, desc); + } + } +}; + +/** + * Consume and convert an entire Body to a Buffer. + * + * Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body + * + * @return Promise + */ +function consumeBody() { + var _this4 = this; + + if (this[INTERNALS].disturbed) { + return Body.Promise.reject(new TypeError(`body used already for: ${this.url}`)); + } + + this[INTERNALS].disturbed = true; + + if (this[INTERNALS].error) { + return Body.Promise.reject(this[INTERNALS].error); + } + + let body = this.body; + + // body is null + if (body === null) { + return Body.Promise.resolve(Buffer.alloc(0)); + } + + // body is blob + if (isBlob(body)) { + body = body.stream(); + } + + // body is buffer + if (Buffer.isBuffer(body)) { + return Body.Promise.resolve(body); + } + + // istanbul ignore if: should never happen + if (!(body instanceof Stream__default['default'])) { + return Body.Promise.resolve(Buffer.alloc(0)); + } + + // body is stream + // get ready to actually consume the body + let accum = []; + let accumBytes = 0; + let abort = false; + + return new Body.Promise(function (resolve, reject) { + let resTimeout; + + // allow timeout on slow response body + if (_this4.timeout) { + resTimeout = setTimeout(function () { + abort = true; + reject(new FetchError(`Response timeout while trying to fetch ${_this4.url} (over ${_this4.timeout}ms)`, 'body-timeout')); + }, _this4.timeout); + } + + // handle stream errors + body.on('error', function (err) { + if (err.name === 'AbortError') { + // if the request was aborted, reject with this Error + abort = true; + reject(err); + } else { + // other errors, such as incorrect content-encoding + reject(new FetchError(`Invalid response body while trying to fetch ${_this4.url}: ${err.message}`, 'system', err)); + } + }); + + body.on('data', function (chunk) { + if (abort || chunk === null) { + return; + } + + if (_this4.size && accumBytes + chunk.length > _this4.size) { + abort = true; + reject(new FetchError(`content size at ${_this4.url} over limit: ${_this4.size}`, 'max-size')); + return; + } + + accumBytes += chunk.length; + accum.push(chunk); + }); + + body.on('end', function () { + if (abort) { + return; + } + + clearTimeout(resTimeout); + + try { + resolve(Buffer.concat(accum, accumBytes)); + } catch (err) { + // handle streams that have accumulated too much data (issue #414) + reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, 'system', err)); + } + }); + }); +} + +/** + * Detect buffer encoding and convert to target encoding + * ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding + * + * @param Buffer buffer Incoming buffer + * @param String encoding Target encoding + * @return String + */ +function convertBody(buffer, headers) { + if (typeof convert !== 'function') { + throw new Error('The package `encoding` must be installed to use the textConverted() function'); + } + + const ct = headers.get('content-type'); + let charset = 'utf-8'; + let res, str; + + // header + if (ct) { + res = /charset=([^;]*)/i.exec(ct); + } + + // no charset in content type, peek at response body for at most 1024 bytes + str = buffer.slice(0, 1024).toString(); + + // html5 + if (!res && str) { + res = /${message}`); + } + function handle_error(req, res, statusCode, error) { + handle_page({ + pattern: null, + parts: [ + { name: null, component: { default: error_route } } + ] + }, req, res, statusCode, error || 'Unknown error'); + } + function handle_page(page, req, res, status = 200, error = null) { + var _a, _b; + return __awaiter(this, void 0, void 0, function* () { + const is_service_worker_index = req.path === '/service-worker-index.html'; + const build_info = get_build_info(); + res.setHeader('Content-Type', 'text/html'); + // preload main js and css + // TODO detect other stuff we can preload like fonts? + let preload_files = Array.isArray(build_info.assets.main) ? build_info.assets.main : [build_info.assets.main]; + if ((_a = build_info === null || build_info === void 0 ? void 0 : build_info.css) === null || _a === void 0 ? void 0 : _a.main) { + preload_files = preload_files.concat((_b = build_info === null || build_info === void 0 ? void 0 : build_info.css) === null || _b === void 0 ? void 0 : _b.main); + } + let es6_preload = false; + if (build_info.bundler === 'rollup') { + es6_preload = true; + const route = page.parts[page.parts.length - 1].file; + const deps = build_info.dependencies[route]; + if (deps) { + preload_files = preload_files.concat(deps); + } + } + else if (!error && !is_service_worker_index) { + page.parts.forEach(part => { + if (!part) + return; + // using concat because it could be a string or an array. thanks webpack! + preload_files = preload_files.concat(build_info.assets[part.name]); + }); + } + const link = preload_files + .filter((v, i, a) => a.indexOf(v) === i) // remove any duplicates + .filter(file => file && !file.match(/\.map$/)) // exclude source maps + .map((file) => { + const as = /\.css$/.test(file) ? 'style' : 'script'; + const rel = es6_preload && as === 'script' ? 'modulepreload' : 'preload'; + return `<${req.baseUrl}/client/${file}>;rel="${rel}";as="${as}"`; + }) + .join(', '); + res.setHeader('Link', link); + let session; + try { + session = yield session_getter(req, res); + } + catch (err) { + return bail(res, err); + } + let redirect; + let preload_error; + const preload_context = { + redirect: (statusCode, location) => { + if (redirect && (redirect.statusCode !== statusCode || redirect.location !== location)) { + throw new Error('Conflicting redirects'); + } + location = location.replace(/^\//g, ''); // leading slash (only) + redirect = { statusCode, location }; + }, + error: (statusCode, message) => { + preload_error = { statusCode, message }; + }, + fetch: (url, opts) => { + const protocol = req.socket.encrypted ? 'https' : 'http'; + const parsed = new Url__default['default'].URL(url, `${protocol}://127.0.0.1:${process.env.PORT}${req.baseUrl ? req.baseUrl + '/' : ''}`); + opts = Object.assign({}, opts); + const include_credentials = (opts.credentials === 'include' || + opts.credentials !== 'omit' && parsed.origin === `${protocol}://127.0.0.1:${process.env.PORT}`); + if (include_credentials) { + opts.headers = Object.assign({}, opts.headers); + const cookies = Object.assign({}, parse_1(req.headers.cookie || ''), parse_1(opts.headers.cookie || '')); + const set_cookie = res.getHeader('Set-Cookie'); + (Array.isArray(set_cookie) ? set_cookie : [set_cookie]).forEach((s) => { + const m = /([^=]+)=([^;]+)/.exec(s); + if (m) + cookies[m[1]] = m[2]; + }); + const str = Object.keys(cookies) + .map(key => `${key}=${cookies[key]}`) + .join('; '); + opts.headers.cookie = str; + if (!opts.headers.authorization && req.headers.authorization) { + opts.headers.authorization = req.headers.authorization; + } + } + return fetch(parsed.href, opts); + } + }; + let preloaded; + let match; + let params; + try { + const root_preload = manifest.root_comp.preload || (() => { }); + const root_preloaded = root_preload.call(preload_context, { + host: req.headers.host, + path: req.path, + query: req.query, + params: {} + }, session); + match = error ? null : page.pattern.exec(req.path); + let toPreload = [root_preloaded]; + if (!is_service_worker_index) { + toPreload = toPreload.concat(page.parts.map(part => { + if (!part) + return null; + // the deepest level is used below, to initialise the store + params = part.params ? part.params(match) : {}; + return part.component.preload + ? part.component.preload.call(preload_context, { + host: req.headers.host, + path: req.path, + query: req.query, + params + }, session) + : {}; + })); + } + preloaded = yield Promise.all(toPreload); + } + catch (err) { + if (error) { + return bail(res, err); + } + preload_error = { statusCode: 500, message: err }; + preloaded = []; // appease TypeScript + } + try { + if (redirect) { + const location = Url__default['default'].resolve((req.baseUrl || '') + '/', redirect.location); + res.statusCode = redirect.statusCode; + res.setHeader('Location', location); + res.end(); + return; + } + if (preload_error) { + if (!error) { + handle_error(req, res, preload_error.statusCode, preload_error.message); + } + else { + bail(res, preload_error.message); + } + return; + } + const segments = req.path.split('/').filter(Boolean); + // TODO make this less confusing + const layout_segments = [segments[0]]; + let l = 1; + page.parts.forEach((part, i) => { + layout_segments[l] = segments[i + 1]; + if (!part) + return null; + l++; + }); + if (error instanceof Error && error.stack) { + error.stack = sourcemap_stacktrace(error.stack); + } + const pageContext = { + host: req.headers.host, + path: req.path, + query: req.query, + params, + error: error + ? error instanceof Error + ? error + : { message: error, name: 'PreloadError' } + : null + }; + const props = { + stores: { + page: { + subscribe: writable(pageContext).subscribe + }, + preloading: { + subscribe: writable(null).subscribe + }, + session: writable(session) + }, + segments: layout_segments, + status: error ? status : 200, + error: pageContext.error, + level0: { + props: preloaded[0] + }, + level1: { + segment: segments[0], + props: {} + } + }; + if (!is_service_worker_index) { + let level_index = 1; + for (let i = 0; i < page.parts.length; i += 1) { + const part = page.parts[i]; + if (!part) + continue; + props[`level${level_index++}`] = { + component: part.component.default, + props: preloaded[i + 1] || {}, + segment: segments[i] + }; + } + } + const { html, head, css } = App.render(props); + const serialized = { + preloaded: `[${preloaded.map(data => try_serialize(data, err => { + console.error(`Failed to serialize preloaded data to transmit to the client at the /${segments.join('/')} route: ${err.message}`); + console.warn('The client will re-render over the server-rendered page fresh instead of continuing where it left off. See https://sapper.svelte.dev/docs#Return_value for more information'); + })).join(',')}]`, + session: session && try_serialize(session, err => { + throw new Error(`Failed to serialize session data: ${err.message}`); + }), + error: error && serialize_error(props.error) + }; + let script = `__SAPPER__={${[ + error && `error:${serialized.error},status:${status}`, + `baseUrl:"${req.baseUrl}"`, + serialized.preloaded && `preloaded:${serialized.preloaded}`, + serialized.session && `session:${serialized.session}` + ].filter(Boolean).join(',')}};`; + if (has_service_worker) { + script += `if('serviceWorker' in navigator)navigator.serviceWorker.register('${req.baseUrl}/service-worker.js');`; + } + const file = [].concat(build_info.assets.main).filter(f => f && /\.js$/.test(f))[0]; + const main = `${req.baseUrl}/client/${file}`; + // users can set a CSP nonce using res.locals.nonce + const nonce_value = (res.locals && res.locals.nonce) ? res.locals.nonce : ''; + const nonce_attr = nonce_value ? ` nonce="${nonce_value}"` : ''; + if (build_info.bundler === 'rollup') { + if (build_info.legacy_assets) { + const legacy_main = `${req.baseUrl}/client/legacy/${build_info.legacy_assets.main}`; + script += `(function(){try{eval("async function x(){}");var main="${main}"}catch(e){main="${legacy_main}"};var s=document.createElement("script");try{new Function("if(0)import('')")();s.src=main;s.type="module";s.crossOrigin="use-credentials";}catch(e){s.src="${req.baseUrl}/client/shimport@${build_info.shimport}.js";s.setAttribute("data-main",main);}document.head.appendChild(s);}());`; + } + else { + script += `var s=document.createElement("script");try{new Function("if(0)import('')")();s.src="${main}";s.type="module";s.crossOrigin="use-credentials";}catch(e){s.src="${req.baseUrl}/client/shimport@${build_info.shimport}.js";s.setAttribute("data-main","${main}")}document.head.appendChild(s)`; + } + } + else { + script += ``) + .replace('%sapper.html%', () => html) + .replace('%sapper.head%', () => head) + .replace('%sapper.styles%', () => styles) + .replace(/%sapper\.cspnonce%/g, () => nonce_value); + res.statusCode = status; + res.end(body); + } + catch (err) { + if (error) { + bail(res, err); + } + else { + handle_error(req, res, 500, err); + } + } + }); + } + return function find_route(req, res, next) { + const path = req.path === '/service-worker-index.html' ? '/' : req.path; + const page = pages.find(page => page.pattern.test(path)); + if (page) { + handle_page(page, req, res); + } + else { + handle_error(req, res, 404, 'Not found'); + } + }; +} +function read_template(dir = build_dir) { + return fs__default['default'].readFileSync(`${dir}/template.html`, 'utf-8'); +} +function try_serialize(data, fail) { + try { + return devalue(data); + } + catch (err) { + if (fail) + fail(err); + return null; + } +} +// Ensure we return something truthy so the client will not re-render the page over the error +function serialize_error(error) { + if (!error) + return null; + let serialized = try_serialize(error); + if (!serialized) { + const { name, message, stack } = error; + serialized = try_serialize({ name, message, stack }); + } + if (!serialized) { + serialized = '{}'; + } + return serialized; +} + +function middleware(opts = {}) { + const { session, ignore } = opts; + let emitted_basepath = false; + return compose_handlers(ignore, [ + (req, res, next) => { + if (req.baseUrl === undefined) { + let originalUrl = req.originalUrl || req.url; + if (req.url === '/' && originalUrl[originalUrl.length - 1] !== '/') { + originalUrl += '/'; + } + req.baseUrl = originalUrl + ? originalUrl.slice(0, -req.url.length) + : ''; + } + if (!emitted_basepath && process.send) { + process.send({ + __sapper__: true, + event: 'basepath', + basepath: req.baseUrl + }); + emitted_basepath = true; + } + if (req.path === undefined) { + req.path = req.url.replace(/\?.*/, ''); + } + next(); + }, + fs__default['default'].existsSync(path__default['default'].join(build_dir, 'service-worker.js')) && serve({ + pathname: '/service-worker.js', + cache_control: 'no-cache, no-store, must-revalidate' + }), + fs__default['default'].existsSync(path__default['default'].join(build_dir, 'service-worker.js.map')) && serve({ + pathname: '/service-worker.js.map', + cache_control: 'no-cache, no-store, must-revalidate' + }), + serve({ + prefix: '/client/', + cache_control: 'max-age=31536000, immutable' + }), + get_server_route_handler(manifest.server_routes), + get_page_handler(manifest, session || noop) + ].filter(Boolean)); +} +function compose_handlers(ignore, handlers) { + const total = handlers.length; + function nth_handler(n, req, res, next) { + if (n >= total) { + return next(); + } + handlers[n](req, res, () => nth_handler(n + 1, req, res, next)); + } + return !ignore + ? (req, res, next) => nth_handler(0, req, res, next) + : (req, res, next) => { + if (should_ignore(req.path, ignore)) { + next(); + } + else { + nth_handler(0, req, res, next); + } + }; +} +function should_ignore(uri, val) { + if (Array.isArray(val)) + return val.some(x => should_ignore(uri, x)); + if (val instanceof RegExp) + return val.test(uri); + if (typeof val === 'function') + return val(uri); + return uri.startsWith(val.charCodeAt(0) === 47 ? val : `/${val}`); +} +function serve({ prefix, pathname, cache_control }) { + const filter = pathname + ? (req) => req.path === pathname + : (req) => req.path.startsWith(prefix); + const cache = new Map(); + const read = (file) => (cache.has(file) ? cache : cache.set(file, fs__default['default'].readFileSync(path__default['default'].join(build_dir, file)))).get(file); + return (req, res, next) => { + if (filter(req)) { + const type = lite.getType(req.path); + try { + const file = path__default['default'].posix.normalize(decodeURIComponent(req.path)); + const data = read(file); + res.setHeader('Content-Type', type); + res.setHeader('Cache-Control', cache_control); + res.end(data); + } + catch (err) { + if (err.code === 'ENOENT') { + next(); + } + else { + console.error(err); + res.statusCode = 500; + res.end('an error occurred while reading a static file from disk'); + } + } + } + else { + next(); + } + }; +} +function noop() { } + +const { PORT, NODE_ENV } = process.env; +const dev = NODE_ENV === 'development'; + +polka__default['default']() // You can also use Express + .use( + compression__default['default']({ threshold: 0 }), + sirv__default['default']('static', { dev }), + middleware() + ) + .listen(PORT, err => { + if (err) console.log('error', err); + }); diff --git a/__sapper__/build/service-worker.js b/__sapper__/build/service-worker.js new file mode 100644 index 0000000..9e05c12 --- /dev/null +++ b/__sapper__/build/service-worker.js @@ -0,0 +1 @@ +!function(){"use strict";const e=1618988151097,t=`cache${e}`,n=["/client/client.c2670d50.js","/client/inject_styles.5607aec6.js","/client/index.ade356b9.js","/client/about.e97c8bf2.js","/client/index.39882a62.js","/client/[slug].fac92fe6.js"].concat(["/service-worker-index.html","/favicon.png","/global.css","/logo-192.png","/logo-512.png","/main.css","/manifest.json","/tailwind.css"]),s=new Set(n);self.addEventListener("install",(e=>{e.waitUntil(caches.open(t).then((e=>e.addAll(n))).then((()=>{self.skipWaiting()})))})),self.addEventListener("activate",(e=>{e.waitUntil(caches.keys().then((async e=>{for(const n of e)n!==t&&await caches.delete(n);self.clients.claim()})))})),self.addEventListener("fetch",(t=>{if("GET"!==t.request.method||t.request.headers.has("range"))return;const n=new URL(t.request.url),c=n.protocol.startsWith("http"),a=n.hostname===self.location.hostname&&n.port!==self.location.port,i=n.host===self.location.host&&s.has(n.pathname),o="only-if-cached"===t.request.cache&&!i;!c||a||o||t.respondWith((async()=>i&&await caches.match(t.request)||async function(t){const n=await caches.open(`offline${e}`);try{const e=await fetch(t);return n.put(t,e.clone()),e}catch(e){const s=await n.match(t);if(s)return s;throw e}}(t.request))())}))}(); diff --git a/__sapper__/build/template.html b/__sapper__/build/template.html new file mode 100644 index 0000000..e312256 --- /dev/null +++ b/__sapper__/build/template.html @@ -0,0 +1 @@ + %sapper.base% %sapper.scripts% %sapper.styles% %sapper.head%