mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-11 19:04:30 +00:00
move old docs over
This commit is contained in:
3
site/config.js
Normal file
3
site/config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export const SLUG_PRESERVE_UNICODE = false;
|
||||
export const SLUG_SEPARATOR = '_';
|
||||
export const SLUG_LANG = 'en';
|
||||
@@ -82,7 +82,7 @@ In addition to `child.component` and `child.props`, there is a `child.segment` p
|
||||
```
|
||||
|
||||
|
||||
### Preloading
|
||||
### Preloading in layouts
|
||||
|
||||
Like page components, layout components can use `preload`:
|
||||
|
||||
@@ -10,11 +10,11 @@ Until we reach version 1.0, there may be occasional changes to the project struc
|
||||
Consult [sapper-template](https://github.com/sveltejs/sapper-template) for full examples of all the below points.
|
||||
|
||||
|
||||
#### package.json
|
||||
##### package.json
|
||||
|
||||
To start a dev server, use `sapper dev` rather than `node server.js`. In all likelihood, your package.json will have an `npm run dev` script that will need to be updated.
|
||||
|
||||
#### Entry points
|
||||
##### Entry points
|
||||
|
||||
As of version 0.7, Sapper expects to find your entry points — for client, server and service worker — in an `app` folder. Instead of using magically-injected `__variables__`, each entry point imports from its corresponding file in the `app/manifests` folder. These are automatically generated by Sapper.
|
||||
|
||||
@@ -50,7 +50,7 @@ import { assets, shell, timestamp, routes } from './manifest/service-worker.js';
|
||||
```
|
||||
|
||||
|
||||
#### Templates and error pages
|
||||
##### Templates and error pages
|
||||
|
||||
In previous versions, we had `templates/2xx.html`, `templates/4xx.html` and `templates/5xx.html`. Now, we have a single template, `app/template.html`, which should look like your old `templates/2xx.html`.
|
||||
|
||||
@@ -61,7 +61,7 @@ This page is just like any other, except that it will get rendered whenever an e
|
||||
Note that you can now use `this.error(statusCode, error)` inside your `preload` functions.
|
||||
|
||||
|
||||
#### Webpack configs
|
||||
##### Webpack configs
|
||||
|
||||
Your webpack configs now live in a `webpack` directory:
|
||||
|
||||
@@ -73,12 +73,12 @@ If you have a service worker, you should also have a `webpack/service-worker.con
|
||||
|
||||
### <0.9 to 0.10
|
||||
|
||||
#### app/template.html
|
||||
##### app/template.html
|
||||
|
||||
* Your `<head>` element must contain `%sapper.base%` (see ([base URLs](guide#base-urls))
|
||||
* Remove references to your service worker; this is now handled by `%sapper.scripts%`
|
||||
|
||||
#### Pages
|
||||
##### Pages
|
||||
|
||||
* Your `preload` functions should now use `this.fetch` instead of `fetch`. `this.fetch` allows you to make credentialled requests on the server, and means that you no longer need to create a `global.fetch` object in `app/server.js`.
|
||||
|
||||
51
site/package-lock.json
generated
51
site/package-lock.json
generated
@@ -825,6 +825,11 @@
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@polka/send": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@polka/send/-/send-0.4.0.tgz",
|
||||
"integrity": "sha1-4nccVnHTYXWDJTSriGqIT3XzHm8="
|
||||
},
|
||||
"@polka/url": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-0.5.0.tgz",
|
||||
@@ -1194,6 +1199,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"clipboard": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz",
|
||||
"integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"good-listener": "^1.2.2",
|
||||
"select": "^1.1.2",
|
||||
"tiny-emitter": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"collection-visit": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
|
||||
@@ -1389,6 +1405,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"delegate": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
|
||||
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
|
||||
"optional": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.127",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.127.tgz",
|
||||
@@ -2193,6 +2215,15 @@
|
||||
"integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==",
|
||||
"dev": true
|
||||
},
|
||||
"good-listener": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
|
||||
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"delegate": "^3.1.2"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.1.15",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
|
||||
@@ -2969,6 +3000,14 @@
|
||||
"integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
|
||||
"dev": true
|
||||
},
|
||||
"prismjs": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.16.0.tgz",
|
||||
"integrity": "sha512-OA4MKxjFZHSvZcisLGe14THYsug/nF6O1f0pAJc0KN0wTyAcLqmsbE+lTGKSpyh+9pEW57+k6pg2AfYR+coyHA==",
|
||||
"requires": {
|
||||
"clipboard": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"private": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
|
||||
@@ -3267,6 +3306,12 @@
|
||||
"string-hash": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
|
||||
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
|
||||
"optional": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
|
||||
@@ -3628,6 +3673,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tiny-emitter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
|
||||
"optional": true
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
"test": "run-p --race dev cy:run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@polka/send": "^0.4.0",
|
||||
"compression": "^1.7.1",
|
||||
"highlight.js": "^9.15.6",
|
||||
"marked": "^0.6.2",
|
||||
"polka": "^0.5.0",
|
||||
"polka": "^0.5.2",
|
||||
"prismjs": "^1.16.0",
|
||||
"sirv": "^0.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
<script>
|
||||
export let segment;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
border-bottom: 1px solid rgba(170,30,30,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;
|
||||
}
|
||||
|
||||
.selected {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.selected::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
width: calc(100% - 1em);
|
||||
height: 2px;
|
||||
background-color: rgb(170,30,30);
|
||||
display: block;
|
||||
bottom: -1px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
padding: 1em 0.5em;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a class='{segment === undefined ? "selected" : ""}' href='.'>home</a></li>
|
||||
<li><a class='{segment === "about" ? "selected" : ""}' 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 class='{segment === "blog" ? "selected" : ""}' href='blog'>blog</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -1,40 +1,71 @@
|
||||
<script>
|
||||
const dev = process.env.NODE_ENV === 'development';
|
||||
|
||||
export let status;
|
||||
export let error;
|
||||
|
||||
const dev = process.env.NODE_ENV === 'development';
|
||||
// we don't want to use <svelte:window bind:online> here,
|
||||
// because we only care about the online state when
|
||||
// the page first loads
|
||||
let online = typeof navigator !== 'undefined'
|
||||
? navigator.onLine
|
||||
: true;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
h1, p {
|
||||
margin: 0 auto;
|
||||
.container {
|
||||
padding: var(--top-offset) var(--side-nav) 6rem var(--side-nav);
|
||||
}
|
||||
|
||||
h1, p { margin: 0 auto }
|
||||
|
||||
h1 {
|
||||
font-size: 2.8em;
|
||||
font-weight: 700;
|
||||
font-weight: 300;
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 1em auto;
|
||||
p { margin: 1em auto }
|
||||
|
||||
.error {
|
||||
background-color: #da106e;
|
||||
color: white;
|
||||
padding: 12px 16px;
|
||||
font: 600 16px/1.7 var(--font);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
@media (min-width: 480px) {
|
||||
h1 {
|
||||
font-size: 4em;
|
||||
}
|
||||
}
|
||||
/* @media (min-width: 480px) {
|
||||
h1 { font-size: 4em }
|
||||
} */
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
<title>{status}</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1>{status}</h1>
|
||||
<div class="container">
|
||||
{#if online}
|
||||
<h1>Yikes!</h1>
|
||||
|
||||
<p>{error.message}</p>
|
||||
{#if error.message}
|
||||
<p class="error">{status}: {error.message}</p>
|
||||
{:else}
|
||||
<p class="error">Encountered a {status} error</p>
|
||||
{/if}
|
||||
|
||||
{#if dev && error.stack}
|
||||
<pre>{error.stack}</pre>
|
||||
{/if}
|
||||
{#if dev && error.stack}
|
||||
<pre>{error.stack}</pre>
|
||||
{:else}
|
||||
{#if status >= 500}
|
||||
<p>Please try reloading the page.</p>
|
||||
{/if}
|
||||
|
||||
<p>If the error persists, please drop by <a href="https://discord.gg/yy75DKs">Discord chatroom</a> and let us know, or raise an issue on <a href="https://github.com/sveltejs/svelte">GitHub</a>. Thanks!</p>
|
||||
{/if}
|
||||
{:else}
|
||||
<h1>It looks like you're offline</h1>
|
||||
|
||||
<p>Reload the page once you've found the internet.</p>
|
||||
{/if}
|
||||
</div>
|
||||
136
site/src/routes/docs/_GuideContents.svelte
Normal file
136
site/src/routes/docs/_GuideContents.svelte
Normal file
@@ -0,0 +1,136 @@
|
||||
<script>
|
||||
import { onMount, afterUpdate } from 'svelte';
|
||||
import { Icon } from '@sveltejs/site-kit';
|
||||
|
||||
export let sections = [];
|
||||
export let active_section = null;
|
||||
export let show_contents;
|
||||
export let prevent_sidebar_scroll = false;
|
||||
|
||||
let ul;
|
||||
|
||||
afterUpdate(() => {
|
||||
// bit of a hack — prevent sidebar scrolling if
|
||||
// TOC is open on mobile, or scroll came from within sidebar
|
||||
if (prevent_sidebar_scroll || show_contents && window.innerWidth < 832) return;
|
||||
|
||||
const active = ul.querySelector('.active');
|
||||
|
||||
if (active) {
|
||||
const { top, bottom } = active.getBoundingClientRect();
|
||||
|
||||
const min = 200;
|
||||
const max = window.innerHeight - 200;
|
||||
|
||||
if (top > max) {
|
||||
ul.parentNode.scrollBy({
|
||||
top: top - max,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else if (bottom < min) {
|
||||
ul.parentNode.scrollBy({
|
||||
top: bottom - min,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.reference-toc li {
|
||||
display: block;
|
||||
line-height: 1.2;
|
||||
margin: 0 0 4rem 0;
|
||||
}
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
opacity: 0.75;
|
||||
transition: opacity 0.2s;
|
||||
border-bottom: none;
|
||||
padding: 0;
|
||||
color: var(--second);
|
||||
}
|
||||
|
||||
@media (min-width: 832px) {
|
||||
a {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
display: block;
|
||||
padding: 0 0 .8rem 0;
|
||||
font-size: var(--h6);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.subsection {
|
||||
display: block;
|
||||
font-size: 1.6rem;
|
||||
font-family: var(--font);
|
||||
padding: 0 0 0.6em 0;
|
||||
}
|
||||
|
||||
.section:hover,
|
||||
.subsection:hover {
|
||||
color: var(--flash);
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
.subsection[data-level="4"] {
|
||||
padding-left: 1.2rem;
|
||||
}
|
||||
|
||||
.active { opacity: 1 }
|
||||
|
||||
.icon-container {
|
||||
position: absolute;
|
||||
top: -.2rem;
|
||||
right: 2.4rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<ul
|
||||
bind:this={ul}
|
||||
class="reference-toc"
|
||||
on:mouseenter="{() => prevent_sidebar_scroll = true}"
|
||||
on:mouseleave="{() => prevent_sidebar_scroll = false}"
|
||||
>
|
||||
{#each sections as section}
|
||||
<li>
|
||||
<a class="section" class:active="{section.slug === active_section}" href="docs#{section.slug}">
|
||||
{section.metadata.title}
|
||||
|
||||
{#if section.slug === active_section}
|
||||
<div class="icon-container">
|
||||
<Icon name="arrow-right" />
|
||||
</div>
|
||||
{/if}
|
||||
</a>
|
||||
|
||||
{#each section.subsections as subsection}
|
||||
<!-- see <script> below: on:click='scrollTo(event, subsection.slug)' -->
|
||||
<a
|
||||
class="subsection"
|
||||
class:active="{subsection.slug === active_section}"
|
||||
href="docs#{subsection.slug}"
|
||||
data-level="{subsection.level}"
|
||||
>
|
||||
{subsection.title}
|
||||
|
||||
{#if subsection.slug === active_section}
|
||||
<div class="icon-container">
|
||||
<Icon name="arrow-right" />
|
||||
</div>
|
||||
{/if}
|
||||
</a>
|
||||
{/each}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
158
site/src/routes/docs/_sections.js
Normal file
158
site/src/routes/docs/_sections.js
Normal file
@@ -0,0 +1,158 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { SLUG_SEPARATOR, SLUG_PRESERVE_UNICODE } from '../../../config';
|
||||
import { extract_frontmatter, extract_metadata, langs, link_renderer } from '@sveltejs/site-kit/utils/markdown.js';
|
||||
import { make_session_slug_processor } from '@sveltejs/site-kit/utils/slug';
|
||||
import marked from 'marked';
|
||||
// import PrismJS from 'prismjs';
|
||||
// import 'prismjs/components/prism-bash';
|
||||
import hljs from 'highlight.js';
|
||||
|
||||
const escaped = {
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
};
|
||||
|
||||
const unescaped = Object.keys(escaped).reduce(
|
||||
(unescaped, key) => ((unescaped[escaped[key]] = key), unescaped),
|
||||
{}
|
||||
);
|
||||
|
||||
function unescape(str) {
|
||||
return String(str).replace(/&.+?;/g, match => unescaped[match] || match);
|
||||
}
|
||||
|
||||
const blockTypes = [
|
||||
'blockquote',
|
||||
'html',
|
||||
'heading',
|
||||
'hr',
|
||||
'list',
|
||||
'listitem',
|
||||
'paragraph',
|
||||
'table',
|
||||
'tablerow',
|
||||
'tablecell'
|
||||
];
|
||||
|
||||
export default function() {
|
||||
const make_slug = make_session_slug_processor({
|
||||
separator: SLUG_SEPARATOR,
|
||||
preserve_unicode: SLUG_PRESERVE_UNICODE
|
||||
});
|
||||
|
||||
return fs
|
||||
.readdirSync(`content/docs`)
|
||||
.filter(file => file[0] !== '.' && path.extname(file) === '.md')
|
||||
.map(file => {
|
||||
const markdown = fs.readFileSync(`content/docs/${file}`, 'utf-8');
|
||||
|
||||
const { content, metadata } = extract_frontmatter(markdown);
|
||||
|
||||
const section_slug = make_slug(metadata.title);
|
||||
|
||||
const subsections = [];
|
||||
|
||||
const renderer = new marked.Renderer();
|
||||
|
||||
let block_open = false;
|
||||
|
||||
renderer.link = link_renderer;
|
||||
|
||||
renderer.hr = () => {
|
||||
block_open = true;
|
||||
|
||||
return '<div class="side-by-side"><div class="copy">';
|
||||
};
|
||||
|
||||
renderer.code = (source, lang) => {
|
||||
source = source.replace(/^ +/gm, match =>
|
||||
match.split(' ').join('\t')
|
||||
);
|
||||
|
||||
const lines = source.split('\n');
|
||||
|
||||
const meta = extract_metadata(lines[0], lang);
|
||||
|
||||
let prefix = '';
|
||||
let class_name = 'code-block';
|
||||
|
||||
if (meta) {
|
||||
source = lines.slice(1).join('\n');
|
||||
const filename = meta.filename || (lang === 'html' && 'App.svelte');
|
||||
if (filename) {
|
||||
prefix = `<span class='filename'>${prefix} ${filename}</span>`;
|
||||
class_name += ' named';
|
||||
}
|
||||
}
|
||||
|
||||
if (meta && meta.hidden) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const plang = langs[lang];
|
||||
const { value: highlighted } = hljs.highlight(lang, source);
|
||||
// const highlighted = PrismJS.highlight(
|
||||
// source,
|
||||
// PrismJS.languages[plang],
|
||||
// lang
|
||||
// );
|
||||
|
||||
const html = `<div class='${class_name}'>${prefix}<pre class='language-${plang}'><code>${highlighted}</code></pre></div>`;
|
||||
|
||||
if (block_open) {
|
||||
block_open = false;
|
||||
return `</div><div class="code">${html}</div></div>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
renderer.heading = (text, level, rawtext) => {
|
||||
const slug = level <= 4 && make_slug(rawtext);
|
||||
|
||||
if (level === 3 || level === 4) {
|
||||
const title = unescape(
|
||||
text
|
||||
.replace(/<\/?code>/g, '')
|
||||
.replace(/\.(\w+)(\((.+)?\))?/, (m, $1, $2, $3) => {
|
||||
if ($3) return `.${$1}(...)`;
|
||||
if ($2) return `.${$1}()`;
|
||||
return `.${$1}`;
|
||||
})
|
||||
);
|
||||
|
||||
subsections.push({ slug, title, level });
|
||||
}
|
||||
|
||||
return `
|
||||
<h${level}>
|
||||
<span id="${slug}" class="offset-anchor" ${level > 4 ? 'data-scrollignore' : ''}></span>
|
||||
<a href="docs#${slug}" class="anchor" aria-hidden="true"></a>
|
||||
${text}
|
||||
</h${level}>`;
|
||||
};
|
||||
|
||||
blockTypes.forEach(type => {
|
||||
const fn = renderer[type];
|
||||
renderer[type] = function() {
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
const html = marked(content, { renderer });
|
||||
|
||||
const hashes = {};
|
||||
|
||||
return {
|
||||
html: html.replace(/@@(\d+)/g, (m, id) => hashes[id] || m),
|
||||
metadata,
|
||||
subsections,
|
||||
slug: section_slug,
|
||||
file,
|
||||
};
|
||||
});
|
||||
}
|
||||
14
site/src/routes/docs/index.json.js
Normal file
14
site/src/routes/docs/index.json.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import send from '@polka/send';
|
||||
import get_sections from './_sections.js';
|
||||
|
||||
let json;
|
||||
|
||||
export function get(req, res) {
|
||||
if (!json || process.env.NODE_ENV !== 'production') {
|
||||
json = JSON.stringify(get_sections()); // TODO it errors if I send the non-stringified value
|
||||
}
|
||||
|
||||
send(res, 200, json, {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
}
|
||||
397
site/src/routes/docs/index.svelte
Normal file
397
site/src/routes/docs/index.svelte
Normal file
@@ -0,0 +1,397 @@
|
||||
<script context="module">
|
||||
export async function preload() {
|
||||
const sections = await this.fetch(`docs.json`).then(r => r.json());
|
||||
return { sections };
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import GuideContents from './_GuideContents.svelte';
|
||||
import { Icon } from '@sveltejs/site-kit';
|
||||
import { getFragment } from '@sveltejs/site-kit/utils/navigation';
|
||||
|
||||
export let sections;
|
||||
let active_section;
|
||||
|
||||
let container;
|
||||
let aside;
|
||||
let show_contents = false;
|
||||
|
||||
onMount(() => {
|
||||
// don't update `active_section` for headings above level 4, see _sections.js
|
||||
const anchors = container.querySelectorAll('[id]:not([data-scrollignore])');
|
||||
|
||||
let positions;
|
||||
|
||||
const onresize = () => {
|
||||
const { top } = container.getBoundingClientRect();
|
||||
positions = [].map.call(anchors, anchor => {
|
||||
return anchor.getBoundingClientRect().top - top;
|
||||
});
|
||||
}
|
||||
|
||||
let last_id = getFragment();
|
||||
|
||||
const onscroll = () => {
|
||||
const top = -window.scrollY;
|
||||
|
||||
let i = anchors.length;
|
||||
while (i--) {
|
||||
if (positions[i] + top < 40) {
|
||||
const anchor = anchors[i];
|
||||
const { id } = anchor;
|
||||
|
||||
if (id !== last_id) {
|
||||
active_section = id;
|
||||
last_id = id;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', onscroll, true);
|
||||
window.addEventListener('resize', onresize, true);
|
||||
|
||||
// wait for fonts to load...
|
||||
const timeouts = [
|
||||
setTimeout(onresize, 1000),
|
||||
setTimeout(onscroll, 5000)
|
||||
];
|
||||
|
||||
onresize();
|
||||
onscroll();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', onscroll, true);
|
||||
window.removeEventListener('resize', onresize, true);
|
||||
|
||||
timeouts.forEach(timeout => clearTimeout(timeout));
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
aside {
|
||||
position: fixed;
|
||||
background-color: white;
|
||||
left: 0.8rem;
|
||||
bottom: 0.8rem;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
overflow: hidden;
|
||||
border: 1px solid #eee;
|
||||
box-shadow: 1px 1px 6px rgba(0,0,0,0.1);
|
||||
transition: width 0.2s, height 0.2s;
|
||||
}
|
||||
|
||||
aside button {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 3.4rem;
|
||||
height: 3.4rem;
|
||||
}
|
||||
|
||||
aside.open {
|
||||
width: calc(100vw - 3rem);
|
||||
height: calc(100vh - var(--nav-h));
|
||||
}
|
||||
|
||||
aside.open::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: calc(100% - 2rem);
|
||||
height: 2em;
|
||||
background: linear-gradient(to top, rgba(255,255,255,0) 0%, rgba(255,255,255,0.7) 50%, rgba(255,255,255,1) 100%);
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
aside::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 1.9em;
|
||||
width: calc(100% - 2rem);
|
||||
height: 2em;
|
||||
background: linear-gradient(to bottom, rgba(255,255,255,0) 0%, rgba(255,255,255,0.7) 50%, rgba(255,255,255,1) 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: absolute;
|
||||
font-family: var(--font);
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 4em 1.6rem 2em 3.2rem;
|
||||
bottom: 2em;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: var(--top-offset) var(--side-nav);
|
||||
tab-size: 2;
|
||||
-moz-tab-size: 2;
|
||||
}
|
||||
|
||||
@media (min-width: 832px) { /* can't use vars in @media :( */
|
||||
aside {
|
||||
display: block;
|
||||
width: var(--sidebar-w);
|
||||
height: 100vh;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
background-color: var(--second);
|
||||
color: white;
|
||||
}
|
||||
|
||||
aside.open::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
aside::after {
|
||||
content: '';
|
||||
bottom: 0;
|
||||
height: var(--top-offset);
|
||||
background: linear-gradient(to bottom, rgba(103,103,120,0) 0%, rgba(103,103,120,0.7) 50%, rgba(103,103,120,1) 100%);
|
||||
}
|
||||
|
||||
aside button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding: var(--top-offset) 0 6.4rem 3.2rem;
|
||||
font-family: var(--font);
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
bottom: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-left: calc(var(--sidebar-w) + var(--side-nav));
|
||||
}
|
||||
|
||||
.content :global(.side-by-side) {
|
||||
display: grid;
|
||||
grid-template-columns: calc(50% - 0.5em) calc(50% - 0.5em);
|
||||
grid-gap: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.content h2 {
|
||||
margin-top: 8rem;
|
||||
padding: 2rem 1.6rem 4rem 0.2rem;
|
||||
border-top: var(--border-w) solid #6767785b; /* based on --second */
|
||||
color: var(--text);
|
||||
line-height: 1;
|
||||
font-size: var(--h3);
|
||||
letter-spacing: .05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.content section:first-of-type > h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.content :global(h4) {
|
||||
margin: 2em 0 1em 0;
|
||||
}
|
||||
|
||||
.content :global(.offset-anchor) {
|
||||
position: relative;
|
||||
display: block;
|
||||
top: calc(-1 * (var(--nav-h) + var(--top-offset) - 1rem));
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.content :global(.anchor) {
|
||||
position: absolute;
|
||||
display: block;
|
||||
background: url(/icons/link.svg) 0 50% no-repeat;
|
||||
background-size: 1em 1em;
|
||||
width: 1.4em;
|
||||
height: 1em;
|
||||
left: -1.3em;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
border: none !important; /* TODO get rid of linkify */
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.content :global(h2):hover :global(.anchor),
|
||||
.content :global(h3):hover :global(.anchor),
|
||||
.content :global(h4):hover :global(.anchor),
|
||||
.content :global(h5):hover :global(.anchor),
|
||||
.content :global(h6):hover :global(.anchor) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.content :global(h5) :global(.anchor),
|
||||
.content :global(h6) :global(.anchor) {
|
||||
top: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.content :global(h3),
|
||||
.content :global(h3 > code) {
|
||||
margin: 6.4rem 0 0 0;
|
||||
padding: 2rem 1.6rem 5.6rem .2rem;
|
||||
color: var(--text);
|
||||
border-top: var(--border-w) solid #6767781f; /* based on --second */
|
||||
background: transparent;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.content :global(h3):first-of-type {
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* avoid doubled border-top */
|
||||
.content :global(h3 > code) {
|
||||
border-radius: 0 0 0 0;
|
||||
border: none;
|
||||
font-size: var(--h4);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
|
||||
.content :global(h4),
|
||||
.content :global(h4 > code) {
|
||||
font-weight: 600;
|
||||
font-size: var(--h4);
|
||||
color: var(--second);
|
||||
margin: 6.4rem 0 1.6rem 0;
|
||||
padding-left: 0;
|
||||
background: transparent;
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.content :global(h5) {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.content :global(code) {
|
||||
padding: .3rem .8rem .3rem;
|
||||
margin: 0 0.2rem;
|
||||
top: -.1rem;
|
||||
background: var(--back-api);
|
||||
}
|
||||
|
||||
.content :global(pre) :global(code) {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
top: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.content :global(pre) {
|
||||
margin: 0 0 2em 0;
|
||||
}
|
||||
|
||||
.content :global(.icon) {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
stroke: currentColor;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.content :global(table) {
|
||||
margin: 0 0 2em 0;
|
||||
}
|
||||
|
||||
section > :global(.code-block) > :global(pre) {
|
||||
display: inline-block;
|
||||
background: var(--back-api);
|
||||
color: white;
|
||||
padding: .3rem .8rem;
|
||||
margin: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
section > :global(.code-block)> :global(pre.language-markup) {
|
||||
padding: .3rem .8rem .2rem;
|
||||
background: var(--back-api);
|
||||
}
|
||||
|
||||
section > :global(p) {
|
||||
max-width: var(--linemax)
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: var(--h5);
|
||||
float: right;
|
||||
pointer-events: all;
|
||||
color: var(--prime);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* no linkify on these */
|
||||
small a { all: unset }
|
||||
small a:before { all: unset }
|
||||
|
||||
section :global(blockquote) {
|
||||
color: hsl(204, 100%, 50%);
|
||||
border: 2px solid var(--flash);
|
||||
}
|
||||
|
||||
section :global(blockquote) :global(code) {
|
||||
background: hsl(204, 100%, 95%) !important;
|
||||
color: hsl(204, 100%, 50%);
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
<title>API Docs • Svelte</title>
|
||||
|
||||
<meta name="twitter:title" content="Svelte API docs">
|
||||
<meta name="twitter:description" content="Cybernetically enhanced web apps">
|
||||
<meta name="Description" content="Cybernetically enhanced web apps">
|
||||
</svelte:head>
|
||||
|
||||
<div bind:this={container} class='content listify'>
|
||||
{#each sections as section}
|
||||
<section data-id={section.slug}>
|
||||
<h2>
|
||||
<span class="offset-anchor" id={section.slug}></span>
|
||||
<a href="docs#{section.slug}" class="anchor" aria-hidden></a>
|
||||
|
||||
{section.metadata.title}
|
||||
<small>
|
||||
<a href='https://github.com/sveltejs/svelte/edit/master/site/content/docs/{section.file}' title='edit this section'>
|
||||
<Icon name='edit' /></a>
|
||||
</small>
|
||||
</h2>
|
||||
|
||||
{@html section.html}
|
||||
</section>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<aside bind:this={aside} class="sidebar-container" class:open={show_contents}>
|
||||
<div class="sidebar" on:click="{() => show_contents = false}"> <!-- scroll container -->
|
||||
<GuideContents {sections} {active_section} {show_contents} />
|
||||
</div>
|
||||
|
||||
<button on:click="{() => show_contents = !show_contents}">
|
||||
<Icon name="{show_contents? 'close' : 'menu'}"/>
|
||||
</button>
|
||||
</aside>
|
||||
@@ -1,15 +0,0 @@
|
||||
export default function process_markdown(markdown) {
|
||||
const match = /---\n([\s\S]+?)\n---/.exec(markdown);
|
||||
const frontMatter = match[1];
|
||||
const content = markdown.slice(match[0].length);
|
||||
|
||||
const metadata = {};
|
||||
frontMatter.split('\n').forEach(pair => {
|
||||
const colonIndex = pair.indexOf(':');
|
||||
metadata[pair.slice(0, colonIndex).trim()] = pair
|
||||
.slice(colonIndex + 1)
|
||||
.trim();
|
||||
});
|
||||
|
||||
return { metadata, content };
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import process_markdown from './_process_markdown.js';
|
||||
import marked from 'marked';
|
||||
import hljs from 'highlight.js';
|
||||
|
||||
const langs = {
|
||||
'hidden-data': 'json',
|
||||
'html-no-repl': 'html'
|
||||
};
|
||||
|
||||
function btoa(str) {
|
||||
return new Buffer(str).toString('base64');
|
||||
}
|
||||
|
||||
const escaped = {
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>'
|
||||
};
|
||||
|
||||
const unescaped = Object.keys(escaped).reduce(
|
||||
(unescaped, key) => ((unescaped[escaped[key]] = key), unescaped),
|
||||
{}
|
||||
);
|
||||
|
||||
function unescape(str) {
|
||||
return String(str).replace(/&.+?;/g, match => unescaped[match] || match);
|
||||
}
|
||||
|
||||
export default () => fs
|
||||
.readdirSync(`content/guide`)
|
||||
.filter(file => file[0] !== '.' && path.extname(file) === '.md')
|
||||
.map(file => {
|
||||
const markdown = fs.readFileSync(`content/guide/${file}`, 'utf-8');
|
||||
|
||||
const { content, metadata } = process_markdown(markdown);
|
||||
|
||||
// syntax highlighting
|
||||
let uid = 0;
|
||||
const highlighted = {};
|
||||
|
||||
const tweaked_content = content.replace(
|
||||
/```([\w-]+)?\n([\s\S]+?)```/g,
|
||||
(match, lang, code) => {
|
||||
const { value } = hljs.highlight(lang, code);
|
||||
highlighted[++uid] = value;
|
||||
|
||||
return `@@${uid}`;
|
||||
}
|
||||
);
|
||||
|
||||
const html = marked(tweaked_content)
|
||||
.replace(/<p>@@(\d+)<\/p>/g, (match, id) => {
|
||||
return `<pre><code>${highlighted[id]}</code></pre>`;
|
||||
})
|
||||
.replace(/^\t+/gm, match => match.split('\t').join(' '));
|
||||
|
||||
const subsections = [];
|
||||
const pattern = /<h3 id="(.+?)">(.+?)<\/h3>/g;
|
||||
let match;
|
||||
|
||||
while ((match = pattern.exec(html))) {
|
||||
const slug = match[1];
|
||||
// const title = unescape(
|
||||
// match[2].replace(/<\/?code>/g, '').replace(/\.(\w+)\W.*/, '.$1')
|
||||
// );
|
||||
const title = unescape(match[2]);
|
||||
|
||||
subsections.push({ slug, title });
|
||||
}
|
||||
|
||||
return {
|
||||
html,
|
||||
metadata,
|
||||
subsections,
|
||||
slug: file.replace(/^\d+-/, '').replace(/\.md$/, ''),
|
||||
file
|
||||
};
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
import sections from './_sections.js';
|
||||
|
||||
const dev = process.env.NODE_ENV === 'development';
|
||||
let json;
|
||||
|
||||
export function get(req, res) {
|
||||
if (dev || !json) {
|
||||
json = JSON.stringify(sections());
|
||||
}
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': `max-age=${30 * 60 * 1e3}` // 30 minutes
|
||||
});
|
||||
res.end(json);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
TODO
|
||||
Reference in New Issue
Block a user