continue svelte exploration

This commit is contained in:
jane 2021-08-18 23:39:53 -04:00
parent a297a965aa
commit ed727faec9
49 changed files with 39809 additions and 1330 deletions

5
frontend/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
.DS_Store
/node_modules/
/src/node_modules/@sapper/
yarn-error.log
/__sapper__/

View file

@ -0,0 +1,11 @@
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx|svelte)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-svelte-csf"
]
}

View file

@ -0,0 +1,9 @@
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}

View file

@ -1,105 +1 @@
*Looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)* # Todo Frontend
---
# svelte app
This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template.
To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
```bash
npx degit sveltejs/template svelte-app
cd svelte-app
```
*Note that you will need to have [Node.js](https://nodejs.org) installed.*
## Get started
Install the dependencies...
```bash
cd svelte-app
npm install
```
...then start [Rollup](https://rollupjs.org):
```bash
npm run dev
```
Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`.
If you're using [Visual Studio Code](https://code.visualstudio.com/) we recommend installing the official extension [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). If you are using other editors you may need to install a plugin in order to get syntax highlighting and intellisense.
## Building and running in production mode
To create an optimised version of the app:
```bash
npm run build
```
You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com).
## Single-page app mode
By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json:
```js
"start": "sirv public --single"
```
## Using TypeScript
This template comes with a script to set up a TypeScript development environment, you can run it immediately after cloning the template with:
```bash
node scripts/setupTypeScript.js
```
Or remove the script via:
```bash
rm scripts/setupTypeScript.js
```
## Deploying to the web
### With [Vercel](https://vercel.com)
Install `vercel` if you haven't already:
```bash
npm install -g vercel
```
Then, from within your project folder:
```bash
cd public
vercel deploy --name my-project
```
### With [surge](https://surge.sh/)
Install `surge` if you haven't already:
```bash
npm install -g surge
```
Then, from within your project folder:
```bash
npm run build
surge public my-project.surge.sh
```

38893
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,23 +1,39 @@
{ {
"name": "svelte-app", "name": "TODO",
"version": "1.0.0", "description": "TODO",
"private": true, "version": "0.0.1",
"scripts": { "scripts": {
"build": "rollup -c", "dev": "sapper dev",
"dev": "rollup -c -w", "build": "sapper build --legacy",
"start": "sirv public --no-clear --host --port 3000" "export": "sapper export --legacy",
}, "start": "node __sapper__/build",
"devDependencies": { "build-storybook": "build-storybook -s static",
"@rollup/plugin-commonjs": "^17.0.0", "storybook": "start-storybook -p 9009 -s static"
"@rollup/plugin-node-resolve": "^11.0.0",
"rollup": "^2.3.4",
"rollup-plugin-css-only": "^3.1.0",
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-svelte": "^7.0.0",
"rollup-plugin-terser": "^7.0.0",
"svelte": "^3.0.0"
}, },
"dependencies": { "dependencies": {
"sirv-cli": "^1.0.0" "compression": "^1.7.1",
"polka": "next",
"sirv": "^1.0.0"
},
"devDependencies": {
"sapper": "^0.28.0",
"svelte": "^3.17.3",
"@babel/core": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/runtime": "^7.0.0",
"@rollup/plugin-babel": "^5.0.0",
"@rollup/plugin-commonjs": "^14.0.0",
"@rollup/plugin-node-resolve": "^8.0.0",
"@rollup/plugin-replace": "^2.4.0",
"@rollup/plugin-url": "^5.0.0",
"rollup": "^2.3.4",
"rollup-plugin-svelte": "^7.0.0",
"rollup-plugin-terser": "^7.0.0",
"@storybook/addon-actions": "^6.3.7",
"@storybook/addons": "^6.3.7",
"@storybook/svelte": "^6.3.7",
"@storybook/theming": "^6.3.7"
} }
} }

View file

@ -1 +0,0 @@
.svelte-lxvsk7{font-family:inherit;font-size:inherit}input.svelte-lxvsk7{display:block;margin:0 0 0.5em 0}select.svelte-lxvsk7{float:left;margin:0 1em 1em 0;width:14em}.buttons.svelte-lxvsk7{clear:both}

View file

@ -1,801 +0,0 @@
(function(l, r) { if (!l || l.getElementById('livereloadscript')) return; r = l.createElement('script'); r.async = 1; r.src = '//' + (self.location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; r.id = 'livereloadscript'; l.getElementsByTagName('head')[0].appendChild(r) })(self.document);
var app = (function () {
'use strict';
function noop() { }
function add_location(element, file, line, column, char) {
element.__svelte_meta = {
loc: { file, line, column, char }
};
}
function run(fn) {
return fn();
}
function blank_object() {
return Object.create(null);
}
function run_all(fns) {
fns.forEach(run);
}
function is_function(thing) {
return typeof thing === 'function';
}
function safe_not_equal(a, b) {
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
}
function is_empty(obj) {
return Object.keys(obj).length === 0;
}
function append(target, node) {
target.appendChild(node);
}
function insert(target, node, anchor) {
target.insertBefore(node, anchor || null);
}
function detach(node) {
node.parentNode.removeChild(node);
}
function destroy_each(iterations, detaching) {
for (let i = 0; i < iterations.length; i += 1) {
if (iterations[i])
iterations[i].d(detaching);
}
}
function element(name) {
return document.createElement(name);
}
function text(data) {
return document.createTextNode(data);
}
function space() {
return text(' ');
}
function listen(node, event, handler, options) {
node.addEventListener(event, handler, options);
return () => node.removeEventListener(event, handler, options);
}
function attr(node, attribute, value) {
if (value == null)
node.removeAttribute(attribute);
else if (node.getAttribute(attribute) !== value)
node.setAttribute(attribute, value);
}
function children(element) {
return Array.from(element.childNodes);
}
function set_input_value(input, value) {
input.value = value == null ? '' : value;
}
function select_option(select, value) {
for (let i = 0; i < select.options.length; i += 1) {
const option = select.options[i];
if (option.__value === value) {
option.selected = true;
return;
}
}
}
function select_value(select) {
const selected_option = select.querySelector(':checked') || select.options[0];
return selected_option && selected_option.__value;
}
function custom_event(type, detail, bubbles = false) {
const e = document.createEvent('CustomEvent');
e.initCustomEvent(type, bubbles, false, detail);
return e;
}
let current_component;
function set_current_component(component) {
current_component = component;
}
const dirty_components = [];
const binding_callbacks = [];
const render_callbacks = [];
const flush_callbacks = [];
const resolved_promise = Promise.resolve();
let update_scheduled = false;
function schedule_update() {
if (!update_scheduled) {
update_scheduled = true;
resolved_promise.then(flush);
}
}
function add_render_callback(fn) {
render_callbacks.push(fn);
}
let flushing = false;
const seen_callbacks = new Set();
function flush() {
if (flushing)
return;
flushing = true;
do {
// first, call beforeUpdate functions
// and update components
for (let i = 0; i < dirty_components.length; i += 1) {
const component = dirty_components[i];
set_current_component(component);
update(component.$$);
}
set_current_component(null);
dirty_components.length = 0;
while (binding_callbacks.length)
binding_callbacks.pop()();
// then, once components are updated, call
// afterUpdate functions. This may cause
// subsequent updates...
for (let i = 0; i < render_callbacks.length; i += 1) {
const callback = render_callbacks[i];
if (!seen_callbacks.has(callback)) {
// ...so guard against infinite loops
seen_callbacks.add(callback);
callback();
}
}
render_callbacks.length = 0;
} while (dirty_components.length);
while (flush_callbacks.length) {
flush_callbacks.pop()();
}
update_scheduled = false;
flushing = false;
seen_callbacks.clear();
}
function update($$) {
if ($$.fragment !== null) {
$$.update();
run_all($$.before_update);
const dirty = $$.dirty;
$$.dirty = [-1];
$$.fragment && $$.fragment.p($$.ctx, dirty);
$$.after_update.forEach(add_render_callback);
}
}
const outroing = new Set();
function transition_in(block, local) {
if (block && block.i) {
outroing.delete(block);
block.i(local);
}
}
function mount_component(component, target, anchor, customElement) {
const { fragment, on_mount, on_destroy, after_update } = component.$$;
fragment && fragment.m(target, anchor);
if (!customElement) {
// onMount happens before the initial afterUpdate
add_render_callback(() => {
const new_on_destroy = on_mount.map(run).filter(is_function);
if (on_destroy) {
on_destroy.push(...new_on_destroy);
}
else {
// Edge case - component was destroyed immediately,
// most likely as a result of a binding initialising
run_all(new_on_destroy);
}
component.$$.on_mount = [];
});
}
after_update.forEach(add_render_callback);
}
function destroy_component(component, detaching) {
const $$ = component.$$;
if ($$.fragment !== null) {
run_all($$.on_destroy);
$$.fragment && $$.fragment.d(detaching);
// TODO null out other refs, including component.$$ (but need to
// preserve final state?)
$$.on_destroy = $$.fragment = null;
$$.ctx = [];
}
}
function make_dirty(component, i) {
if (component.$$.dirty[0] === -1) {
dirty_components.push(component);
schedule_update();
component.$$.dirty.fill(0);
}
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));
}
function init(component, options, instance, create_fragment, not_equal, props, append_styles, dirty = [-1]) {
const parent_component = current_component;
set_current_component(component);
const $$ = component.$$ = {
fragment: null,
ctx: null,
// state
props,
update: noop,
not_equal,
bound: blank_object(),
// lifecycle
on_mount: [],
on_destroy: [],
on_disconnect: [],
before_update: [],
after_update: [],
context: new Map(parent_component ? parent_component.$$.context : options.context || []),
// everything else
callbacks: blank_object(),
dirty,
skip_bound: false,
root: options.target || parent_component.$$.root
};
append_styles && append_styles($$.root);
let ready = false;
$$.ctx = instance
? instance(component, options.props || {}, (i, ret, ...rest) => {
const value = rest.length ? rest[0] : ret;
if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
if (!$$.skip_bound && $$.bound[i])
$$.bound[i](value);
if (ready)
make_dirty(component, i);
}
return ret;
})
: [];
$$.update();
ready = true;
run_all($$.before_update);
// `false` as a special case of no DOM component
$$.fragment = create_fragment ? create_fragment($$.ctx) : false;
if (options.target) {
if (options.hydrate) {
const nodes = children(options.target);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.l(nodes);
nodes.forEach(detach);
}
else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$$.fragment && $$.fragment.c();
}
if (options.intro)
transition_in(component.$$.fragment);
mount_component(component, options.target, options.anchor, options.customElement);
flush();
}
set_current_component(parent_component);
}
/**
* Base class for Svelte components. Used when dev=false.
*/
class SvelteComponent {
$destroy() {
destroy_component(this, 1);
this.$destroy = noop;
}
$on(type, callback) {
const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = []));
callbacks.push(callback);
return () => {
const index = callbacks.indexOf(callback);
if (index !== -1)
callbacks.splice(index, 1);
};
}
$set($$props) {
if (this.$$set && !is_empty($$props)) {
this.$$.skip_bound = true;
this.$$set($$props);
this.$$.skip_bound = false;
}
}
}
function dispatch_dev(type, detail) {
document.dispatchEvent(custom_event(type, Object.assign({ version: '3.42.1' }, detail), true));
}
function append_dev(target, node) {
dispatch_dev('SvelteDOMInsert', { target, node });
append(target, node);
}
function insert_dev(target, node, anchor) {
dispatch_dev('SvelteDOMInsert', { target, node, anchor });
insert(target, node, anchor);
}
function detach_dev(node) {
dispatch_dev('SvelteDOMRemove', { node });
detach(node);
}
function listen_dev(node, event, handler, options, has_prevent_default, has_stop_propagation) {
const modifiers = options === true ? ['capture'] : options ? Array.from(Object.keys(options)) : [];
if (has_prevent_default)
modifiers.push('preventDefault');
if (has_stop_propagation)
modifiers.push('stopPropagation');
dispatch_dev('SvelteDOMAddEventListener', { node, event, handler, modifiers });
const dispose = listen(node, event, handler, options);
return () => {
dispatch_dev('SvelteDOMRemoveEventListener', { node, event, handler, modifiers });
dispose();
};
}
function attr_dev(node, attribute, value) {
attr(node, attribute, value);
if (value == null)
dispatch_dev('SvelteDOMRemoveAttribute', { node, attribute });
else
dispatch_dev('SvelteDOMSetAttribute', { node, attribute, value });
}
function prop_dev(node, property, value) {
node[property] = value;
dispatch_dev('SvelteDOMSetProperty', { node, property, value });
}
function set_data_dev(text, data) {
data = '' + data;
if (text.wholeText === data)
return;
dispatch_dev('SvelteDOMSetData', { node: text, data });
text.data = data;
}
function validate_each_argument(arg) {
if (typeof arg !== 'string' && !(arg && typeof arg === 'object' && 'length' in arg)) {
let msg = '{#each} only iterates over array-like objects.';
if (typeof Symbol === 'function' && arg && Symbol.iterator in arg) {
msg += ' You can use a spread to convert this iterable into an array.';
}
throw new Error(msg);
}
}
function validate_slots(name, slot, keys) {
for (const slot_key of Object.keys(slot)) {
if (!~keys.indexOf(slot_key)) {
console.warn(`<${name}> received an unexpected slot "${slot_key}".`);
}
}
}
/**
* Base class for Svelte components with some minor dev-enhancements. Used when dev=true.
*/
class SvelteComponentDev extends SvelteComponent {
constructor(options) {
if (!options || (!options.target && !options.$$inline)) {
throw new Error("'target' is a required option");
}
super();
}
$destroy() {
super.$destroy();
this.$destroy = () => {
console.warn('Component was already destroyed'); // eslint-disable-line no-console
};
}
$capture_state() { }
$inject_state() { }
}
/* src/App.svelte generated by Svelte v3.42.1 */
const file = "src/App.svelte";
function get_each_context(ctx, list, i) {
const child_ctx = ctx.slice();
child_ctx[15] = list[i];
child_ctx[3] = i;
return child_ctx;
}
// (56:1) {#each filteredPeople as person, i}
function create_each_block(ctx) {
let option;
let t0_value = /*person*/ ctx[15].last + "";
let t0;
let t1;
let t2_value = /*person*/ ctx[15].first + "";
let t2;
const block = {
c: function create() {
option = element("option");
t0 = text(t0_value);
t1 = text(", ");
t2 = text(t2_value);
option.__value = /*i*/ ctx[3];
option.value = option.__value;
attr_dev(option, "class", "svelte-lxvsk7");
add_location(option, file, 56, 2, 1258);
},
m: function mount(target, anchor) {
insert_dev(target, option, anchor);
append_dev(option, t0);
append_dev(option, t1);
append_dev(option, t2);
},
p: function update(ctx, dirty) {
if (dirty & /*filteredPeople*/ 2 && t0_value !== (t0_value = /*person*/ ctx[15].last + "")) set_data_dev(t0, t0_value);
if (dirty & /*filteredPeople*/ 2 && t2_value !== (t2_value = /*person*/ ctx[15].first + "")) set_data_dev(t2, t2_value);
},
d: function destroy(detaching) {
if (detaching) detach_dev(option);
}
};
dispatch_dev("SvelteRegisterBlock", {
block,
id: create_each_block.name,
type: "each",
source: "(56:1) {#each filteredPeople as person, i}",
ctx
});
return block;
}
function create_fragment(ctx) {
let input0;
let t0;
let select;
let t1;
let label0;
let input1;
let t2;
let label1;
let input2;
let t3;
let div;
let button0;
let t4;
let button0_disabled_value;
let t5;
let button1;
let t6;
let button1_disabled_value;
let t7;
let button2;
let t8;
let button2_disabled_value;
let mounted;
let dispose;
let each_value = /*filteredPeople*/ ctx[1];
validate_each_argument(each_value);
let each_blocks = [];
for (let i = 0; i < each_value.length; i += 1) {
each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i));
}
const block = {
c: function create() {
input0 = element("input");
t0 = space();
select = element("select");
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].c();
}
t1 = space();
label0 = element("label");
input1 = element("input");
t2 = space();
label1 = element("label");
input2 = element("input");
t3 = space();
div = element("div");
button0 = element("button");
t4 = text("create");
t5 = space();
button1 = element("button");
t6 = text("update");
t7 = space();
button2 = element("button");
t8 = text("delete");
attr_dev(input0, "placeholder", "filter prefix");
attr_dev(input0, "class", "svelte-lxvsk7");
add_location(input0, file, 52, 0, 1129);
attr_dev(select, "size", 5);
attr_dev(select, "class", "svelte-lxvsk7");
if (/*i*/ ctx[3] === void 0) add_render_callback(() => /*select_change_handler*/ ctx[11].call(select));
add_location(select, file, 54, 0, 1186);
attr_dev(input1, "placeholder", "first");
attr_dev(input1, "class", "svelte-lxvsk7");
add_location(input1, file, 60, 7, 1342);
attr_dev(label0, "class", "svelte-lxvsk7");
add_location(label0, file, 60, 0, 1335);
attr_dev(input2, "placeholder", "last");
attr_dev(input2, "class", "svelte-lxvsk7");
add_location(input2, file, 61, 7, 1404);
attr_dev(label1, "class", "svelte-lxvsk7");
add_location(label1, file, 61, 0, 1397);
button0.disabled = button0_disabled_value = !/*first*/ ctx[4] || !/*last*/ ctx[5];
attr_dev(button0, "class", "svelte-lxvsk7");
add_location(button0, file, 64, 1, 1481);
button1.disabled = button1_disabled_value = !/*first*/ ctx[4] || !/*last*/ ctx[5] || !/*selected*/ ctx[2];
attr_dev(button1, "class", "svelte-lxvsk7");
add_location(button1, file, 65, 1, 1553);
button2.disabled = button2_disabled_value = !/*selected*/ ctx[2];
attr_dev(button2, "class", "svelte-lxvsk7");
add_location(button2, file, 66, 1, 1638);
attr_dev(div, "class", "buttons svelte-lxvsk7");
add_location(div, file, 63, 0, 1458);
},
l: function claim(nodes) {
throw new Error("options.hydrate only works if the component was compiled with the `hydratable: true` option");
},
m: function mount(target, anchor) {
insert_dev(target, input0, anchor);
set_input_value(input0, /*prefix*/ ctx[0]);
insert_dev(target, t0, anchor);
insert_dev(target, select, anchor);
for (let i = 0; i < each_blocks.length; i += 1) {
each_blocks[i].m(select, null);
}
select_option(select, /*i*/ ctx[3]);
insert_dev(target, t1, anchor);
insert_dev(target, label0, anchor);
append_dev(label0, input1);
set_input_value(input1, /*first*/ ctx[4]);
insert_dev(target, t2, anchor);
insert_dev(target, label1, anchor);
append_dev(label1, input2);
set_input_value(input2, /*last*/ ctx[5]);
insert_dev(target, t3, anchor);
insert_dev(target, div, anchor);
append_dev(div, button0);
append_dev(button0, t4);
append_dev(div, t5);
append_dev(div, button1);
append_dev(button1, t6);
append_dev(div, t7);
append_dev(div, button2);
append_dev(button2, t8);
if (!mounted) {
dispose = [
listen_dev(input0, "input", /*input0_input_handler*/ ctx[10]),
listen_dev(select, "change", /*select_change_handler*/ ctx[11]),
listen_dev(input1, "input", /*input1_input_handler*/ ctx[12]),
listen_dev(input2, "input", /*input2_input_handler*/ ctx[13]),
listen_dev(button0, "click", /*create*/ ctx[6], false, false, false),
listen_dev(button1, "click", /*update*/ ctx[7], false, false, false),
listen_dev(button2, "click", /*remove*/ ctx[8], false, false, false)
];
mounted = true;
}
},
p: function update(ctx, [dirty]) {
if (dirty & /*prefix*/ 1 && input0.value !== /*prefix*/ ctx[0]) {
set_input_value(input0, /*prefix*/ ctx[0]);
}
if (dirty & /*filteredPeople*/ 2) {
each_value = /*filteredPeople*/ ctx[1];
validate_each_argument(each_value);
let i;
for (i = 0; i < each_value.length; i += 1) {
const child_ctx = get_each_context(ctx, each_value, i);
if (each_blocks[i]) {
each_blocks[i].p(child_ctx, dirty);
} else {
each_blocks[i] = create_each_block(child_ctx);
each_blocks[i].c();
each_blocks[i].m(select, null);
}
}
for (; i < each_blocks.length; i += 1) {
each_blocks[i].d(1);
}
each_blocks.length = each_value.length;
}
if (dirty & /*i*/ 8) {
select_option(select, /*i*/ ctx[3]);
}
if (dirty & /*first*/ 16 && input1.value !== /*first*/ ctx[4]) {
set_input_value(input1, /*first*/ ctx[4]);
}
if (dirty & /*last*/ 32 && input2.value !== /*last*/ ctx[5]) {
set_input_value(input2, /*last*/ ctx[5]);
}
if (dirty & /*first, last*/ 48 && button0_disabled_value !== (button0_disabled_value = !/*first*/ ctx[4] || !/*last*/ ctx[5])) {
prop_dev(button0, "disabled", button0_disabled_value);
}
if (dirty & /*first, last, selected*/ 52 && button1_disabled_value !== (button1_disabled_value = !/*first*/ ctx[4] || !/*last*/ ctx[5] || !/*selected*/ ctx[2])) {
prop_dev(button1, "disabled", button1_disabled_value);
}
if (dirty & /*selected*/ 4 && button2_disabled_value !== (button2_disabled_value = !/*selected*/ ctx[2])) {
prop_dev(button2, "disabled", button2_disabled_value);
}
},
i: noop,
o: noop,
d: function destroy(detaching) {
if (detaching) detach_dev(input0);
if (detaching) detach_dev(t0);
if (detaching) detach_dev(select);
destroy_each(each_blocks, detaching);
if (detaching) detach_dev(t1);
if (detaching) detach_dev(label0);
if (detaching) detach_dev(t2);
if (detaching) detach_dev(label1);
if (detaching) detach_dev(t3);
if (detaching) detach_dev(div);
mounted = false;
run_all(dispose);
}
};
dispatch_dev("SvelteRegisterBlock", {
block,
id: create_fragment.name,
type: "component",
source: "",
ctx
});
return block;
}
function instance($$self, $$props, $$invalidate) {
let filteredPeople;
let selected;
let { $$slots: slots = {}, $$scope } = $$props;
validate_slots('App', slots, []);
let people = [
{ first: 'Hans', last: 'Emil' },
{ first: 'Max', last: 'Mustermann' },
{ first: 'Roman', last: 'Tisch' }
];
let prefix = '';
let first = '';
let last = '';
let i = 0;
function create() {
$$invalidate(9, people = people.concat({ first, last }));
$$invalidate(3, i = people.length - 1);
$$invalidate(4, first = $$invalidate(5, last = ''));
}
function update() {
$$invalidate(2, selected.first = first, selected);
$$invalidate(2, selected.last = last, selected);
$$invalidate(9, people);
}
function remove() {
// Remove selected person from the source array (people), not the filtered array
const index = people.indexOf(selected);
$$invalidate(9, people = [...people.slice(0, index), ...people.slice(index + 1)]);
$$invalidate(4, first = $$invalidate(5, last = ''));
$$invalidate(3, i = Math.min(i, filteredPeople.length - 2));
}
function reset_inputs(person) {
$$invalidate(4, first = person ? person.first : '');
$$invalidate(5, last = person ? person.last : '');
}
const writable_props = [];
Object.keys($$props).forEach(key => {
if (!~writable_props.indexOf(key) && key.slice(0, 2) !== '$$' && key !== 'slot') console.warn(`<App> was created with unknown prop '${key}'`);
});
function input0_input_handler() {
prefix = this.value;
$$invalidate(0, prefix);
}
function select_change_handler() {
i = select_value(this);
$$invalidate(3, i);
}
function input1_input_handler() {
first = this.value;
$$invalidate(4, first);
}
function input2_input_handler() {
last = this.value;
$$invalidate(5, last);
}
$$self.$capture_state = () => ({
people,
prefix,
first,
last,
i,
create,
update,
remove,
reset_inputs,
filteredPeople,
selected
});
$$self.$inject_state = $$props => {
if ('people' in $$props) $$invalidate(9, people = $$props.people);
if ('prefix' in $$props) $$invalidate(0, prefix = $$props.prefix);
if ('first' in $$props) $$invalidate(4, first = $$props.first);
if ('last' in $$props) $$invalidate(5, last = $$props.last);
if ('i' in $$props) $$invalidate(3, i = $$props.i);
if ('filteredPeople' in $$props) $$invalidate(1, filteredPeople = $$props.filteredPeople);
if ('selected' in $$props) $$invalidate(2, selected = $$props.selected);
};
if ($$props && "$$inject" in $$props) {
$$self.$inject_state($$props.$$inject);
}
$$self.$$.update = () => {
if ($$self.$$.dirty & /*prefix, people*/ 513) {
$$invalidate(1, filteredPeople = prefix
? people.filter(person => {
const name = `${person.last}, ${person.first}`;
return name.toLowerCase().startsWith(prefix.toLowerCase());
})
: people);
}
if ($$self.$$.dirty & /*filteredPeople, i*/ 10) {
$$invalidate(2, selected = filteredPeople[i]);
}
if ($$self.$$.dirty & /*selected*/ 4) {
reset_inputs(selected);
}
};
return [
prefix,
filteredPeople,
selected,
i,
first,
last,
create,
update,
remove,
people,
input0_input_handler,
select_change_handler,
input1_input_handler,
input2_input_handler
];
}
class App extends SvelteComponentDev {
constructor(options) {
super(options);
init(this, options, instance, create_fragment, safe_not_equal, {});
dispatch_dev("SvelteRegisterComponent", {
component: this,
tagName: "App",
options,
id: create_fragment.name
});
}
}
var app = new App({
target: document.body
});
return app;
}());
//# sourceMappingURL=bundle.js.map

File diff suppressed because one or more lines are too long

View file

@ -1,13 +0,0 @@
{
"hosts": {
"localhost:3000": "LOCAL",
"dev.j4.pm": "test",
"todo.j4.pm": "prod"
},
"defaultConfig": {},
"configs": {
"LOCAL": {},
"test": {"apiUrl": "https://dev.j4.pm/api"},
"prod": {"apiUrl": "https://todo.j4.pm/api"}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -1,63 +0,0 @@
html, body {
position: relative;
width: 100%;
height: 100%;
}
body {
color: #333;
margin: 0;
padding: 8px;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
a {
color: rgb(0,100,200);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:visited {
color: rgb(0,80,160);
}
label {
display: block;
}
input, button, select, textarea {
font-family: inherit;
font-size: inherit;
-webkit-padding: 0.4em 0;
padding: 0.4em;
margin: 0 0 0.5em 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px;
}
input:disabled {
color: #ccc;
}
button {
color: #333;
background-color: #f4f4f4;
outline: none;
}
button:disabled {
color: #999;
}
button:not(:disabled):active {
background-color: #ddd;
}
button:focus {
border-color: #666;
}

View file

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Todo</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View file

@ -1,25 +0,0 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View file

@ -1,3 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View file

@ -1,76 +1,127 @@
import svelte from 'rollup-plugin-svelte'; import path from 'path';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve'; import resolve from '@rollup/plugin-node-resolve';
import livereload from 'rollup-plugin-livereload'; import replace from '@rollup/plugin-replace';
import commonjs from '@rollup/plugin-commonjs';
import url from '@rollup/plugin-url';
import svelte from 'rollup-plugin-svelte';
import babel from '@rollup/plugin-babel';
import { terser } from 'rollup-plugin-terser'; import { terser } from 'rollup-plugin-terser';
import css from 'rollup-plugin-css-only'; import config from 'sapper/config/rollup.js';
import pkg from './package.json';
const production = !process.env.ROLLUP_WATCH; const mode = process.env.NODE_ENV;
const dev = mode === 'development';
const legacy = !!process.env.SAPPER_LEGACY_BUILD;
function serve() { const onwarn = (warning, onwarn) =>
let server; (warning.code === 'MISSING_EXPORT' && /'preload'/.test(warning.message)) ||
(warning.code === 'CIRCULAR_DEPENDENCY' && /[/\\]@sapper[/\\]/.test(warning.message)) ||
function toExit() { onwarn(warning);
if (server) server.kill(0);
}
return {
writeBundle() {
if (server) return;
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
process.on('SIGTERM', toExit);
process.on('exit', toExit);
}
};
}
export default { export default {
input: 'src/main.js', client: {
output: { input: config.client.input(),
sourcemap: true, output: config.client.output(),
format: 'iife', plugins: [
name: 'app', replace({
file: 'public/build/bundle.js' preventAssignment: true,
values:{
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode)
},
}),
svelte({
compilerOptions: {
dev,
hydratable: true
}
}),
url({
sourceDir: path.resolve(__dirname, 'src/node_modules/images'),
publicPath: '/client/'
}),
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
legacy && babel({
extensions: ['.js', '.mjs', '.html', '.svelte'],
babelHelpers: 'runtime',
exclude: ['node_modules/@babel/**'],
presets: [
['@babel/preset-env', {
targets: '> 0.25%, not dead'
}]
],
plugins: [
'@babel/plugin-syntax-dynamic-import',
['@babel/plugin-transform-runtime', {
useESModules: true
}]
]
}),
!dev && terser({
module: true
})
],
preserveEntrySignatures: false,
onwarn,
}, },
plugins: [
svelte({
compilerOptions: {
// enable run-time checks when not in production
dev: !production
}
}),
// we'll extract any component CSS out into
// a separate file - better for performance
css({ output: 'bundle.css' }),
// If you have external dependencies installed from server: {
// npm, you'll most likely need these plugins. In input: config.server.input(),
// some cases you'll need additional configuration - output: config.server.output(),
// consult the documentation for details: plugins: [
// https://github.com/rollup/plugins/tree/master/packages/commonjs replace({
resolve({ preventAssignment: true,
browser: true, values:{
dedupe: ['svelte'] 'process.browser': false,
}), 'process.env.NODE_ENV': JSON.stringify(mode)
commonjs(), },
}),
svelte({
compilerOptions: {
dev,
generate: 'ssr',
hydratable: true
},
emitCss: false
}),
url({
sourceDir: path.resolve(__dirname, 'src/node_modules/images'),
publicPath: '/client/',
emitFiles: false // already emitted by client build
}),
resolve({
dedupe: ['svelte']
}),
commonjs()
],
external: Object.keys(pkg.dependencies).concat(require('module').builtinModules),
preserveEntrySignatures: 'strict',
onwarn,
},
// In dev mode, call `npm run start` once serviceworker: {
// the bundle has been generated input: config.serviceworker.input(),
!production && serve(), output: config.serviceworker.output(),
plugins: [
// Watch the `public` directory and refresh the resolve(),
// browser on changes when not in production replace({
!production && livereload('public'), preventAssignment: true,
values:{
// If we're building for production (npm run build 'process.browser': true,
// instead of npm run dev), minify 'process.env.NODE_ENV': JSON.stringify(mode)
production && terser() },
], }),
watch: { commonjs(),
clearScreen: false !dev && terser()
],
preserveEntrySignatures: false,
onwarn,
} }
}; };

View file

@ -1,121 +0,0 @@
// @ts-check
/** This script modifies the project to support TS code in .svelte files like:
<script lang="ts">
export let name: string;
</script>
As well as validating the code for CI.
*/
/** To work on this script:
rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template
*/
const fs = require("fs")
const path = require("path")
const { argv } = require("process")
const projectRoot = argv[2] || path.join(__dirname, "..")
// Add deps to pkg.json
const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8"))
packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, {
"svelte-check": "^2.0.0",
"svelte-preprocess": "^4.0.0",
"@rollup/plugin-typescript": "^8.0.0",
"typescript": "^4.0.0",
"tslib": "^2.0.0",
"@tsconfig/svelte": "^2.0.0"
})
// Add script for checking
packageJSON.scripts = Object.assign(packageJSON.scripts, {
"check": "svelte-check --tsconfig ./tsconfig.json"
})
// Write the package JSON
fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " "))
// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too
const beforeMainJSPath = path.join(projectRoot, "src", "main.js")
const afterMainTSPath = path.join(projectRoot, "src", "main.ts")
fs.renameSync(beforeMainJSPath, afterMainTSPath)
// Switch the app.svelte file to use TS
const appSveltePath = path.join(projectRoot, "src", "App.svelte")
let appFile = fs.readFileSync(appSveltePath, "utf8")
appFile = appFile.replace("<script>", '<script lang="ts">')
appFile = appFile.replace("export let name;", 'export let name: string;')
fs.writeFileSync(appSveltePath, appFile)
// Edit rollup config
const rollupConfigPath = path.join(projectRoot, "rollup.config.js")
let rollupConfig = fs.readFileSync(rollupConfigPath, "utf8")
// Edit imports
rollupConfig = rollupConfig.replace(`'rollup-plugin-terser';`, `'rollup-plugin-terser';
import sveltePreprocess from 'svelte-preprocess';
import typescript from '@rollup/plugin-typescript';`)
// Replace name of entry point
rollupConfig = rollupConfig.replace(`'src/main.js'`, `'src/main.ts'`)
// Add preprocessor
rollupConfig = rollupConfig.replace(
'compilerOptions:',
'preprocess: sveltePreprocess({ sourceMap: !production }),\n\t\t\tcompilerOptions:'
);
// Add TypeScript
rollupConfig = rollupConfig.replace(
'commonjs(),',
'commonjs(),\n\t\ttypescript({\n\t\t\tsourceMap: !production,\n\t\t\tinlineSources: !production\n\t\t}),'
);
fs.writeFileSync(rollupConfigPath, rollupConfig)
// Add TSConfig
const tsconfig = `{
"extends": "@tsconfig/svelte/tsconfig.json",
"include": ["src/**/*"],
"exclude": ["node_modules/*", "__sapper__/*", "public/*"]
}`
const tsconfigPath = path.join(projectRoot, "tsconfig.json")
fs.writeFileSync(tsconfigPath, tsconfig)
// Add global.d.ts
const dtsPath = path.join(projectRoot, "src", "global.d.ts")
fs.writeFileSync(dtsPath, `/// <reference types="svelte" />`)
// Delete this script, but not during testing
if (!argv[2]) {
// Remove the script
fs.unlinkSync(path.join(__filename))
// Check for Mac's DS_store file, and if it's the only one left remove it
const remainingFiles = fs.readdirSync(path.join(__dirname))
if (remainingFiles.length === 1 && remainingFiles[0] === '.DS_store') {
fs.unlinkSync(path.join(__dirname, '.DS_store'))
}
// Check if the scripts folder is empty
if (fs.readdirSync(path.join(__dirname)).length === 0) {
// Remove the scripts folder
fs.rmdirSync(path.join(__dirname))
}
}
// Adds the extension recommendation
fs.mkdirSync(path.join(projectRoot, ".vscode"), { recursive: true })
fs.writeFileSync(path.join(projectRoot, ".vscode", "extensions.json"), `{
"recommendations": ["svelte.svelte-vscode"]
}
`)
console.log("Converted to TypeScript.")
if (fs.existsSync(path.join(projectRoot, "node_modules"))) {
console.log("\nYou will need to re-run your dependency manager to get started.")
}

View file

@ -1,90 +0,0 @@
<!-- https://eugenkiss.github.io/7guis/tasks#crud -->
<script>
let people = [
{ first: 'Hans', last: 'Emil' },
{ first: 'Max', last: 'Mustermann' },
{ first: 'Roman', last: 'Tisch' }
];
let prefix = '';
let first = '';
let last = '';
let i = 0;
$: filteredPeople = prefix
? people.filter(person => {
const name = `${person.last}, ${person.first}`;
return name.toLowerCase().startsWith(prefix.toLowerCase());
})
: people;
$: selected = filteredPeople[i];
$: reset_inputs(selected);
function create() {
people = people.concat({ first, last });
i = people.length - 1;
first = last = '';
}
function update() {
selected.first = first;
selected.last = last;
people = people;
}
function remove() {
// Remove selected person from the source array (people), not the filtered array
const index = people.indexOf(selected);
people = [...people.slice(0, index), ...people.slice(index + 1)];
first = last = '';
i = Math.min(i, filteredPeople.length - 2);
}
function reset_inputs(person) {
first = person ? person.first : '';
last = person ? person.last : '';
}
</script>
<input placeholder="filter prefix" bind:value={prefix}>
<select bind:value={i} size={5}>
{#each filteredPeople as person, i}
<option value={i}>{person.last}, {person.first}</option>
{/each}
</select>
<label><input bind:value={first} placeholder="first"></label>
<label><input bind:value={last} placeholder="last"></label>
<div class='buttons'>
<button on:click={create} disabled="{!first || !last}">create</button>
<button on:click={update} disabled="{!first || !last || !selected}">update</button>
<button on:click={remove} disabled="{!selected}">delete</button>
</div>
<style>
* {
font-family: inherit;
font-size: inherit;
}
input {
display: block;
margin: 0 0 0.5em 0;
}
select {
float: left;
margin: 0 1em 1em 0;
width: 14em;
}
.buttons {
clear: both;
}
</style>

39
frontend/src/ambient.d.ts vendored Normal file
View file

@ -0,0 +1,39 @@
/**
* These declarations tell TypeScript that we allow import of images, e.g.
* ```
<script lang='ts'>
import successkid from 'images/successkid.jpg';
</script>
<img src="{successkid}">
```
*/
declare module "*.gif" {
const value: string;
export default value;
}
declare module "*.jpg" {
const value: string;
export default value;
}
declare module "*.jpeg" {
const value: string;
export default value;
}
declare module "*.png" {
const value: string;
export default value;
}
declare module "*.svg" {
const value: string;
export default value;
}
declare module "*.webp" {
const value: string;
export default value;
}

5
frontend/src/client.js Normal file
View file

@ -0,0 +1,5 @@
import * as sapper from '@sapper/app';
sapper.start({
target: document.querySelector('#sapper')
});

View file

@ -0,0 +1,42 @@
<script>
import './button.css';
import { createEventDispatcher } from 'svelte';
/**
* Is this the principal call to action on the page?
*/
export let primary = false;
/**
* What background color to use
*/
export let backgroundColor;
/**
* How large should the button be?
*/
export let size = 'medium';
/**
* Button contents
*/
export let label = '';
let mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
let style = backgroundColor ? `background-color: ${backgroundColor}` : '';
const dispatch = createEventDispatcher();
/**
* Optional click handler
*/
function onClick(event) {
dispatch('click', event);
}
</script>
<button
type="button"
class={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
{style}
on:click={onClick}>
{label}
</button>

View file

@ -0,0 +1,48 @@
<script>
import './header.css';
import Button from './Button.svelte';
import { createEventDispatcher } from 'svelte';
export let user = null;
const dispatch = createEventDispatcher();
function onLogin(event) {
dispatch('login', event);
}
function onLogout(event) {
dispatch('logout', event);
}
function onCreateAccount(event) {
dispatch('createAccount', event);
}
</script>
<header>
<div class="wrapper">
<div>
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path
d="M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z"
fill="#FFF" />
<path
d="M5.3 10.6l10.4 6v11.1l-10.4-6v-11zm11.4-6.2l9.7 5.5-9.7 5.6V4.4z"
fill="#555AB9" />
<path d="M27.2 10.6v11.2l-10.5 6V16.5l10.5-6zM15.7 4.4v11L6 10l9.7-5.5z" fill="#91BAF8" />
</g>
</svg>
<h1>Acme</h1>
</div>
<div>
{#if user}
<Button size="small" on:click={onLogout} label="Log out" />
{/if}
{#if !user}
<Button size="small" on:click={onLogin} label="Log in" />
<Button primary size="small" on:click={onCreateAccount} label="Sign up" />
{/if}
</div>
</div>
</header>

View file

@ -0,0 +1,60 @@
<script>
export let segment;
</script>
<style>
nav {
border-bottom: 1px solid rgba(255,62,0,0.1);
font-weight: 300;
padding: 0 1em;
}
ul {
margin: 0;
padding: 0;
}
/* clearfix */
ul::after {
content: '';
display: block;
clear: both;
}
li {
display: block;
float: left;
}
[aria-current] {
position: relative;
display: inline-block;
}
[aria-current]::after {
position: absolute;
content: '';
width: calc(100% - 1em);
height: 2px;
background-color: rgb(255,62,0);
display: block;
bottom: -1px;
}
a {
text-decoration: none;
padding: 1em 0.5em;
display: block;
}
</style>
<nav>
<ul>
<li><a aria-current="{segment === undefined ? 'page' : undefined}" href=".">home</a></li>
<li><a aria-current="{segment === 'about' ? 'page' : undefined}" href="about">about</a></li>
<!-- for the blog link, we're using rel=prefetch so that Sapper prefetches
the blog data when we hover over the link or tap it on a touchscreen -->
<li><a rel=prefetch aria-current="{segment === 'blog' ? 'page' : undefined}" href="blog">blog</a></li>
</ul>
</nav>

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.1" viewBox="0 0 48 48"><title>illustration/code-brackets</title><g id="illustration/code-brackets" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><path id="Combined-Shape" fill="#87E6E5" d="M11.4139325,12 C11.7605938,12 12,12.5059743 12,13.3779712 L12,17.4951758 L6.43502246,23.3839989 C5.85499251,23.9978337 5.85499251,25.0021663 6.43502246,25.6160011 L12,31.5048242 L12,35.6220288 C12,36.4939606 11.7605228,37 11.4139325,37 C11.2725831,37 11.1134406,36.9158987 10.9453839,36.7379973 L0.435022463,25.6160011 C-0.145007488,25.0021663 -0.145007488,23.9978337 0.435022463,23.3839989 L10.9453839,12.2620027 C11.1134051,12.0841663 11.2725831,12 11.4139325,12 Z M36.5860675,12 C36.7274169,12 36.8865594,12.0841013 37.0546161,12.2620027 L47.5649775,23.3839989 C48.1450075,23.9978337 48.1450075,25.0021663 47.5649775,25.6160011 L37.0546161,36.7379973 C36.8865949,36.9158337 36.7274169,37 36.5860675,37 C36.2394062,37 36,36.4940257 36,35.6220288 L36,31.5048242 L41.5649775,25.6160011 C42.1450075,25.0021663 42.1450075,23.9978337 41.5649775,23.3839989 L36,17.4951758 L36,13.3779712 C36,12.5060394 36.2394772,12 36.5860675,12 Z"/><rect id="Rectangle-7-Copy-5" width="35.57" height="4" x="5.009" y="22.662" fill="#A0DB77" rx="2" transform="translate(22.793959, 24.662305) rotate(-75.000000) translate(-22.793959, -24.662305)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.3 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.1" viewBox="0 0 48 48"><title>illustration/comments</title><g id="illustration/comments" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><path id="Path" fill="#96D07C" d="M2.52730803,17.9196415 C2.44329744,17.9745167 2.36370847,18.000488 2.29303375,18.000488 C2.1197031,18.000488 2,17.8443588 2,17.5752855 L2,4 C2,1.790861 3.790861,3.23296945e-13 6,3.23296945e-13 L33.9995117,3.23296945e-13 C36.2086507,3.23296945e-13 37.9995117,1.790861 37.9995117,4 L37.9995117,9.999512 C37.9995117,12.208651 36.2086507,13.999512 33.9995117,13.999512 L8,13.999512 C7.83499225,13.999512 7.6723181,13.9895206 7.51254954,13.9701099 L2.52730803,17.9196415 Z"/><path id="Path" fill="#73E1E0" d="M7.51066,44.9703679 L2.52730803,47.9186655 C2.44329744,47.9735407 2.36370847,47.999512 2.29303375,47.999512 C2.1197031,47.999512 2,47.8433828 2,47.5743095 L2,35 C2,32.790861 3.790861,31 6,31 L26,31 C28.209139,31 30,32.790861 30,35 L30,41 C30,43.209139 28.209139,45 26,45 L8,45 C7.8343417,45 7.67103544,44.9899297 7.51066,44.9703679 Z"/><path id="Path" fill="#FFD476" d="M46,19.5 L46,33.0747975 C46,33.3438708 45.8802969,33.5 45.7069663,33.5 C45.6362915,33.5 45.5567026,33.4740287 45.472692,33.4191535 L40.4887103,29.4704446 C40.3285371,29.489956 40.1654415,29.5 40,29.5 L18,29.5 C15.790861,29.5 14,27.709139 14,25.5 L14,19.5 C14,17.290861 15.790861,15.5 18,15.5 L42,15.5 C44.209139,15.5 46,17.290861 46,19.5 Z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.1" viewBox="0 0 48 48"><title>illustration/direction</title><g id="illustration/direction" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><path id="Combined-Shape" fill="#FFD476" d="M23.4917015,33.6030641 L2.93840258,31.4321033 C2.38917316,31.3740904 1.99096346,30.8818233 2.04897631,30.3325939 C2.0747515,30.0885705 2.18934861,29.8625419 2.37095722,29.6975265 L34.2609105,0.721285325 C34.6696614,0.349881049 35.3021022,0.38015648 35.6735064,0.788907393 C35.9232621,1.06377731 36.0001133,1.45442096 35.8730901,1.80341447 L24.5364357,32.9506164 C24.3793473,33.3822133 23.9484565,33.6513092 23.4917015,33.6030641 L23.4917015,33.6030641 Z"/><path id="Combined-Shape-Copy" fill="#FFC445" d="M24.3163597,33.2881029 C24.0306575,33.0138462 23.9337246,32.5968232 24.069176,32.2246735 L35.091923,1.9399251 C35.2266075,1.56988243 35.5659249,1.31333613 35.9586669,1.28460955 C36.5094802,1.24432106 36.9886628,1.65818318 37.0289513,2.20899647 L40.2437557,46.1609256 C40.2644355,46.4436546 40.1641446,46.7218752 39.9678293,46.9263833 C39.5853672,47.3248067 38.9523344,47.3377458 38.5539111,46.9552837 L24.3163597,33.2881029 L24.3163597,33.2881029 Z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.1" viewBox="0 0 48 48"><title>illustration/flow</title><g id="illustration/flow" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><path id="Combined-Shape" fill="#79C9FC" fill-rule="nonzero" d="M30,29 C32.7614237,29 35,26.7614237 35,24 C35,14.6111593 27.3888407,7 18,7 C8.61115925,7 1,14.6111593 1,24 C1,33.3888407 8.61115925,41 18,41 C19.3333404,41 20.6447683,40.8466238 21.9154603,40.5471706 C19.5096374,39.3319645 17.5510566,37.8612875 16.0456579,36.1314815 C14.1063138,33.9030427 12.769443,31.0725999 12.0293806,27.6556449 C11.360469,26.565281 11,25.3082308 11,24 C11,20.1340068 14.1340068,17 18,17 C21.8659932,17 25,20.1340068 25,24 C25,26.125 27.7040312,29 30,29 Z"/><path id="Combined-Shape-Copy" fill="#FFC445" fill-rule="nonzero" d="M42,29 C44.7614237,29 47,26.7614237 47,24 C47,14.6111593 39.3888407,7 30,7 C20.6111593,7 13,14.6111593 13,24 C13,33.3888407 20.6111593,41 30,41 C31.3333404,41 32.6447683,40.8466238 33.9154603,40.5471706 C31.5096374,39.3319645 29.4051056,37.9781963 28.0456579,36.1314815 C26.0625,33.4375 23,27.1875 23,24 C23,20.1340068 26.1340068,17 30,17 C33.8659932,17 37,20.1340068 37,24 C37.02301,26.3435241 39.7040312,29 42,29 Z" transform="translate(30.000000, 24.000000) scale(-1, -1) translate(-30.000000, -24.000000)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.1" viewBox="0 0 48 48"><title>illustration/plugin</title><g id="illustration/plugin" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><path id="Combined-Shape" fill="#79C9FC" d="M26,15.3994248 C26,15.4091303 26,15.4188459 26,15.4285714 L26,21.4694881 C25.8463595,21.4969567 25.6941676,21.51275 25.5873784,21.51275 C25.4974117,21.51275 25.4230979,21.4768034 25.377756,21.4206259 L25.2660784,21.2822603 L25.1317423,21.1657666 C24.2436317,20.3956144 23.100098,19.9633214 21.895551,19.9633214 C19.2039137,19.9633214 17,22.1075558 17,24.7804643 C17,27.4533728 19.2039137,29.5976071 21.895551,29.5976071 C23.1972122,29.5976071 24.3149423,29.2878193 25.1231445,28.3613697 C25.4542273,27.9818463 25.568273,27.9073214 25.5873784,27.9073214 C25.681532,27.9073214 25.8352452,27.9239643 26,27.9524591 L26,32.5714286 C26,32.5811541 26,32.5908697 26,32.6005752 L26,33 C26,35.209139 24.209139,37 22,37 L4,37 C1.790861,37 0,35.209139 0,33 L0,15 C0,12.790861 1.790861,11 4,11 L22,11 C24.209139,11 26,12.790861 26,15 L26,15.3994248 Z"/><path id="Path" fill="#87E6E5" d="M27.9998779,32.5714286 C27.9998779,33.3604068 28.6572726,34 29.4682101,34 L46.5315458,34 C47.3424832,34 47.9998779,33.3604068 47.9998779,32.5714286 L47.9998779,15.4285714 C47.9998779,14.6395932 47.3424832,14 46.5315458,14 L29.4682101,14 C28.6572726,14 27.9998779,14.6395932 27.9998779,15.4285714 L27.9998779,21.8355216 C27.9334367,22.2650514 27.8567585,22.6454496 27.746391,22.8084643 C27.4245309,23.2838571 26.2402709,23.51275 25.5873784,23.51275 C24.8705773,23.51275 24.2322714,23.1857725 23.8214379,22.6767605 C23.3096996,22.2329909 22.6349941,21.9633214 21.895551,21.9633214 C20.2963823,21.9633214 19,23.2245992 19,24.7804643 C19,26.3363293 20.2963823,27.5976071 21.895551,27.5976071 C22.5398535,27.5976071 23.2399343,27.477727 23.6160247,27.0466112 C24.1396029,26.4464286 24.7367044,25.9073214 25.5873784,25.9073214 C26.2402709,25.9073214 27.5912951,26.1766031 27.8226692,26.6116071 C27.8819199,26.7230038 27.9403239,26.921677 27.9998779,27.1556219 L27.9998779,32.5714286 Z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.1" viewBox="0 0 48 48"><title>illustration/repo</title><g id="illustration/repo" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><path id="Rectangle-62-Copy" fill="#B7F0EF" d="M27.2217723,9.04506931 L41.2217723,6.2682098 C43.3886973,5.83840648 45.4937616,7.2466219 45.9235649,9.41354696 C45.9743993,9.66983721 46,9.93049166 46,10.1917747 L46,32.581381 C46,34.4904961 44.650862,36.1335143 42.7782277,36.5049459 L28.7782277,39.2818054 C26.6113027,39.7116087 24.5062384,38.3033933 24.0764351,36.1364682 C24.0256007,35.880178 24,35.6195235 24,35.3582405 L24,12.9686342 C24,11.0595191 25.349138,9.4165009 27.2217723,9.04506931 Z" opacity=".7"/><path id="Combined-Shape" fill="#87E6E5" d="M6.77822775,6.2682098 L20.7782277,9.04506931 C22.650862,9.4165009 24,11.0595191 24,12.9686342 L24,35.3582405 C24,37.5673795 22.209139,39.3582405 20,39.3582405 C19.738717,39.3582405 19.4780625,39.3326398 19.2217723,39.2818054 L5.22177225,36.5049459 C3.34913798,36.1335143 2,34.4904961 2,32.581381 L2,10.1917747 C2,7.98263571 3.790861,6.19177471 6,6.19177471 C6.26128305,6.19177471 6.5219375,6.21737537 6.77822775,6.2682098 Z"/><path id="Rectangle-63-Copy-2" fill="#61C1FD" d="M22,10 C23.1666667,10.2291667 24.0179036,10.625 24.5537109,11.1875 C25.0895182,11.75 25.5716146,12.875 26,14.5625 C26,29.3020833 26,37.5208333 26,39.21875 C26,40.9166667 26.4241536,42.9583333 27.2724609,45.34375 L24.5537109,41.875 L22.9824219,45.34375 C22.327474,43.1979167 22,41.2291667 22,39.4375 C22,37.6458333 22,27.8333333 22,10 Z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48" version="1.1" viewBox="0 0 48 48"><title>illustration/stackalt</title><g id="illustration/stackalt" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><path id="Combined-Shape" fill="#FFAE00" d="M23.8628277,0 L23.8628277,48 L3.32291648,36.2491883 L3.32155653,11.9499781 L23.8628277,0 Z M23.8670509,0 L44.408322,11.9499781 L44.4069621,36.2491883 L23.8670509,48 L23.8670509,0 Z" opacity=".196"/><path id="Rectangle-46-Copy-3" fill="#66BF3C" d="M15.8232279,19.1155258 L24.7368455,21.4714881 C29.6053842,22.7582937 33.4077423,26.5606518 34.694548,31.4291905 L37.0505103,40.3428082 C37.6150232,42.4786032 36.3412474,44.6676353 34.2054524,45.2321482 C33.5569474,45.4035549 32.87575,45.4091235 32.2245294,45.2483418 L23.3459013,43.0562718 C18.2976962,41.809906 14.3561301,37.8683399 13.1097642,32.8201348 L10.9176943,23.9415066 C10.3881737,21.7967682 11.6975664,19.6288529 13.8423049,19.0993322 C14.4935255,18.9385505 15.1747229,18.9441191 15.8232279,19.1155258 Z" opacity=".5" transform="translate(23.999997, 32.166058) rotate(-45.000000) translate(-23.999997, -32.166058)"/><path id="Rectangle-46-Copy-2" fill="#FFAE00" d="M15.8232279,11.2216893 L24.7368455,13.5776516 C29.6053842,14.8644572 33.4077423,18.6668153 34.694548,23.5353541 L37.0505103,32.4489717 C37.6150232,34.5847667 36.3412474,36.7737988 34.2054524,37.3383117 C33.5569474,37.5097184 32.87575,37.515287 32.2245294,37.3545053 L23.3459013,35.1624353 C18.2976962,33.9160695 14.3561301,29.9745034 13.1097642,24.9262983 L10.9176943,16.0476701 C10.3881737,13.9029317 11.6975664,11.7350164 13.8423049,11.2054957 C14.4935255,11.044714 15.1747229,11.0502826 15.8232279,11.2216893 Z" opacity=".5" transform="translate(23.999997, 24.272222) rotate(-45.000000) translate(-23.999997, -24.272222)"/><path id="Rectangle-46-Copy" fill="#FC521F" d="M15.8232279,3.32785281 L24.7368455,5.68381509 C29.6053842,6.97062075 33.4077423,10.7729788 34.694548,15.6415176 L37.0505103,24.5551352 C37.6150232,26.6909302 36.3412474,28.8799623 34.2054524,29.4444752 C33.5569474,29.6158819 32.87575,29.6214505 32.2245294,29.4606688 L23.3459013,27.2685988 C18.2976962,26.022233 14.3561301,22.0806669 13.1097642,17.0324618 L10.9176943,8.15383364 C10.3881737,6.00909519 11.6975664,3.84117987 13.8423049,3.31165925 C14.4935255,3.15087753 15.1747229,3.15644615 15.8232279,3.32785281 Z" opacity=".5" transform="translate(23.999997, 16.378385) rotate(-45.000000) translate(-23.999997, -16.378385)"/></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,30 @@
.storybook-button {
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 700;
border: 0;
border-radius: 3em;
cursor: pointer;
display: inline-block;
line-height: 1;
}
.storybook-button--primary {
color: white;
background-color: #1ea7fd;
}
.storybook-button--secondary {
color: #333;
background-color: transparent;
box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
}
.storybook-button--small {
font-size: 12px;
padding: 10px 16px;
}
.storybook-button--medium {
font-size: 14px;
padding: 11px 20px;
}
.storybook-button--large {
font-size: 16px;
padding: 12px 24px;
}

View file

@ -0,0 +1,26 @@
.wrapper {
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
padding: 15px 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
svg {
display: inline-block;
vertical-align: top;
}
h1 {
font-weight: 900;
font-size: 20px;
line-height: 1;
margin: 6px 0 6px 10px;
display: inline-block;
vertical-align: top;
}
button + button {
margin-left: 10px;
}

View file

@ -1,7 +0,0 @@
import App from './App.svelte';
var app = new App({
target: document.body
});
export default app;

View file

@ -0,0 +1,40 @@
<script>
export let status;
export let error;
const dev = process.env.NODE_ENV === 'development';
</script>
<style>
h1, p {
margin: 0 auto;
}
h1 {
font-size: 2.8em;
font-weight: 700;
margin: 0 0 0.5em 0;
}
p {
margin: 1em auto;
}
@media (min-width: 480px) {
h1 {
font-size: 4em;
}
}
</style>
<svelte:head>
<title>{status}</title>
</svelte:head>
<h1>{status}</h1>
<p>{error.message}</p>
{#if dev && error.stack}
<pre>{error.stack}</pre>
{/if}

View file

@ -0,0 +1,24 @@
<script>
// import Nav from '../components/Nav.svelte';
// export let segment;
import Header from '../components/Header.svelte';
</script>
<style>
main {
position: relative;
max-width: 56em;
background-color: white;
padding: 2em;
margin: 0 auto;
box-sizing: border-box;
}
</style>
<!-- <Nav {segment}/> -->
<Header />
<main>
<slot></slot>
</main>

View file

@ -0,0 +1,7 @@
<svelte:head>
<title>About</title>
</svelte:head>
<h1>About this site</h1>
<p>This is the 'about' page. There's not much here.</p>

View file

@ -0,0 +1,28 @@
import posts from './_posts.js';
const lookup = new Map();
posts.forEach(post => {
lookup.set(post.slug, JSON.stringify(post));
});
export 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`
}));
}
}

View file

@ -0,0 +1,64 @@
<script context="module">
export async function preload({ params }) {
// the `slug` parameter is available because
// this file is called [slug].svelte
const res = await this.fetch(`blog/${params.slug}.json`);
const data = await res.json();
if (res.status === 200) {
return { post: data };
} else {
this.error(res.status, data.message);
}
}
</script>
<script>
export let post;
</script>
<style>
/*
By default, CSS is locally scoped to the component,
and any unused styles are dead-code-eliminated.
In this page, Svelte can't know which elements are
going to appear inside the {{{post.html}}} block,
so we have to use the :global(...) modifier to target
all elements inside .content
*/
.content :global(h2) {
font-size: 1.4em;
font-weight: 500;
}
.content :global(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 :global(pre) :global(code) {
background-color: transparent;
padding: 0;
}
.content :global(ul) {
line-height: 1.5;
}
.content :global(li) {
margin: 0 0 0.5em 0;
}
</style>
<svelte:head>
<title>{post.title}</title>
</svelte:head>
<h1>{post.title}</h1>
<div class="content">
{@html post.html}
</div>

View file

@ -0,0 +1,92 @@
// Ordinarily, you'd generate this data from markdown files in your
// repo, or fetch them from a database of some kind. But in order to
// avoid unnecessary dependencies in the starter template, and in the
// service of obviousness, we're just going to leave it here.
// This file is called `_posts.js` rather than `posts.js`, because
// we don't want to create an `/blog/posts` route — the leading
// underscore tells Sapper not to do that.
const posts = [
{
title: 'What is Sapper?',
slug: 'what-is-sapper',
html: `
<p>First, you have to know what <a href='https://svelte.dev'>Svelte</a> is. Svelte is a UI framework with a bold new idea: rather than providing a library that you write code with (like React or Vue, for example), it's a compiler that turns your components into highly optimized vanilla JavaScript. If you haven't already read the <a href='https://svelte.dev/blog/frameworks-without-the-framework'>introductory blog post</a>, you should!</p>
<p>Sapper is a Next.js-style framework (<a href='blog/how-is-sapper-different-from-next'>more on that here</a>) built around Svelte. It makes it embarrassingly easy to create extremely high performance web apps. Out of the box, you get:</p>
<ul>
<li>Code-splitting, dynamic imports and hot module replacement, powered by webpack</li>
<li>Server-side rendering (SSR) with client-side hydration</li>
<li>Service worker for offline support, and all the PWA bells and whistles</li>
<li>The nicest development experience you've ever had, or your money back</li>
</ul>
<p>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.</p>
`
},
{
title: 'How to use Sapper',
slug: 'how-to-use-sapper',
html: `
<h2>Step one</h2>
<p>Create a new project, using <a href='https://github.com/Rich-Harris/degit'>degit</a>:</p>
<pre><code>npx degit "sveltejs/sapper-template#rollup" my-app
cd my-app
npm install # or yarn!
npm run dev
</code></pre>
<h2>Step two</h2>
<p>Go to <a href='http://localhost:3000'>localhost:3000</a>. Open <code>my-app</code> in your editor. Edit the files in the <code>src/routes</code> directory or add new ones.</p>
<h2>Step three</h2>
<p>...</p>
<h2>Step four</h2>
<p>Resist overdone joke formats.</p>
`
},
{
title: 'Why the name?',
slug: 'why-the-name',
html: `
<p>In war, the soldiers who build bridges, repair roads, clear minefields and conduct demolitions all under combat conditions are known as <em>sappers</em>.</p>
<p>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 <strong>S</strong>velte <strong>app</strong> mak<strong>er</strong>, is your courageous and dutiful ally.</p>
`
},
{
title: 'How is Sapper different from Next.js?',
slug: 'how-is-sapper-different-from-next',
html: `
<p><a href='https://github.com/zeit/next.js'>Next.js</a> is a React framework from <a href='https://vercel.com/'>Vercel</a>, and is the inspiration for Sapper. There are a few notable differences, however:</p>
<ul>
<li>It's powered by <a href='https://svelte.dev'>Svelte</a> instead of React, so it's faster and your apps are smaller</li>
<li>Instead of route masking, we encode route parameters in filenames. For example, the page you're looking at right now is <code>src/routes/blog/[slug].svelte</code></li>
<li>As well as pages (Svelte components, which render on server or client), you can create <em>server routes</em> in your <code>routes</code> directory. These are just <code>.js</code> files that export functions corresponding to HTTP methods, and receive Express <code>request</code> and <code>response</code> objects as arguments. This makes it very easy to, for example, add a JSON API such as the one <a href='blog/how-is-sapper-different-from-next.json'>powering this very page</a></li>
<li>Links are just <code>&lt;a&gt;</code> elements, rather than framework-specific <code>&lt;Link&gt;</code> components. That means, for example, that <a href='blog/how-can-i-get-involved'>this link right here</a>, despite being inside a blob of HTML, works with the router as you'd expect.</li>
</ul>
`
},
{
title: 'How can I get involved?',
slug: 'how-can-i-get-involved',
html: `
<p>We're so glad you asked! Come on over to the <a href='https://github.com/sveltejs/svelte'>Svelte</a> and <a href='https://github.com/sveltejs/sapper'>Sapper</a> repos, and join us in the <a href='https://svelte.dev/chat'>Discord chatroom</a>. Everyone is welcome, especially you!</p>
`
}
];
posts.forEach(post => {
post.html = post.html.replace(/^\t{3}/gm, '');
});
export default posts;

View file

@ -0,0 +1,16 @@
import posts from './_posts.js';
const contents = JSON.stringify(posts.map(post => {
return {
title: post.title,
slug: post.slug
};
}));
export function get(req, res) {
res.writeHead(200, {
'Content-Type': 'application/json'
});
res.end(contents);
}

View file

@ -0,0 +1,34 @@
<script context="module">
export function preload() {
return this.fetch(`blog.json`).then(r => r.json()).then(posts => {
return { posts };
});
}
</script>
<script>
export let posts;
</script>
<style>
ul {
margin: 0 0 1em 0;
line-height: 1.5;
}
</style>
<svelte:head>
<title>Blog</title>
</svelte:head>
<h1>Recent posts</h1>
<ul>
{#each posts as post}
<!-- we're using the non-standard `rel=prefetch` attribute to
tell Sapper to load the data for the page as soon as
the user hovers over the link or taps it, instead of
waiting for the 'click' event -->
<li><a rel="prefetch" href="blog/{post.slug}">{post.title}</a></li>
{/each}
</ul>

View file

@ -0,0 +1,41 @@
<script>
// import successkid from 'images/successkid.jpg';
</script>
<style>
/* h1, figure, p {
text-align: center;
margin: 0 auto;
}
h1 {
font-size: 2.8em;
text-transform: uppercase;
font-weight: 700;
margin: 0 0 0.5em 0;
}
figure {
margin: 0 0 1em 0;
}
img {
width: 100%;
max-width: 400px;
margin: 0 0 1em 0;
}
p {
margin: 1em auto;
}
@media (min-width: 480px) {
h1 {
font-size: 4em;
}
} */
</style>
<svelte:head>
<title>Todo</title>
</svelte:head>

17
frontend/src/server.js Normal file
View file

@ -0,0 +1,17 @@
import sirv from 'sirv';
import polka from 'polka';
import compression from 'compression';
import * as sapper from '@sapper/server';
const { PORT, NODE_ENV } = process.env;
const dev = NODE_ENV === 'development';
polka() // You can also use Express
.use(
compression({ threshold: 0 }),
sirv('static', { dev }),
sapper.middleware()
)
.listen(PORT, err => {
if (err) console.log('error', err);
});

View file

@ -0,0 +1,86 @@
import { timestamp, files, shell } from '@sapper/service-worker';
const ASSETS = `cache${timestamp}`;
// `shell` is an array of all the files generated by the bundler,
// `files` is an array of everything in the `static` directory
const to_cache = shell.concat(files);
const staticAssets = new Set(to_cache);
self.addEventListener('install', event => {
event.waitUntil(
caches
.open(ASSETS)
.then(cache => cache.addAll(to_cache))
.then(() => {
self.skipWaiting();
})
);
});
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(async keys => {
// delete old caches
for (const key of keys) {
if (key !== ASSETS) await caches.delete(key);
}
self.clients.claim();
})
);
});
/**
* Fetch the asset from the network and store it in the cache.
* Fall back to the cache if the user is offline.
*/
async function fetchAndCache(request) {
const cache = await caches.open(`offline${timestamp}`)
try {
const response = await fetch(request);
cache.put(request, response.clone());
return response;
} catch (err) {
const response = await cache.match(request);
if (response) return response;
throw err;
}
}
self.addEventListener('fetch', event => {
if (event.request.method !== 'GET' || event.request.headers.has('range')) return;
const url = new URL(event.request.url);
// don't try to handle e.g. data: URIs
const isHttp = url.protocol.startsWith('http');
const isDevServerRequest = url.hostname === self.location.hostname && url.port !== self.location.port;
const isStaticAsset = url.host === self.location.host && staticAssets.has(url.pathname);
const skipBecauseUncached = event.request.cache === 'only-if-cached' && !isStaticAsset;
if (isHttp && !isDevServerRequest && !skipBecauseUncached) {
event.respondWith(
(async () => {
// always serve static files and bundler-generated assets from cache.
// if your application has other URLs with data that will never change,
// set this variable to true for them and they will only be fetched once.
const cachedAsset = isStaticAsset && await caches.match(event.request);
// for pages, you might want to serve a shell `service-worker-index.html` file,
// which Sapper has generated for you. It's not right for every
// app, but if it's right for yours then uncomment this section
/*
if (!cachedAsset && url.origin === self.origin && routes.find(route => route.pattern.test(url.pathname))) {
return caches.match('/service-worker-index.html');
}
*/
return cachedAsset || fetchAndCache(event.request);
})()
);
}
});

View file

@ -0,0 +1,34 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="theme-color" content="#333333">
%sapper.base%
<link rel="stylesheet" href="global.css">
<link rel="manifest" href="manifest.json" crossorigin="use-credentials">
<link rel="icon" type="image/png" href="favicon.png">
<link rel="apple-touch-icon" href="logo-192.png">
<!-- Sapper creates a <script> tag containing `src/client.js`
and anything else it needs to hydrate the app and
initialise the router -->
%sapper.scripts%
<!-- Sapper generates a <style> tag containing critical CSS
for the current page. CSS for the rest of the app is
lazily loaded when it precaches secondary pages -->
%sapper.styles%
<!-- This contains the contents of the <svelte:head> component, if
the current page has one -->
%sapper.head%
</head>
<body>
<!-- The application will be rendered inside this element,
because `src/client.js` references it -->
<div id="sapper">%sapper.html%</div>
</body>
</html>