mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-12 03:05:12 +00:00
tidy up
This commit is contained in:
12
runtime/internal/Sapper.html
Normal file
12
runtime/internal/Sapper.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<script>
|
||||
import { setContext } from 'svelte';
|
||||
import { CONTEXT_KEY } from './shared';
|
||||
|
||||
export let Root;
|
||||
export let props;
|
||||
export let session;
|
||||
|
||||
setContext(CONTEXT_KEY, session);
|
||||
</script>
|
||||
|
||||
<Root {...props}/>
|
||||
1
runtime/internal/layout.html
Normal file
1
runtime/internal/layout.html
Normal file
@@ -0,0 +1 @@
|
||||
<svelte:component this={child.component} {...child.props}/>
|
||||
10
runtime/internal/shared.mjs
Normal file
10
runtime/internal/shared.mjs
Normal file
@@ -0,0 +1,10 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const stores = {
|
||||
preloading: writable(false),
|
||||
page: writable(null)
|
||||
};
|
||||
|
||||
export const CONTEXT_KEY = {};
|
||||
|
||||
export const preload = () => ({});
|
||||
337
runtime/src/app/app.ts
Normal file
337
runtime/src/app/app.ts
Normal file
@@ -0,0 +1,337 @@
|
||||
import { writable } from 'svelte/store.mjs';
|
||||
import Sapper from '@sapper/internal/Sapper.html';
|
||||
import { stores } from '@sapper/internal/shared';
|
||||
import { Root, root_preload, ErrorComponent, ignore, components, routes } from '@sapper/internal/manifest-client';
|
||||
import {
|
||||
Target,
|
||||
ScrollPosition,
|
||||
Component,
|
||||
Redirect,
|
||||
ComponentLoader,
|
||||
ComponentConstructor,
|
||||
Route,
|
||||
Page
|
||||
} from './types';
|
||||
import goto from './goto';
|
||||
|
||||
declare const __SAPPER__;
|
||||
export const initial_data = typeof __SAPPER__ !== 'undefined' && __SAPPER__;
|
||||
|
||||
let ready = false;
|
||||
let root_component: Component;
|
||||
let current_token: {};
|
||||
let root_preloaded: Promise<any>;
|
||||
let current_branch = [];
|
||||
|
||||
const session = writable(initial_data && initial_data.session);
|
||||
|
||||
let $session;
|
||||
let session_dirty: boolean;
|
||||
|
||||
session.subscribe(async value => {
|
||||
$session = value;
|
||||
|
||||
if (!ready) return;
|
||||
session_dirty = true;
|
||||
|
||||
const target = select_target(new URL(location.href));
|
||||
|
||||
const token = current_token = {};
|
||||
const { redirect, props, branch } = await hydrate_target(target);
|
||||
if (token !== current_token) return; // a secondary navigation happened while we were loading
|
||||
|
||||
await render(redirect, branch, props, target.page);
|
||||
});
|
||||
|
||||
export let prefetching: {
|
||||
href: string;
|
||||
promise: Promise<{ redirect?: Redirect, data?: any }>;
|
||||
} = null;
|
||||
export function set_prefetching(href, promise) {
|
||||
prefetching = { href, promise };
|
||||
}
|
||||
|
||||
export let store;
|
||||
export function set_store(fn) {
|
||||
store = fn(initial_data.store);
|
||||
}
|
||||
|
||||
export let target: Node;
|
||||
export function set_target(element) {
|
||||
target = element;
|
||||
}
|
||||
|
||||
export let uid = 1;
|
||||
export function set_uid(n) {
|
||||
uid = n;
|
||||
}
|
||||
|
||||
export let cid: number;
|
||||
export function set_cid(n) {
|
||||
cid = n;
|
||||
}
|
||||
|
||||
const _history = typeof history !== 'undefined' ? history : {
|
||||
pushState: (state: any, title: string, href: string) => {},
|
||||
replaceState: (state: any, title: string, href: string) => {},
|
||||
scrollRestoration: ''
|
||||
};
|
||||
export { _history as history };
|
||||
|
||||
export const scroll_history: Record<string, ScrollPosition> = {};
|
||||
|
||||
export function select_target(url: URL): Target {
|
||||
if (url.origin !== location.origin) return null;
|
||||
if (!url.pathname.startsWith(initial_data.baseUrl)) return null;
|
||||
|
||||
const path = url.pathname.slice(initial_data.baseUrl.length);
|
||||
|
||||
// avoid accidental clashes between server routes and page routes
|
||||
if (ignore.some(pattern => pattern.test(path))) return;
|
||||
|
||||
for (let i = 0; i < routes.length; i += 1) {
|
||||
const route = routes[i];
|
||||
|
||||
const match = route.pattern.exec(path);
|
||||
if (match) {
|
||||
const query: Record<string, string | string[]> = Object.create(null);
|
||||
if (url.search.length > 0) {
|
||||
url.search.slice(1).split('&').forEach(searchParam => {
|
||||
let [, key, value] = /([^=]*)(?:=(.*))?/.exec(decodeURIComponent(searchParam));
|
||||
value = (value || '').replace(/\+/g, ' ');
|
||||
if (typeof query[key] === 'string') query[key] = [<string>query[key]];
|
||||
if (typeof query[key] === 'object') (query[key] as string[]).push(value);
|
||||
else query[key] = value;
|
||||
});
|
||||
}
|
||||
|
||||
const part = route.parts[route.parts.length - 1];
|
||||
const params = part.params ? part.params(match) : {};
|
||||
|
||||
const page = { path, query, params };
|
||||
|
||||
return { href: url.href, route, match, page };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function scroll_state() {
|
||||
return {
|
||||
x: pageXOffset,
|
||||
y: pageYOffset
|
||||
};
|
||||
}
|
||||
|
||||
export async function navigate(target: Target, id: number, noscroll?: boolean, hash?: string): Promise<any> {
|
||||
if (id) {
|
||||
// popstate or initial navigation
|
||||
cid = id;
|
||||
} else {
|
||||
const current_scroll = scroll_state();
|
||||
|
||||
// clicked on a link. preserve scroll state
|
||||
scroll_history[cid] = current_scroll;
|
||||
|
||||
id = cid = ++uid;
|
||||
scroll_history[cid] = noscroll ? current_scroll : { x: 0, y: 0 };
|
||||
}
|
||||
|
||||
cid = id;
|
||||
|
||||
if (root_component) stores.preloading.set(true);
|
||||
|
||||
const loaded = prefetching && prefetching.href === target.href ?
|
||||
prefetching.promise :
|
||||
hydrate_target(target);
|
||||
|
||||
prefetching = null;
|
||||
|
||||
const token = current_token = {};
|
||||
const { redirect, props, branch } = await loaded;
|
||||
if (token !== current_token) return; // a secondary navigation happened while we were loading
|
||||
|
||||
await render(redirect, branch, props, target.page);
|
||||
if (document.activeElement) document.activeElement.blur();
|
||||
|
||||
if (!noscroll) {
|
||||
let scroll = scroll_history[id];
|
||||
|
||||
if (hash) {
|
||||
// scroll is an element id (from a hash), we need to compute y.
|
||||
const deep_linked = document.querySelector(hash);
|
||||
|
||||
if (deep_linked) {
|
||||
scroll = {
|
||||
x: 0,
|
||||
y: deep_linked.getBoundingClientRect().top
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
scroll_history[cid] = scroll;
|
||||
if (scroll) scrollTo(scroll.x, scroll.y);
|
||||
}
|
||||
}
|
||||
|
||||
async function render(redirect: Redirect, branch: any[], props: any, page: Page) {
|
||||
if (redirect) return goto(redirect.location, { replaceState: true });
|
||||
|
||||
stores.page.set(page);
|
||||
stores.preloading.set(false);
|
||||
|
||||
if (root_component) {
|
||||
root_component.props = props;
|
||||
} else {
|
||||
// first load — remove SSR'd <head> contents
|
||||
const start = document.querySelector('#sapper-head-start');
|
||||
const end = document.querySelector('#sapper-head-end');
|
||||
|
||||
if (start && end) {
|
||||
while (start.nextSibling !== end) detach(start.nextSibling);
|
||||
detach(start);
|
||||
detach(end);
|
||||
}
|
||||
|
||||
root_component = new Sapper({
|
||||
target,
|
||||
props: {
|
||||
Root,
|
||||
props,
|
||||
session
|
||||
},
|
||||
hydrate: true
|
||||
});
|
||||
}
|
||||
|
||||
current_branch = branch;
|
||||
ready = true;
|
||||
session_dirty = false;
|
||||
}
|
||||
|
||||
export async function hydrate_target(target: Target): Promise<{
|
||||
redirect?: Redirect;
|
||||
props?: any;
|
||||
branch?: Array<{ Component: ComponentConstructor, preload: (page) => Promise<any>, segment: string }>
|
||||
}> {
|
||||
const { route, page } = target;
|
||||
const segments = page.path.split('/').filter(Boolean);
|
||||
|
||||
let redirect: Redirect = null;
|
||||
let error: { statusCode: number, message: Error | string } = null;
|
||||
|
||||
const preload_context = {
|
||||
fetch: (url: string, opts?: any) => fetch(url, opts),
|
||||
redirect: (statusCode: number, location: string) => {
|
||||
if (redirect && (redirect.statusCode !== statusCode || redirect.location !== location)) {
|
||||
throw new Error(`Conflicting redirects`);
|
||||
}
|
||||
redirect = { statusCode, location };
|
||||
},
|
||||
error: (statusCode: number, message: Error | string) => {
|
||||
error = { statusCode, message };
|
||||
}
|
||||
};
|
||||
|
||||
if (!root_preloaded) {
|
||||
root_preloaded = initial_data.preloaded[0] || root_preload.call(preload_context, {
|
||||
path: page.path,
|
||||
query: page.query,
|
||||
params: {}
|
||||
}, $session);
|
||||
}
|
||||
|
||||
let branch;
|
||||
|
||||
try {
|
||||
branch = await Promise.all(route.parts.map(async (part, i) => {
|
||||
if (!part) return null;
|
||||
|
||||
const segment = segments[i];
|
||||
if (!session_dirty && current_branch[i] && current_branch[i].segment === segment) return current_branch[i];
|
||||
|
||||
const { default: Component, preload } = await load_component(components[part.i]);
|
||||
|
||||
let preloaded;
|
||||
if (ready || !initial_data.preloaded[i + 1]) {
|
||||
preloaded = preload
|
||||
? await preload.call(preload_context, {
|
||||
path: page.path,
|
||||
query: page.query,
|
||||
params: part.params ? part.params(target.match) : {}
|
||||
}, $session)
|
||||
: {};
|
||||
} else {
|
||||
preloaded = initial_data.preloaded[i + 1];
|
||||
}
|
||||
|
||||
return { Component, preloaded, segment };
|
||||
}));
|
||||
} catch (e) {
|
||||
error = { statusCode: 500, message: e };
|
||||
branch = [];
|
||||
}
|
||||
|
||||
if (redirect) return { redirect };
|
||||
|
||||
if (error) {
|
||||
// TODO be nice if this was less of a special case
|
||||
return {
|
||||
props: {
|
||||
child: {
|
||||
component: ErrorComponent,
|
||||
props: {
|
||||
error: typeof error.message === 'string' ? new Error(error.message) : error.message,
|
||||
status: error.statusCode
|
||||
}
|
||||
}
|
||||
},
|
||||
branch
|
||||
};
|
||||
}
|
||||
|
||||
const props = Object.assign({}, await root_preloaded, { child: {} });
|
||||
let level = props.child;
|
||||
|
||||
branch.forEach(node => {
|
||||
if (!node) return;
|
||||
|
||||
level.segment = node.segment;
|
||||
level.component = node.Component;
|
||||
level.props = Object.assign({}, node.preloaded, { child: {} });
|
||||
|
||||
level = level.props.child;
|
||||
});
|
||||
|
||||
return { props, branch };
|
||||
}
|
||||
|
||||
function load_css(chunk: string) {
|
||||
const href = `client/${chunk}`;
|
||||
if (document.querySelector(`link[href="${href}"]`)) return;
|
||||
|
||||
return new Promise((fulfil, reject) => {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = href;
|
||||
|
||||
link.onload = () => fulfil();
|
||||
link.onerror = reject;
|
||||
|
||||
document.head.appendChild(link);
|
||||
});
|
||||
}
|
||||
|
||||
export function load_component(component: ComponentLoader): Promise<{
|
||||
default: ComponentConstructor,
|
||||
preload?: (input: any) => any
|
||||
}> {
|
||||
// TODO this is temporary — once placeholders are
|
||||
// always rewritten, scratch the ternary
|
||||
const promises: Array<Promise<any>> = (typeof component.css === 'string' ? [] : component.css.map(load_css));
|
||||
promises.unshift(component.js());
|
||||
return Promise.all(promises).then(values => values[0]);
|
||||
}
|
||||
|
||||
function detach(node: Node) {
|
||||
node.parentNode.removeChild(node);
|
||||
}
|
||||
13
runtime/src/app/goto/index.ts
Normal file
13
runtime/src/app/goto/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { history, select_target, navigate, cid } from '../app';
|
||||
|
||||
export default function goto(href: string, opts = { replaceState: false }) {
|
||||
const target = select_target(new URL(href, document.baseURI));
|
||||
|
||||
if (target) {
|
||||
history[opts.replaceState ? 'replaceState' : 'pushState']({ id: cid }, '', href);
|
||||
return navigate(target, null).then(() => {});
|
||||
}
|
||||
|
||||
location.href = href;
|
||||
return new Promise(f => {}); // never resolves
|
||||
}
|
||||
12
runtime/src/app/index.ts
Normal file
12
runtime/src/app/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { getContext } from 'svelte';
|
||||
import { CONTEXT_KEY, stores } from '@sapper/internal/shared';
|
||||
|
||||
export const preloading = { subscribe: stores.preloading.subscribe };
|
||||
export const page = { subscribe: stores.page.subscribe };
|
||||
|
||||
export const getSession = () => getContext(CONTEXT_KEY);
|
||||
|
||||
export { default as start } from './start/index';
|
||||
export { default as goto } from './goto/index';
|
||||
export { default as prefetch } from './prefetch/index';
|
||||
export { default as prefetchRoutes } from './prefetchRoutes/index';
|
||||
14
runtime/src/app/prefetch/index.ts
Normal file
14
runtime/src/app/prefetch/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { select_target, prefetching, set_prefetching, hydrate_target } from '../app';
|
||||
import { Target } from '../types';
|
||||
|
||||
export default function prefetch(href: string) {
|
||||
const target: Target = select_target(new URL(href, document.baseURI));
|
||||
|
||||
if (target) {
|
||||
if (!prefetching || href !== prefetching.href) {
|
||||
set_prefetching(href, hydrate_target(target));
|
||||
}
|
||||
|
||||
return prefetching.promise;
|
||||
}
|
||||
}
|
||||
13
runtime/src/app/prefetchRoutes/index.ts
Normal file
13
runtime/src/app/prefetchRoutes/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { components, routes } from '@sapper/internal/manifest-client';
|
||||
import { load_component } from '../app';
|
||||
|
||||
export default function prefetchRoutes(pathnames: string[]) {
|
||||
return routes
|
||||
.filter(pathnames
|
||||
? route => pathnames.some(pathname => route.pattern.test(pathname))
|
||||
: () => true
|
||||
)
|
||||
.reduce((promise: Promise<any>, route) => promise.then(() => {
|
||||
return Promise.all(route.parts.map(part => part && load_component(components[part.i])));
|
||||
}), Promise.resolve());
|
||||
}
|
||||
130
runtime/src/app/start/index.ts
Normal file
130
runtime/src/app/start/index.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import {
|
||||
cid,
|
||||
history,
|
||||
initial_data,
|
||||
navigate,
|
||||
scroll_history,
|
||||
scroll_state,
|
||||
select_target,
|
||||
set_target,
|
||||
uid,
|
||||
set_uid,
|
||||
set_cid
|
||||
} from '../app';
|
||||
import prefetch from '../prefetch/index';
|
||||
|
||||
export default function start(opts: {
|
||||
target: Node
|
||||
}) {
|
||||
if ('scrollRestoration' in history) {
|
||||
history.scrollRestoration = 'manual';
|
||||
}
|
||||
|
||||
set_target(opts.target);
|
||||
|
||||
addEventListener('click', handle_click);
|
||||
addEventListener('popstate', handle_popstate);
|
||||
|
||||
// prefetch
|
||||
addEventListener('touchstart', trigger_prefetch);
|
||||
addEventListener('mousemove', handle_mousemove);
|
||||
|
||||
return Promise.resolve().then(() => {
|
||||
const { hash, href } = location;
|
||||
|
||||
history.replaceState({ id: uid }, '', href);
|
||||
|
||||
if (!initial_data.error) {
|
||||
const target = select_target(new URL(location.href));
|
||||
if (target) return navigate(target, uid, false, hash);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mousemove_timeout: NodeJS.Timer;
|
||||
|
||||
function handle_mousemove(event: MouseEvent) {
|
||||
clearTimeout(mousemove_timeout);
|
||||
mousemove_timeout = setTimeout(() => {
|
||||
trigger_prefetch(event);
|
||||
}, 20);
|
||||
}
|
||||
|
||||
function trigger_prefetch(event: MouseEvent | TouchEvent) {
|
||||
const a: HTMLAnchorElement = <HTMLAnchorElement>find_anchor(<Node>event.target);
|
||||
if (!a || a.rel !== 'prefetch') return;
|
||||
|
||||
prefetch(a.href);
|
||||
}
|
||||
|
||||
function handle_click(event: MouseEvent) {
|
||||
// Adapted from https://github.com/visionmedia/page.js
|
||||
// MIT license https://github.com/visionmedia/page.js#license
|
||||
if (which(event) !== 1) return;
|
||||
if (event.metaKey || event.ctrlKey || event.shiftKey) return;
|
||||
if (event.defaultPrevented) return;
|
||||
|
||||
const a: HTMLAnchorElement | SVGAElement = <HTMLAnchorElement | SVGAElement>find_anchor(<Node>event.target);
|
||||
if (!a) return;
|
||||
|
||||
if (!a.href) return;
|
||||
|
||||
// check if link is inside an svg
|
||||
// in this case, both href and target are always inside an object
|
||||
const svg = typeof a.href === 'object' && a.href.constructor.name === 'SVGAnimatedString';
|
||||
const href = String(svg ? (<SVGAElement>a).href.baseVal : a.href);
|
||||
|
||||
if (href === location.href) {
|
||||
if (!location.hash) event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore if tag has
|
||||
// 1. 'download' attribute
|
||||
// 2. rel='external' attribute
|
||||
if (a.hasAttribute('download') || a.getAttribute('rel') === 'external') return;
|
||||
|
||||
// Ignore if <a> has a target
|
||||
if (svg ? (<SVGAElement>a).target.baseVal : a.target) return;
|
||||
|
||||
const url = new URL(href);
|
||||
|
||||
// Don't handle hash changes
|
||||
if (url.pathname === location.pathname && url.search === location.search) return;
|
||||
|
||||
const target = select_target(url);
|
||||
if (target) {
|
||||
const noscroll = a.hasAttribute('sapper-noscroll');
|
||||
navigate(target, null, noscroll, url.hash);
|
||||
event.preventDefault();
|
||||
history.pushState({ id: cid }, '', url.href);
|
||||
}
|
||||
}
|
||||
|
||||
function which(event: MouseEvent) {
|
||||
return event.which === null ? event.button : event.which;
|
||||
}
|
||||
|
||||
function find_anchor(node: Node) {
|
||||
while (node && node.nodeName.toUpperCase() !== 'A') node = node.parentNode; // SVG <a> elements have a lowercase name
|
||||
return node;
|
||||
}
|
||||
|
||||
function handle_popstate(event: PopStateEvent) {
|
||||
scroll_history[cid] = scroll_state();
|
||||
|
||||
if (event.state) {
|
||||
const url = new URL(location.href);
|
||||
const target = select_target(url);
|
||||
if (target) {
|
||||
navigate(target, event.state.id);
|
||||
} else {
|
||||
location.href = location.href;
|
||||
}
|
||||
} else {
|
||||
// hashchange
|
||||
set_uid(uid + 1);
|
||||
set_cid(uid);
|
||||
history.replaceState({ id: cid }, '', location.href);
|
||||
}
|
||||
}
|
||||
62
runtime/src/app/types.ts
Normal file
62
runtime/src/app/types.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
export type Params = Record<string, string>;
|
||||
export type Query = Record<string, string | true>;
|
||||
export type RouteData = { params: Params, query: Query, path: string };
|
||||
|
||||
type Child = {
|
||||
segment?: string;
|
||||
props?: any;
|
||||
component?: Component;
|
||||
};
|
||||
|
||||
export interface ComponentConstructor {
|
||||
new (options: { target: Node, props: any, hydrate: boolean }): Component;
|
||||
preload: (props: { params: Params, query: Query }) => Promise<any>;
|
||||
};
|
||||
|
||||
export interface Component {
|
||||
$set: (data: any) => void;
|
||||
$destroy: () => void;
|
||||
}
|
||||
|
||||
export type ComponentLoader = {
|
||||
js: () => Promise<{ default: ComponentConstructor }>,
|
||||
css: string[]
|
||||
};
|
||||
|
||||
export type Route = {
|
||||
pattern: RegExp;
|
||||
parts: Array<{
|
||||
i: number;
|
||||
params?: (match: RegExpExecArray) => Record<string, string>;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type Manifest = {
|
||||
ignore: RegExp[];
|
||||
root: ComponentConstructor;
|
||||
error: () => Promise<{ default: ComponentConstructor }>;
|
||||
pages: Route[]
|
||||
};
|
||||
|
||||
export type ScrollPosition = {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export type Target = {
|
||||
href: string;
|
||||
route: Route;
|
||||
match: RegExpExecArray;
|
||||
page: Page;
|
||||
};
|
||||
|
||||
export type Redirect = {
|
||||
statusCode: number;
|
||||
location: string;
|
||||
};
|
||||
|
||||
export type Page = {
|
||||
path: string;
|
||||
params: Record<string, string>;
|
||||
query: Record<string, string | string[]>;
|
||||
};
|
||||
1
runtime/src/server/constants.js
Normal file
1
runtime/src/server/constants.js
Normal file
@@ -0,0 +1 @@
|
||||
export const IGNORE = '__SAPPER__IGNORE__';
|
||||
1
runtime/src/server/index.ts
Normal file
1
runtime/src/server/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as middleware } from './middleware/index';
|
||||
365
runtime/src/server/middleware/get_page_handler.ts
Normal file
365
runtime/src/server/middleware/get_page_handler.ts
Normal file
@@ -0,0 +1,365 @@
|
||||
import { writable } from 'svelte/store.mjs';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import cookie from 'cookie';
|
||||
import devalue from 'devalue';
|
||||
import fetch from 'node-fetch';
|
||||
import URL from 'url';
|
||||
import { IGNORE } from '../constants';
|
||||
import { Manifest, Page, Props, Req, Res } from './types';
|
||||
import { build_dir, dev, src_dir } from '@sapper/internal/manifest-server';
|
||||
import { stores } from '@sapper/internal/shared';
|
||||
import Sapper from '@sapper/internal/Sapper.html';
|
||||
|
||||
export function get_page_handler(
|
||||
manifest: Manifest,
|
||||
session_getter: (req: Req, res: Res) => any
|
||||
) {
|
||||
const get_build_info = dev
|
||||
? () => JSON.parse(fs.readFileSync(path.join(build_dir, 'build.json'), 'utf-8'))
|
||||
: (assets => () => assets)(JSON.parse(fs.readFileSync(path.join(build_dir, 'build.json'), 'utf-8')));
|
||||
|
||||
const template = dev
|
||||
? () => read_template(src_dir)
|
||||
: (str => () => str)(read_template(build_dir));
|
||||
|
||||
const has_service_worker = fs.existsSync(path.join(build_dir, 'service-worker.js'));
|
||||
|
||||
const { server_routes, pages } = manifest;
|
||||
const error_route = manifest.error;
|
||||
|
||||
function handle_error(req: Req, res: Res, statusCode: number, error: Error | string) {
|
||||
handle_page({
|
||||
pattern: null,
|
||||
parts: [
|
||||
{ name: null, component: error_route }
|
||||
]
|
||||
}, req, res, statusCode, error || new Error('Unknown error in preload function'));
|
||||
}
|
||||
|
||||
async function handle_page(page: Page, req: Req, res: Res, status = 200, error: Error | string = null) {
|
||||
const isSWIndexHtml = req.path === '/service-worker-index.html';
|
||||
const build_info: {
|
||||
bundler: 'rollup' | 'webpack',
|
||||
shimport: string | null,
|
||||
assets: Record<string, string | string[]>,
|
||||
legacy_assets?: Record<string, string>
|
||||
} = get_build_info();
|
||||
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.setHeader('Cache-Control', dev ? 'no-cache' : 'max-age=600');
|
||||
|
||||
// preload main.js and current route
|
||||
// TODO detect other stuff we can preload? images, CSS, fonts?
|
||||
let preloaded_chunks = Array.isArray(build_info.assets.main) ? build_info.assets.main : [build_info.assets.main];
|
||||
if (!error && !isSWIndexHtml) {
|
||||
page.parts.forEach(part => {
|
||||
if (!part) return;
|
||||
|
||||
// using concat because it could be a string or an array. thanks webpack!
|
||||
preloaded_chunks = preloaded_chunks.concat(build_info.assets[part.name]);
|
||||
});
|
||||
}
|
||||
|
||||
if (build_info.bundler === 'rollup') {
|
||||
// TODO add dependencies and CSS
|
||||
const link = preloaded_chunks
|
||||
.filter(file => file && !file.match(/\.map$/))
|
||||
.map(file => `<${req.baseUrl}/client/${file}>;rel="modulepreload"`)
|
||||
.join(', ');
|
||||
|
||||
res.setHeader('Link', link);
|
||||
} else {
|
||||
const link = preloaded_chunks
|
||||
.filter(file => file && !file.match(/\.map$/))
|
||||
.map((file) => {
|
||||
const as = /\.css$/.test(file) ? 'style' : 'script';
|
||||
return `<${req.baseUrl}/client/${file}>;rel="preload";as="${as}"`;
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
res.setHeader('Link', link);
|
||||
}
|
||||
|
||||
const session = session_getter(req, res);
|
||||
|
||||
let redirect: { statusCode: number, location: string };
|
||||
let preload_error: { statusCode: number, message: Error | string };
|
||||
|
||||
const preload_context = {
|
||||
redirect: (statusCode: number, location: string) => {
|
||||
if (redirect && (redirect.statusCode !== statusCode || redirect.location !== location)) {
|
||||
throw new Error(`Conflicting redirects`);
|
||||
}
|
||||
location = location.replace(/^\//g, ''); // leading slash (only)
|
||||
redirect = { statusCode, location };
|
||||
},
|
||||
error: (statusCode: number, message: Error | string) => {
|
||||
preload_error = { statusCode, message };
|
||||
},
|
||||
fetch: (url: string, opts?: any) => {
|
||||
const parsed = new URL.URL(url, `http://127.0.0.1:${process.env.PORT}${req.baseUrl ? req.baseUrl + '/' :''}`);
|
||||
|
||||
if (opts) {
|
||||
opts = Object.assign({}, opts);
|
||||
|
||||
const include_cookies = (
|
||||
opts.credentials === 'include' ||
|
||||
opts.credentials === 'same-origin' && parsed.origin === `http://127.0.0.1:${process.env.PORT}`
|
||||
);
|
||||
|
||||
if (include_cookies) {
|
||||
opts.headers = Object.assign({}, opts.headers);
|
||||
|
||||
const cookies = Object.assign(
|
||||
{},
|
||||
cookie.parse(req.headers.cookie || ''),
|
||||
cookie.parse(opts.headers.cookie || '')
|
||||
);
|
||||
|
||||
const set_cookie = res.getHeader('Set-Cookie');
|
||||
(Array.isArray(set_cookie) ? set_cookie : [set_cookie]).forEach(str => {
|
||||
const match = /([^=]+)=([^;]+)/.exec(<string>str);
|
||||
if (match) cookies[match[1]] = match[2];
|
||||
});
|
||||
|
||||
const str = Object.keys(cookies)
|
||||
.map(key => `${key}=${cookies[key]}`)
|
||||
.join('; ');
|
||||
|
||||
opts.headers.cookie = str;
|
||||
}
|
||||
}
|
||||
|
||||
return fetch(parsed.href, opts);
|
||||
}
|
||||
};
|
||||
|
||||
let preloaded;
|
||||
let match;
|
||||
let params;
|
||||
|
||||
try {
|
||||
const root_preloaded = manifest.root_preload
|
||||
? manifest.root_preload.call(preload_context, {
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
params: {}
|
||||
})
|
||||
: {};
|
||||
|
||||
match = error ? null : page.pattern.exec(req.path);
|
||||
|
||||
|
||||
let toPreload = [root_preloaded];
|
||||
if (!isSWIndexHtml) {
|
||||
toPreload = toPreload.concat(page.parts.map(part => {
|
||||
if (!part) return null;
|
||||
|
||||
// the deepest level is used below, to initialise the store
|
||||
params = part.params ? part.params(match) : {};
|
||||
|
||||
return part.preload
|
||||
? part.preload.call(preload_context, {
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
params
|
||||
}, session)
|
||||
: {};
|
||||
}))
|
||||
}
|
||||
|
||||
preloaded = await Promise.all(toPreload);
|
||||
} catch (err) {
|
||||
preload_error = { statusCode: 500, message: err };
|
||||
preloaded = []; // appease TypeScript
|
||||
}
|
||||
|
||||
try {
|
||||
if (redirect) {
|
||||
const location = URL.resolve(req.baseUrl || '/', redirect.location);
|
||||
|
||||
res.statusCode = redirect.statusCode;
|
||||
res.setHeader('Location', location);
|
||||
res.end();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (preload_error) {
|
||||
handle_error(req, res, preload_error.statusCode, preload_error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
const segments = req.path.split('/').filter(Boolean);
|
||||
|
||||
const props = Object.assign({}, preloaded[0], {
|
||||
child: {
|
||||
segment: segments[0],
|
||||
props: {}
|
||||
}
|
||||
});
|
||||
|
||||
let level = props.child;
|
||||
if (!isSWIndexHtml) {
|
||||
for (let i = 0; i < page.parts.length; i += 1) {
|
||||
const part = page.parts[i];
|
||||
if (!part) continue;
|
||||
|
||||
Object.assign(level, {
|
||||
component: part.component,
|
||||
props: Object.assign({}, preloaded[i + 1])
|
||||
});
|
||||
|
||||
level.props.child = <Props["child"]>{
|
||||
segment: segments[i + 1],
|
||||
props: {}
|
||||
};
|
||||
level = level.props.child;
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
props.child.props.error = error instanceof Error ? error : { message: error };
|
||||
props.child.props.status = status;
|
||||
}
|
||||
|
||||
stores.page.set({
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
params: params
|
||||
});
|
||||
|
||||
const { html, head, css } = Sapper.render({
|
||||
Root: manifest.root,
|
||||
props: props,
|
||||
session: writable(session)
|
||||
});
|
||||
|
||||
const serialized = {
|
||||
preloaded: `[${preloaded.map(data => try_serialize(data)).join(',')}]`,
|
||||
session: session && try_serialize(session, err => {
|
||||
throw new Error(`Failed to serialize session data: ${err.message}`);
|
||||
})
|
||||
};
|
||||
|
||||
let script = `__SAPPER__={${[
|
||||
error && `error:1`,
|
||||
`baseUrl:"${req.baseUrl}"`,
|
||||
serialized.preloaded && `preloaded:${serialized.preloaded}`,
|
||||
serialized.session && `session:${serialized.session}`
|
||||
].filter(Boolean).join(',')}};`;
|
||||
|
||||
if (has_service_worker) {
|
||||
script += `if('serviceWorker' in navigator)navigator.serviceWorker.register('${req.baseUrl}/service-worker.js');`;
|
||||
}
|
||||
|
||||
const file = [].concat(build_info.assets.main).filter(file => file && /\.js$/.test(file))[0];
|
||||
const main = `${req.baseUrl}/client/${file}`;
|
||||
|
||||
if (build_info.bundler === 'rollup') {
|
||||
if (build_info.legacy_assets) {
|
||||
const legacy_main = `${req.baseUrl}/client/legacy/${build_info.legacy_assets.main}`;
|
||||
script += `(function(){try{eval("async function x(){}");var main="${main}"}catch(e){main="${legacy_main}"};var s=document.createElement("script");try{new Function("if(0)import('')")();s.src=main;s.type="module";s.crossOrigin="use-credentials";}catch(e){s.src="${req.baseUrl}/client/shimport@${build_info.shimport}.js";s.setAttribute("data-main",main);}document.head.appendChild(s);}());`;
|
||||
} else {
|
||||
script += `var s=document.createElement("script");try{new Function("if(0)import('')")();s.src="${main}";s.type="module";s.crossOrigin="use-credentials";}catch(e){s.src="${req.baseUrl}/client/shimport@${build_info.shimport}.js";s.setAttribute("data-main","${main}")}document.head.appendChild(s)`;
|
||||
}
|
||||
} else {
|
||||
script += `</script><script src="${main}">`;
|
||||
}
|
||||
|
||||
let styles: string;
|
||||
|
||||
// TODO make this consistent across apps
|
||||
// TODO embed build_info in placeholder.ts
|
||||
if (build_info.css && build_info.css.main) {
|
||||
const css_chunks = new Set();
|
||||
if (build_info.css.main) css_chunks.add(build_info.css.main);
|
||||
page.parts.forEach(part => {
|
||||
if (!part) return;
|
||||
const css_chunks_for_part = build_info.css.chunks[part.file];
|
||||
|
||||
if (css_chunks_for_part) {
|
||||
css_chunks_for_part.forEach(file => {
|
||||
css_chunks.add(file);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
styles = Array.from(css_chunks)
|
||||
.map(href => `<link rel="stylesheet" href="client/${href}">`)
|
||||
.join('')
|
||||
} else {
|
||||
styles = (css && css.code ? `<style>${css.code}</style>` : '');
|
||||
}
|
||||
|
||||
// users can set a CSP nonce using res.locals.nonce
|
||||
const nonce_attr = (res.locals && res.locals.nonce) ? ` nonce="${res.locals.nonce}"` : '';
|
||||
|
||||
const body = template()
|
||||
.replace('%sapper.base%', () => `<base href="${req.baseUrl}/">`)
|
||||
.replace('%sapper.scripts%', () => `<script${nonce_attr}>${script}</script>`)
|
||||
.replace('%sapper.html%', () => html)
|
||||
.replace('%sapper.head%', () => `<noscript id='sapper-head-start'></noscript>${head}<noscript id='sapper-head-end'></noscript>`)
|
||||
.replace('%sapper.styles%', () => styles);
|
||||
|
||||
res.statusCode = status;
|
||||
res.end(body);
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
if (error) {
|
||||
// we encountered an error while rendering the error page — oops
|
||||
res.statusCode = 500;
|
||||
res.end(`<pre>${escape_html(err.message)}</pre>`);
|
||||
} else {
|
||||
handle_error(req, res, 500, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return function find_route(req: Req, res: Res, next: () => void) {
|
||||
if (req[IGNORE]) return next();
|
||||
|
||||
if (req.path === '/service-worker-index.html') {
|
||||
const homePage = pages.find(page => page.pattern.test('/'));
|
||||
handle_page(homePage, req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!server_routes.some(route => route.pattern.test(req.path))) {
|
||||
for (const page of pages) {
|
||||
if (page.pattern.test(req.path)) {
|
||||
handle_page(page, req, res);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handle_error(req, res, 404, 'Not found');
|
||||
};
|
||||
}
|
||||
|
||||
function read_template(dir = build_dir) {
|
||||
return fs.readFileSync(`${dir}/template.html`, 'utf-8');
|
||||
}
|
||||
|
||||
function try_serialize(data: any, fail?: (err) => void) {
|
||||
try {
|
||||
return devalue(data);
|
||||
} catch (err) {
|
||||
if (fail) fail(err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function escape_html(html: string) {
|
||||
const chars: Record<string, string> = {
|
||||
'"' : 'quot',
|
||||
"'": '#39',
|
||||
'&': 'amp',
|
||||
'<' : 'lt',
|
||||
'>' : 'gt'
|
||||
};
|
||||
|
||||
return html.replace(/["'&<>]/g, c => `&${chars[c]};`);
|
||||
}
|
||||
78
runtime/src/server/middleware/get_server_route_handler.ts
Normal file
78
runtime/src/server/middleware/get_server_route_handler.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { IGNORE } from '../constants';
|
||||
import { Req, Res, ServerRoute } from './types';
|
||||
|
||||
export function get_server_route_handler(routes: ServerRoute[]) {
|
||||
async function handle_route(route: ServerRoute, req: Req, res: Res, next: () => void) {
|
||||
req.params = route.params(route.pattern.exec(req.path));
|
||||
|
||||
const method = req.method.toLowerCase();
|
||||
// 'delete' cannot be exported from a module because it is a keyword,
|
||||
// so check for 'del' instead
|
||||
const method_export = method === 'delete' ? 'del' : method;
|
||||
const handle_method = route.handlers[method_export];
|
||||
if (handle_method) {
|
||||
if (process.env.SAPPER_EXPORT) {
|
||||
const { write, end, setHeader } = res;
|
||||
const chunks: any[] = [];
|
||||
const headers: Record<string, string> = {};
|
||||
|
||||
// intercept data so that it can be exported
|
||||
res.write = function(chunk: any) {
|
||||
chunks.push(Buffer.from(chunk));
|
||||
write.apply(res, arguments);
|
||||
};
|
||||
|
||||
res.setHeader = function(name: string, value: string) {
|
||||
headers[name.toLowerCase()] = value;
|
||||
setHeader.apply(res, arguments);
|
||||
};
|
||||
|
||||
res.end = function(chunk?: any) {
|
||||
if (chunk) chunks.push(Buffer.from(chunk));
|
||||
end.apply(res, arguments);
|
||||
|
||||
process.send({
|
||||
__sapper__: true,
|
||||
event: 'file',
|
||||
url: req.url,
|
||||
method: req.method,
|
||||
status: res.statusCode,
|
||||
type: headers['content-type'],
|
||||
body: Buffer.concat(chunks).toString()
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const handle_next = (err?: Error) => {
|
||||
if (err) {
|
||||
res.statusCode = 500;
|
||||
res.end(err.message);
|
||||
} else {
|
||||
process.nextTick(next);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
await handle_method(req, res, handle_next);
|
||||
} catch (err) {
|
||||
handle_next(err);
|
||||
}
|
||||
} else {
|
||||
// no matching handler for method
|
||||
process.nextTick(next);
|
||||
}
|
||||
}
|
||||
|
||||
return function find_route(req: Req, res: Res, next: () => void) {
|
||||
if (req[IGNORE]) return next();
|
||||
|
||||
for (const route of routes) {
|
||||
if (route.pattern.test(req.path)) {
|
||||
handle_route(route, req, res, next);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
146
runtime/src/server/middleware/index.ts
Normal file
146
runtime/src/server/middleware/index.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { build_dir, dev, manifest } from '@sapper/internal/manifest-server';
|
||||
import { Handler, Req, Res } from './types';
|
||||
import { get_server_route_handler } from './get_server_route_handler';
|
||||
import { get_page_handler } from './get_page_handler';
|
||||
import { lookup } from './mime';
|
||||
import { IGNORE } from '../constants';
|
||||
|
||||
export default function middleware(opts: {
|
||||
session?: (req: Req, res: Res) => any,
|
||||
ignore?: any
|
||||
} = {}) {
|
||||
const { session, ignore } = opts;
|
||||
|
||||
let emitted_basepath = false;
|
||||
|
||||
return compose_handlers([
|
||||
ignore && ((req: Req, res: Res, next: () => void) => {
|
||||
req[IGNORE] = should_ignore(req.path, ignore);
|
||||
next();
|
||||
}),
|
||||
|
||||
(req: Req, res: Res, next: () => void) => {
|
||||
if (req[IGNORE]) return next();
|
||||
|
||||
if (req.baseUrl === undefined) {
|
||||
let { originalUrl } = req;
|
||||
if (req.url === '/' && originalUrl[originalUrl.length - 1] !== '/') {
|
||||
originalUrl += '/';
|
||||
}
|
||||
|
||||
req.baseUrl = originalUrl
|
||||
? originalUrl.slice(0, -req.url.length)
|
||||
: '';
|
||||
}
|
||||
|
||||
if (!emitted_basepath && process.send) {
|
||||
process.send({
|
||||
__sapper__: true,
|
||||
event: 'basepath',
|
||||
basepath: req.baseUrl
|
||||
});
|
||||
|
||||
emitted_basepath = true;
|
||||
}
|
||||
|
||||
if (req.path === undefined) {
|
||||
req.path = req.url.replace(/\?.*/, '');
|
||||
}
|
||||
|
||||
next();
|
||||
},
|
||||
|
||||
fs.existsSync(path.join(build_dir, 'index.html')) && serve({
|
||||
pathname: '/index.html',
|
||||
cache_control: dev ? 'no-cache' : 'max-age=600'
|
||||
}),
|
||||
|
||||
fs.existsSync(path.join(build_dir, 'service-worker.js')) && serve({
|
||||
pathname: '/service-worker.js',
|
||||
cache_control: 'no-cache, no-store, must-revalidate'
|
||||
}),
|
||||
|
||||
fs.existsSync(path.join(build_dir, 'service-worker.js.map')) && serve({
|
||||
pathname: '/service-worker.js.map',
|
||||
cache_control: 'no-cache, no-store, must-revalidate'
|
||||
}),
|
||||
|
||||
serve({
|
||||
prefix: '/client/',
|
||||
cache_control: dev ? 'no-cache' : 'max-age=31536000, immutable'
|
||||
}),
|
||||
|
||||
get_server_route_handler(manifest.server_routes),
|
||||
|
||||
get_page_handler(manifest, session || noop)
|
||||
].filter(Boolean));
|
||||
}
|
||||
|
||||
export function compose_handlers(handlers: Handler[]) {
|
||||
return (req: Req, res: Res, next: () => void) => {
|
||||
let i = 0;
|
||||
function go() {
|
||||
const handler = handlers[i];
|
||||
|
||||
if (handler) {
|
||||
handler(req, res, () => {
|
||||
i += 1;
|
||||
go();
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
go();
|
||||
};
|
||||
}
|
||||
|
||||
export function should_ignore(uri: string, val: any) {
|
||||
if (Array.isArray(val)) return val.some(x => should_ignore(uri, x));
|
||||
if (val instanceof RegExp) return val.test(uri);
|
||||
if (typeof val === 'function') return val(uri);
|
||||
return uri.startsWith(val.charCodeAt(0) === 47 ? val : `/${val}`);
|
||||
}
|
||||
|
||||
export function serve({ prefix, pathname, cache_control }: {
|
||||
prefix?: string,
|
||||
pathname?: string,
|
||||
cache_control: string
|
||||
}) {
|
||||
const filter = pathname
|
||||
? (req: Req) => req.path === pathname
|
||||
: (req: Req) => req.path.startsWith(prefix);
|
||||
|
||||
const cache: Map<string, Buffer> = new Map();
|
||||
|
||||
const read = dev
|
||||
? (file: string) => fs.readFileSync(path.resolve(build_dir, file))
|
||||
: (file: string) => (cache.has(file) ? cache : cache.set(file, fs.readFileSync(path.resolve(build_dir, file)))).get(file)
|
||||
|
||||
return (req: Req, res: Res, next: () => void) => {
|
||||
if (req[IGNORE]) return next();
|
||||
|
||||
if (filter(req)) {
|
||||
const type = lookup(req.path);
|
||||
|
||||
try {
|
||||
const file = decodeURIComponent(req.path.slice(1));
|
||||
const data = read(file);
|
||||
|
||||
res.setHeader('Content-Type', type);
|
||||
res.setHeader('Cache-Control', cache_control);
|
||||
res.end(data);
|
||||
} catch (err) {
|
||||
res.statusCode = 404;
|
||||
res.end('not found');
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function noop(){}
|
||||
767
runtime/src/server/middleware/mime-types.md
Normal file
767
runtime/src/server/middleware/mime-types.md
Normal file
@@ -0,0 +1,767 @@
|
||||
application/andrew-inset ez
|
||||
application/applixware aw
|
||||
application/atom+xml atom
|
||||
application/atomcat+xml atomcat
|
||||
application/atomsvc+xml atomsvc
|
||||
application/ccxml+xml ccxml
|
||||
application/cdmi-capability cdmia
|
||||
application/cdmi-container cdmic
|
||||
application/cdmi-domain cdmid
|
||||
application/cdmi-object cdmio
|
||||
application/cdmi-queue cdmiq
|
||||
application/cu-seeme cu
|
||||
application/davmount+xml davmount
|
||||
application/docbook+xml dbk
|
||||
application/dssc+der dssc
|
||||
application/dssc+xml xdssc
|
||||
application/ecmascript ecma
|
||||
application/emma+xml emma
|
||||
application/epub+zip epub
|
||||
application/exi exi
|
||||
application/font-tdpfr pfr
|
||||
application/gml+xml gml
|
||||
application/gpx+xml gpx
|
||||
application/gxf gxf
|
||||
application/hyperstudio stk
|
||||
application/inkml+xml ink inkml
|
||||
application/ipfix ipfix
|
||||
application/java-archive jar
|
||||
application/java-serialized-object ser
|
||||
application/java-vm class
|
||||
application/javascript js
|
||||
application/json json map
|
||||
application/jsonml+json jsonml
|
||||
application/lost+xml lostxml
|
||||
application/mac-binhex40 hqx
|
||||
application/mac-compactpro cpt
|
||||
application/mads+xml mads
|
||||
application/marc mrc
|
||||
application/marcxml+xml mrcx
|
||||
application/mathematica ma nb mb
|
||||
application/mathml+xml mathml
|
||||
application/mbox mbox
|
||||
application/mediaservercontrol+xml mscml
|
||||
application/metalink+xml metalink
|
||||
application/metalink4+xml meta4
|
||||
application/mets+xml mets
|
||||
application/mods+xml mods
|
||||
application/mp21 m21 mp21
|
||||
application/mp4 mp4s
|
||||
application/msword doc dot
|
||||
application/mxf mxf
|
||||
application/octet-stream bin dms lrf mar so dist distz pkg bpk dump elc deploy
|
||||
application/oda oda
|
||||
application/oebps-package+xml opf
|
||||
application/ogg ogx
|
||||
application/omdoc+xml omdoc
|
||||
application/onenote onetoc onetoc2 onetmp onepkg
|
||||
application/oxps oxps
|
||||
application/patch-ops-error+xml xer
|
||||
application/pdf pdf
|
||||
application/pgp-encrypted pgp
|
||||
application/pgp-signature asc sig
|
||||
application/pics-rules prf
|
||||
application/pkcs10 p10
|
||||
application/pkcs7-mime p7m p7c
|
||||
application/pkcs7-signature p7s
|
||||
application/pkcs8 p8
|
||||
application/pkix-attr-cert ac
|
||||
application/pkix-cert cer
|
||||
application/pkix-crl crl
|
||||
application/pkix-pkipath pkipath
|
||||
application/pkixcmp pki
|
||||
application/pls+xml pls
|
||||
application/postscript ai eps ps
|
||||
application/prs.cww cww
|
||||
application/pskc+xml pskcxml
|
||||
application/rdf+xml rdf
|
||||
application/reginfo+xml rif
|
||||
application/relax-ng-compact-syntax rnc
|
||||
application/resource-lists+xml rl
|
||||
application/resource-lists-diff+xml rld
|
||||
application/rls-services+xml rs
|
||||
application/rpki-ghostbusters gbr
|
||||
application/rpki-manifest mft
|
||||
application/rpki-roa roa
|
||||
application/rsd+xml rsd
|
||||
application/rss+xml rss
|
||||
application/rtf rtf
|
||||
application/sbml+xml sbml
|
||||
application/scvp-cv-request scq
|
||||
application/scvp-cv-response scs
|
||||
application/scvp-vp-request spq
|
||||
application/scvp-vp-response spp
|
||||
application/sdp sdp
|
||||
application/set-payment-initiation setpay
|
||||
application/set-registration-initiation setreg
|
||||
application/shf+xml shf
|
||||
application/smil+xml smi smil
|
||||
application/sparql-query rq
|
||||
application/sparql-results+xml srx
|
||||
application/srgs gram
|
||||
application/srgs+xml grxml
|
||||
application/sru+xml sru
|
||||
application/ssdl+xml ssdl
|
||||
application/ssml+xml ssml
|
||||
application/tei+xml tei teicorpus
|
||||
application/thraud+xml tfi
|
||||
application/timestamped-data tsd
|
||||
application/vnd.3gpp.pic-bw-large plb
|
||||
application/vnd.3gpp.pic-bw-small psb
|
||||
application/vnd.3gpp.pic-bw-var pvb
|
||||
application/vnd.3gpp2.tcap tcap
|
||||
application/vnd.3m.post-it-notes pwn
|
||||
application/vnd.accpac.simply.aso aso
|
||||
application/vnd.accpac.simply.imp imp
|
||||
application/vnd.acucobol acu
|
||||
application/vnd.acucorp atc acutc
|
||||
application/vnd.adobe.air-application-installer-package+zip air
|
||||
application/vnd.adobe.formscentral.fcdt fcdt
|
||||
application/vnd.adobe.fxp fxp fxpl
|
||||
application/vnd.adobe.xdp+xml xdp
|
||||
application/vnd.adobe.xfdf xfdf
|
||||
application/vnd.ahead.space ahead
|
||||
application/vnd.airzip.filesecure.azf azf
|
||||
application/vnd.airzip.filesecure.azs azs
|
||||
application/vnd.amazon.ebook azw
|
||||
application/vnd.americandynamics.acc acc
|
||||
application/vnd.amiga.ami ami
|
||||
application/vnd.android.package-archive apk
|
||||
application/vnd.anser-web-certificate-issue-initiation cii
|
||||
application/vnd.anser-web-funds-transfer-initiation fti
|
||||
application/vnd.antix.game-component atx
|
||||
application/vnd.apple.installer+xml mpkg
|
||||
application/vnd.apple.mpegurl m3u8
|
||||
application/vnd.aristanetworks.swi swi
|
||||
application/vnd.astraea-software.iota iota
|
||||
application/vnd.audiograph aep
|
||||
application/vnd.blueice.multipass mpm
|
||||
application/vnd.bmi bmi
|
||||
application/vnd.businessobjects rep
|
||||
application/vnd.chemdraw+xml cdxml
|
||||
application/vnd.chipnuts.karaoke-mmd mmd
|
||||
application/vnd.cinderella cdy
|
||||
application/vnd.claymore cla
|
||||
application/vnd.cloanto.rp9 rp9
|
||||
application/vnd.clonk.c4group c4g c4d c4f c4p c4u
|
||||
application/vnd.cluetrust.cartomobile-config c11amc
|
||||
application/vnd.cluetrust.cartomobile-config-pkg c11amz
|
||||
application/vnd.commonspace csp
|
||||
application/vnd.contact.cmsg cdbcmsg
|
||||
application/vnd.cosmocaller cmc
|
||||
application/vnd.crick.clicker clkx
|
||||
application/vnd.crick.clicker.keyboard clkk
|
||||
application/vnd.crick.clicker.palette clkp
|
||||
application/vnd.crick.clicker.template clkt
|
||||
application/vnd.crick.clicker.wordbank clkw
|
||||
application/vnd.criticaltools.wbs+xml wbs
|
||||
application/vnd.ctc-posml pml
|
||||
application/vnd.cups-ppd ppd
|
||||
application/vnd.curl.car car
|
||||
application/vnd.curl.pcurl pcurl
|
||||
application/vnd.dart dart
|
||||
application/vnd.data-vision.rdz rdz
|
||||
application/vnd.dece.data uvf uvvf uvd uvvd
|
||||
application/vnd.dece.ttml+xml uvt uvvt
|
||||
application/vnd.dece.unspecified uvx uvvx
|
||||
application/vnd.dece.zip uvz uvvz
|
||||
application/vnd.denovo.fcselayout-link fe_launch
|
||||
application/vnd.dna dna
|
||||
application/vnd.dolby.mlp mlp
|
||||
application/vnd.dpgraph dpg
|
||||
application/vnd.dreamfactory dfac
|
||||
application/vnd.ds-keypoint kpxx
|
||||
application/vnd.dvb.ait ait
|
||||
application/vnd.dvb.service svc
|
||||
application/vnd.dynageo geo
|
||||
application/vnd.ecowin.chart mag
|
||||
application/vnd.enliven nml
|
||||
application/vnd.epson.esf esf
|
||||
application/vnd.epson.msf msf
|
||||
application/vnd.epson.quickanime qam
|
||||
application/vnd.epson.salt slt
|
||||
application/vnd.epson.ssf ssf
|
||||
application/vnd.eszigno3+xml es3 et3
|
||||
application/vnd.ezpix-album ez2
|
||||
application/vnd.ezpix-package ez3
|
||||
application/vnd.fdf fdf
|
||||
application/vnd.fdsn.mseed mseed
|
||||
application/vnd.fdsn.seed seed dataless
|
||||
application/vnd.flographit gph
|
||||
application/vnd.fluxtime.clip ftc
|
||||
application/vnd.framemaker fm frame maker book
|
||||
application/vnd.frogans.fnc fnc
|
||||
application/vnd.frogans.ltf ltf
|
||||
application/vnd.fsc.weblaunch fsc
|
||||
application/vnd.fujitsu.oasys oas
|
||||
application/vnd.fujitsu.oasys2 oa2
|
||||
application/vnd.fujitsu.oasys3 oa3
|
||||
application/vnd.fujitsu.oasysgp fg5
|
||||
application/vnd.fujitsu.oasysprs bh2
|
||||
application/vnd.fujixerox.ddd ddd
|
||||
application/vnd.fujixerox.docuworks xdw
|
||||
application/vnd.fujixerox.docuworks.binder xbd
|
||||
application/vnd.fuzzysheet fzs
|
||||
application/vnd.genomatix.tuxedo txd
|
||||
application/vnd.geogebra.file ggb
|
||||
application/vnd.geogebra.tool ggt
|
||||
application/vnd.geometry-explorer gex gre
|
||||
application/vnd.geonext gxt
|
||||
application/vnd.geoplan g2w
|
||||
application/vnd.geospace g3w
|
||||
application/vnd.gmx gmx
|
||||
application/vnd.google-earth.kml+xml kml
|
||||
application/vnd.google-earth.kmz kmz
|
||||
application/vnd.grafeq gqf gqs
|
||||
application/vnd.groove-account gac
|
||||
application/vnd.groove-help ghf
|
||||
application/vnd.groove-identity-message gim
|
||||
application/vnd.groove-injector grv
|
||||
application/vnd.groove-tool-message gtm
|
||||
application/vnd.groove-tool-template tpl
|
||||
application/vnd.groove-vcard vcg
|
||||
application/vnd.hal+xml hal
|
||||
application/vnd.handheld-entertainment+xml zmm
|
||||
application/vnd.hbci hbci
|
||||
application/vnd.hhe.lesson-player les
|
||||
application/vnd.hp-hpgl hpgl
|
||||
application/vnd.hp-hpid hpid
|
||||
application/vnd.hp-hps hps
|
||||
application/vnd.hp-jlyt jlt
|
||||
application/vnd.hp-pcl pcl
|
||||
application/vnd.hp-pclxl pclxl
|
||||
application/vnd.hydrostatix.sof-data sfd-hdstx
|
||||
application/vnd.ibm.minipay mpy
|
||||
application/vnd.ibm.modcap afp listafp list3820
|
||||
application/vnd.ibm.rights-management irm
|
||||
application/vnd.ibm.secure-container sc
|
||||
application/vnd.iccprofile icc icm
|
||||
application/vnd.igloader igl
|
||||
application/vnd.immervision-ivp ivp
|
||||
application/vnd.immervision-ivu ivu
|
||||
application/vnd.insors.igm igm
|
||||
application/vnd.intercon.formnet xpw xpx
|
||||
application/vnd.intergeo i2g
|
||||
application/vnd.intu.qbo qbo
|
||||
application/vnd.intu.qfx qfx
|
||||
application/vnd.ipunplugged.rcprofile rcprofile
|
||||
application/vnd.irepository.package+xml irp
|
||||
application/vnd.is-xpr xpr
|
||||
application/vnd.isac.fcs fcs
|
||||
application/vnd.jam jam
|
||||
application/vnd.jcp.javame.midlet-rms rms
|
||||
application/vnd.jisp jisp
|
||||
application/vnd.joost.joda-archive joda
|
||||
application/vnd.kahootz ktz ktr
|
||||
application/vnd.kde.karbon karbon
|
||||
application/vnd.kde.kchart chrt
|
||||
application/vnd.kde.kformula kfo
|
||||
application/vnd.kde.kivio flw
|
||||
application/vnd.kde.kontour kon
|
||||
application/vnd.kde.kpresenter kpr kpt
|
||||
application/vnd.kde.kspread ksp
|
||||
application/vnd.kde.kword kwd kwt
|
||||
application/vnd.kenameaapp htke
|
||||
application/vnd.kidspiration kia
|
||||
application/vnd.kinar kne knp
|
||||
application/vnd.koan skp skd skt skm
|
||||
application/vnd.kodak-descriptor sse
|
||||
application/vnd.las.las+xml lasxml
|
||||
application/vnd.llamagraphics.life-balance.desktop lbd
|
||||
application/vnd.llamagraphics.life-balance.exchange+xml lbe
|
||||
application/vnd.lotus-1-2-3 123
|
||||
application/vnd.lotus-approach apr
|
||||
application/vnd.lotus-freelance pre
|
||||
application/vnd.lotus-notes nsf
|
||||
application/vnd.lotus-organizer org
|
||||
application/vnd.lotus-screencam scm
|
||||
application/vnd.lotus-wordpro lwp
|
||||
application/vnd.macports.portpkg portpkg
|
||||
application/vnd.mcd mcd
|
||||
application/vnd.medcalcdata mc1
|
||||
application/vnd.mediastation.cdkey cdkey
|
||||
application/vnd.mfer mwf
|
||||
application/vnd.mfmp mfm
|
||||
application/vnd.micrografx.flo flo
|
||||
application/vnd.micrografx.igx igx
|
||||
application/vnd.mif mif
|
||||
application/vnd.mobius.daf daf
|
||||
application/vnd.mobius.dis dis
|
||||
application/vnd.mobius.mbk mbk
|
||||
application/vnd.mobius.mqy mqy
|
||||
application/vnd.mobius.msl msl
|
||||
application/vnd.mobius.plc plc
|
||||
application/vnd.mobius.txf txf
|
||||
application/vnd.mophun.application mpn
|
||||
application/vnd.mophun.certificate mpc
|
||||
application/vnd.mozilla.xul+xml xul
|
||||
application/vnd.ms-artgalry cil
|
||||
application/vnd.ms-cab-compressed cab
|
||||
application/vnd.ms-excel xls xlm xla xlc xlt xlw
|
||||
application/vnd.ms-excel.addin.macroenabled.12 xlam
|
||||
application/vnd.ms-excel.sheet.binary.macroenabled.12 xlsb
|
||||
application/vnd.ms-excel.sheet.macroenabled.12 xlsm
|
||||
application/vnd.ms-excel.template.macroenabled.12 xltm
|
||||
application/vnd.ms-fontobject eot
|
||||
application/vnd.ms-htmlhelp chm
|
||||
application/vnd.ms-ims ims
|
||||
application/vnd.ms-lrm lrm
|
||||
application/vnd.ms-officetheme thmx
|
||||
application/vnd.ms-pki.seccat cat
|
||||
application/vnd.ms-pki.stl stl
|
||||
application/vnd.ms-powerpoint ppt pps pot
|
||||
application/vnd.ms-powerpoint.addin.macroenabled.12 ppam
|
||||
application/vnd.ms-powerpoint.presentation.macroenabled.12 pptm
|
||||
application/vnd.ms-powerpoint.slide.macroenabled.12 sldm
|
||||
application/vnd.ms-powerpoint.slideshow.macroenabled.12 ppsm
|
||||
application/vnd.ms-powerpoint.template.macroenabled.12 potm
|
||||
application/vnd.ms-project mpp mpt
|
||||
application/vnd.ms-word.document.macroenabled.12 docm
|
||||
application/vnd.ms-word.template.macroenabled.12 dotm
|
||||
application/vnd.ms-works wps wks wcm wdb
|
||||
application/vnd.ms-wpl wpl
|
||||
application/vnd.ms-xpsdocument xps
|
||||
application/vnd.mseq mseq
|
||||
application/vnd.musician mus
|
||||
application/vnd.muvee.style msty
|
||||
application/vnd.mynfc taglet
|
||||
application/vnd.neurolanguage.nlu nlu
|
||||
application/vnd.nitf ntf nitf
|
||||
application/vnd.noblenet-directory nnd
|
||||
application/vnd.noblenet-sealer nns
|
||||
application/vnd.noblenet-web nnw
|
||||
application/vnd.nokia.n-gage.data ngdat
|
||||
application/vnd.nokia.n-gage.symbian.install n-gage
|
||||
application/vnd.nokia.radio-preset rpst
|
||||
application/vnd.nokia.radio-presets rpss
|
||||
application/vnd.novadigm.edm edm
|
||||
application/vnd.novadigm.edx edx
|
||||
application/vnd.novadigm.ext ext
|
||||
application/vnd.oasis.opendocument.chart odc
|
||||
application/vnd.oasis.opendocument.chart-template otc
|
||||
application/vnd.oasis.opendocument.database odb
|
||||
application/vnd.oasis.opendocument.formula odf
|
||||
application/vnd.oasis.opendocument.formula-template odft
|
||||
application/vnd.oasis.opendocument.graphics odg
|
||||
application/vnd.oasis.opendocument.graphics-template otg
|
||||
application/vnd.oasis.opendocument.image odi
|
||||
application/vnd.oasis.opendocument.image-template oti
|
||||
application/vnd.oasis.opendocument.presentation odp
|
||||
application/vnd.oasis.opendocument.presentation-template otp
|
||||
application/vnd.oasis.opendocument.spreadsheet ods
|
||||
application/vnd.oasis.opendocument.spreadsheet-template ots
|
||||
application/vnd.oasis.opendocument.text odt
|
||||
application/vnd.oasis.opendocument.text-master odm
|
||||
application/vnd.oasis.opendocument.text-template ott
|
||||
application/vnd.oasis.opendocument.text-web oth
|
||||
application/vnd.olpc-sugar xo
|
||||
application/vnd.oma.dd2+xml dd2
|
||||
application/vnd.openofficeorg.extension oxt
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx
|
||||
application/vnd.openxmlformats-officedocument.presentationml.slide sldx
|
||||
application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx
|
||||
application/vnd.openxmlformats-officedocument.presentationml.template potx
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx
|
||||
application/vnd.osgeo.mapguide.package mgp
|
||||
application/vnd.osgi.dp dp
|
||||
application/vnd.osgi.subsystem esa
|
||||
application/vnd.palm pdb pqa oprc
|
||||
application/vnd.pawaafile paw
|
||||
application/vnd.pg.format str
|
||||
application/vnd.pg.osasli ei6
|
||||
application/vnd.picsel efif
|
||||
application/vnd.pmi.widget wg
|
||||
application/vnd.pocketlearn plf
|
||||
application/vnd.powerbuilder6 pbd
|
||||
application/vnd.previewsystems.box box
|
||||
application/vnd.proteus.magazine mgz
|
||||
application/vnd.publishare-delta-tree qps
|
||||
application/vnd.pvi.ptid1 ptid
|
||||
application/vnd.quark.quarkxpress qxd qxt qwd qwt qxl qxb
|
||||
application/vnd.realvnc.bed bed
|
||||
application/vnd.recordare.musicxml mxl
|
||||
application/vnd.recordare.musicxml+xml musicxml
|
||||
application/vnd.rig.cryptonote cryptonote
|
||||
application/vnd.rim.cod cod
|
||||
application/vnd.rn-realmedia rm
|
||||
application/vnd.rn-realmedia-vbr rmvb
|
||||
application/vnd.route66.link66+xml link66
|
||||
application/vnd.sailingtracker.track st
|
||||
application/vnd.seemail see
|
||||
application/vnd.sema sema
|
||||
application/vnd.semd semd
|
||||
application/vnd.semf semf
|
||||
application/vnd.shana.informed.formdata ifm
|
||||
application/vnd.shana.informed.formtemplate itp
|
||||
application/vnd.shana.informed.interchange iif
|
||||
application/vnd.shana.informed.package ipk
|
||||
application/vnd.simtech-mindmapper twd twds
|
||||
application/vnd.smaf mmf
|
||||
application/vnd.smart.teacher teacher
|
||||
application/vnd.solent.sdkm+xml sdkm sdkd
|
||||
application/vnd.spotfire.dxp dxp
|
||||
application/vnd.spotfire.sfs sfs
|
||||
application/vnd.stardivision.calc sdc
|
||||
application/vnd.stardivision.draw sda
|
||||
application/vnd.stardivision.impress sdd
|
||||
application/vnd.stardivision.math smf
|
||||
application/vnd.stardivision.writer sdw vor
|
||||
application/vnd.stardivision.writer-global sgl
|
||||
application/vnd.stepmania.package smzip
|
||||
application/vnd.stepmania.stepchart sm
|
||||
application/vnd.sun.xml.calc sxc
|
||||
application/vnd.sun.xml.calc.template stc
|
||||
application/vnd.sun.xml.draw sxd
|
||||
application/vnd.sun.xml.draw.template std
|
||||
application/vnd.sun.xml.impress sxi
|
||||
application/vnd.sun.xml.impress.template sti
|
||||
application/vnd.sun.xml.math sxm
|
||||
application/vnd.sun.xml.writer sxw
|
||||
application/vnd.sun.xml.writer.global sxg
|
||||
application/vnd.sun.xml.writer.template stw
|
||||
application/vnd.sus-calendar sus susp
|
||||
application/vnd.svd svd
|
||||
application/vnd.symbian.install sis sisx
|
||||
application/vnd.syncml+xml xsm
|
||||
application/vnd.syncml.dm+wbxml bdm
|
||||
application/vnd.syncml.dm+xml xdm
|
||||
application/vnd.tao.intent-module-archive tao
|
||||
application/vnd.tcpdump.pcap pcap cap dmp
|
||||
application/vnd.tmobile-livetv tmo
|
||||
application/vnd.trid.tpt tpt
|
||||
application/vnd.triscape.mxs mxs
|
||||
application/vnd.trueapp tra
|
||||
application/vnd.ufdl ufd ufdl
|
||||
application/vnd.uiq.theme utz
|
||||
application/vnd.umajin umj
|
||||
application/vnd.unity unityweb
|
||||
application/vnd.uoml+xml uoml
|
||||
application/vnd.vcx vcx
|
||||
application/vnd.visio vsd vst vss vsw
|
||||
application/vnd.visionary vis
|
||||
application/vnd.vsf vsf
|
||||
application/vnd.wap.wbxml wbxml
|
||||
application/vnd.wap.wmlc wmlc
|
||||
application/vnd.wap.wmlscriptc wmlsc
|
||||
application/vnd.webturbo wtb
|
||||
application/vnd.wolfram.player nbp
|
||||
application/vnd.wordperfect wpd
|
||||
application/vnd.wqd wqd
|
||||
application/vnd.wt.stf stf
|
||||
application/vnd.xara xar
|
||||
application/vnd.xfdl xfdl
|
||||
application/vnd.yamaha.hv-dic hvd
|
||||
application/vnd.yamaha.hv-script hvs
|
||||
application/vnd.yamaha.hv-voice hvp
|
||||
application/vnd.yamaha.openscoreformat osf
|
||||
application/vnd.yamaha.openscoreformat.osfpvg+xml osfpvg
|
||||
application/vnd.yamaha.smaf-audio saf
|
||||
application/vnd.yamaha.smaf-phrase spf
|
||||
application/vnd.yellowriver-custom-menu cmp
|
||||
application/vnd.zul zir zirz
|
||||
application/vnd.zzazz.deck+xml zaz
|
||||
application/voicexml+xml vxml
|
||||
application/widget wgt
|
||||
application/winhlp hlp
|
||||
application/wsdl+xml wsdl
|
||||
application/wspolicy+xml wspolicy
|
||||
application/x-7z-compressed 7z
|
||||
application/x-abiword abw
|
||||
application/x-ace-compressed ace
|
||||
application/x-apple-diskimage dmg
|
||||
application/x-authorware-bin aab x32 u32 vox
|
||||
application/x-authorware-map aam
|
||||
application/x-authorware-seg aas
|
||||
application/x-bcpio bcpio
|
||||
application/x-bittorrent torrent
|
||||
application/x-blorb blb blorb
|
||||
application/x-bzip bz
|
||||
application/x-bzip2 bz2 boz
|
||||
application/x-cbr cbr cba cbt cbz cb7
|
||||
application/x-cdlink vcd
|
||||
application/x-cfs-compressed cfs
|
||||
application/x-chat chat
|
||||
application/x-chess-pgn pgn
|
||||
application/x-conference nsc
|
||||
application/x-cpio cpio
|
||||
application/x-csh csh
|
||||
application/x-debian-package deb udeb
|
||||
application/x-dgc-compressed dgc
|
||||
application/x-director dir dcr dxr cst cct cxt w3d fgd swa
|
||||
application/x-doom wad
|
||||
application/x-dtbncx+xml ncx
|
||||
application/x-dtbook+xml dtb
|
||||
application/x-dtbresource+xml res
|
||||
application/x-dvi dvi
|
||||
application/x-envoy evy
|
||||
application/x-eva eva
|
||||
application/x-font-bdf bdf
|
||||
application/x-font-ghostscript gsf
|
||||
application/x-font-linux-psf psf
|
||||
application/x-font-pcf pcf
|
||||
application/x-font-snf snf
|
||||
application/x-font-type1 pfa pfb pfm afm
|
||||
application/x-freearc arc
|
||||
application/x-futuresplash spl
|
||||
application/x-gca-compressed gca
|
||||
application/x-glulx ulx
|
||||
application/x-gnumeric gnumeric
|
||||
application/x-gramps-xml gramps
|
||||
application/x-gtar gtar
|
||||
application/x-hdf hdf
|
||||
application/x-install-instructions install
|
||||
application/x-iso9660-image iso
|
||||
application/x-java-jnlp-file jnlp
|
||||
application/x-latex latex
|
||||
application/x-lzh-compressed lzh lha
|
||||
application/x-mie mie
|
||||
application/x-mobipocket-ebook prc mobi
|
||||
application/x-ms-application application
|
||||
application/x-ms-shortcut lnk
|
||||
application/x-ms-wmd wmd
|
||||
application/x-ms-wmz wmz
|
||||
application/x-ms-xbap xbap
|
||||
application/x-msaccess mdb
|
||||
application/x-msbinder obd
|
||||
application/x-mscardfile crd
|
||||
application/x-msclip clp
|
||||
application/x-msdownload exe dll com bat msi
|
||||
application/x-msmediaview mvb m13 m14
|
||||
application/x-msmetafile wmf wmz emf emz
|
||||
application/x-msmoney mny
|
||||
application/x-mspublisher pub
|
||||
application/x-msschedule scd
|
||||
application/x-msterminal trm
|
||||
application/x-mswrite wri
|
||||
application/x-netcdf nc cdf
|
||||
application/x-nzb nzb
|
||||
application/x-pkcs12 p12 pfx
|
||||
application/x-pkcs7-certificates p7b spc
|
||||
application/x-pkcs7-certreqresp p7r
|
||||
application/x-rar-compressed rar
|
||||
application/x-research-info-systems ris
|
||||
application/x-sh sh
|
||||
application/x-shar shar
|
||||
application/x-shockwave-flash swf
|
||||
application/x-silverlight-app xap
|
||||
application/x-sql sql
|
||||
application/x-stuffit sit
|
||||
application/x-stuffitx sitx
|
||||
application/x-subrip srt
|
||||
application/x-sv4cpio sv4cpio
|
||||
application/x-sv4crc sv4crc
|
||||
application/x-t3vm-image t3
|
||||
application/x-tads gam
|
||||
application/x-tar tar
|
||||
application/x-tcl tcl
|
||||
application/x-tex tex
|
||||
application/x-tex-tfm tfm
|
||||
application/x-texinfo texinfo texi
|
||||
application/x-tgif obj
|
||||
application/x-ustar ustar
|
||||
application/x-wais-source src
|
||||
application/x-x509-ca-cert der crt
|
||||
application/x-xfig fig
|
||||
application/x-xliff+xml xlf
|
||||
application/x-xpinstall xpi
|
||||
application/x-xz xz
|
||||
application/x-zmachine z1 z2 z3 z4 z5 z6 z7 z8
|
||||
application/xaml+xml xaml
|
||||
application/xcap-diff+xml xdf
|
||||
application/xenc+xml xenc
|
||||
application/xhtml+xml xhtml xht
|
||||
application/xml xml xsl
|
||||
application/xml-dtd dtd
|
||||
application/xop+xml xop
|
||||
application/xproc+xml xpl
|
||||
application/xslt+xml xslt
|
||||
application/xspf+xml xspf
|
||||
application/xv+xml mxml xhvml xvml xvm
|
||||
application/yang yang
|
||||
application/yin+xml yin
|
||||
application/zip zip
|
||||
audio/adpcm adp
|
||||
audio/basic au snd
|
||||
audio/midi mid midi kar rmi
|
||||
audio/mp4 m4a mp4a
|
||||
audio/mpeg mpga mp2 mp2a mp3 m2a m3a
|
||||
audio/ogg oga ogg spx
|
||||
audio/s3m s3m
|
||||
audio/silk sil
|
||||
audio/vnd.dece.audio uva uvva
|
||||
audio/vnd.digital-winds eol
|
||||
audio/vnd.dra dra
|
||||
audio/vnd.dts dts
|
||||
audio/vnd.dts.hd dtshd
|
||||
audio/vnd.lucent.voice lvp
|
||||
audio/vnd.ms-playready.media.pya pya
|
||||
audio/vnd.nuera.ecelp4800 ecelp4800
|
||||
audio/vnd.nuera.ecelp7470 ecelp7470
|
||||
audio/vnd.nuera.ecelp9600 ecelp9600
|
||||
audio/vnd.rip rip
|
||||
audio/webm weba
|
||||
audio/x-aac aac
|
||||
audio/x-aiff aif aiff aifc
|
||||
audio/x-caf caf
|
||||
audio/x-flac flac
|
||||
audio/x-matroska mka
|
||||
audio/x-mpegurl m3u
|
||||
audio/x-ms-wax wax
|
||||
audio/x-ms-wma wma
|
||||
audio/x-pn-realaudio ram ra
|
||||
audio/x-pn-realaudio-plugin rmp
|
||||
audio/x-wav wav
|
||||
audio/xm xm
|
||||
chemical/x-cdx cdx
|
||||
chemical/x-cif cif
|
||||
chemical/x-cmdf cmdf
|
||||
chemical/x-cml cml
|
||||
chemical/x-csml csml
|
||||
chemical/x-xyz xyz
|
||||
font/collection ttc
|
||||
font/otf otf
|
||||
font/ttf ttf
|
||||
font/woff woff
|
||||
font/woff2 woff2
|
||||
image/bmp bmp
|
||||
image/cgm cgm
|
||||
image/g3fax g3
|
||||
image/gif gif
|
||||
image/ief ief
|
||||
image/jpeg jpeg jpg jpe
|
||||
image/ktx ktx
|
||||
image/png png
|
||||
image/prs.btif btif
|
||||
image/sgi sgi
|
||||
image/svg+xml svg svgz
|
||||
image/tiff tiff tif
|
||||
image/vnd.adobe.photoshop psd
|
||||
image/vnd.dece.graphic uvi uvvi uvg uvvg
|
||||
image/vnd.djvu djvu djv
|
||||
image/vnd.dvb.subtitle sub
|
||||
image/vnd.dwg dwg
|
||||
image/vnd.dxf dxf
|
||||
image/vnd.fastbidsheet fbs
|
||||
image/vnd.fpx fpx
|
||||
image/vnd.fst fst
|
||||
image/vnd.fujixerox.edmics-mmr mmr
|
||||
image/vnd.fujixerox.edmics-rlc rlc
|
||||
image/vnd.ms-modi mdi
|
||||
image/vnd.ms-photo wdp
|
||||
image/vnd.net-fpx npx
|
||||
image/vnd.wap.wbmp wbmp
|
||||
image/vnd.xiff xif
|
||||
image/webp webp
|
||||
image/x-3ds 3ds
|
||||
image/x-cmu-raster ras
|
||||
image/x-cmx cmx
|
||||
image/x-freehand fh fhc fh4 fh5 fh7
|
||||
image/x-icon ico
|
||||
image/x-mrsid-image sid
|
||||
image/x-pcx pcx
|
||||
image/x-pict pic pct
|
||||
image/x-portable-anymap pnm
|
||||
image/x-portable-bitmap pbm
|
||||
image/x-portable-graymap pgm
|
||||
image/x-portable-pixmap ppm
|
||||
image/x-rgb rgb
|
||||
image/x-tga tga
|
||||
image/x-xbitmap xbm
|
||||
image/x-xpixmap xpm
|
||||
image/x-xwindowdump xwd
|
||||
message/rfc822 eml mime
|
||||
model/iges igs iges
|
||||
model/mesh msh mesh silo
|
||||
model/vnd.collada+xml dae
|
||||
model/vnd.dwf dwf
|
||||
model/vnd.gdl gdl
|
||||
model/vnd.gtw gtw
|
||||
model/vnd.mts mts
|
||||
model/vnd.vtu vtu
|
||||
model/vrml wrl vrml
|
||||
model/x3d+binary x3db x3dbz
|
||||
model/x3d+vrml x3dv x3dvz
|
||||
model/x3d+xml x3d x3dz
|
||||
text/cache-manifest appcache
|
||||
text/calendar ics ifb
|
||||
text/css css
|
||||
text/csv csv
|
||||
text/html html htm
|
||||
text/n3 n3
|
||||
text/plain txt text conf def list log in
|
||||
text/prs.lines.tag dsc
|
||||
text/richtext rtx
|
||||
text/sgml sgml sgm
|
||||
text/tab-separated-values tsv
|
||||
text/troff t tr roff man me ms
|
||||
text/turtle ttl
|
||||
text/uri-list uri uris urls
|
||||
text/vcard vcard
|
||||
text/vnd.curl curl
|
||||
text/vnd.curl.dcurl dcurl
|
||||
text/vnd.curl.mcurl mcurl
|
||||
text/vnd.curl.scurl scurl
|
||||
text/vnd.dvb.subtitle sub
|
||||
text/vnd.fly fly
|
||||
text/vnd.fmi.flexstor flx
|
||||
text/vnd.graphviz gv
|
||||
text/vnd.in3d.3dml 3dml
|
||||
text/vnd.in3d.spot spot
|
||||
text/vnd.sun.j2me.app-descriptor jad
|
||||
text/vnd.wap.wml wml
|
||||
text/vnd.wap.wmlscript wmls
|
||||
text/x-asm s asm
|
||||
text/x-c c cc cxx cpp h hh dic
|
||||
text/x-fortran f for f77 f90
|
||||
text/x-java-source java
|
||||
text/x-nfo nfo
|
||||
text/x-opml opml
|
||||
text/x-pascal p pas
|
||||
text/x-setext etx
|
||||
text/x-sfv sfv
|
||||
text/x-uuencode uu
|
||||
text/x-vcalendar vcs
|
||||
text/x-vcard vcf
|
||||
video/3gpp 3gp
|
||||
video/3gpp2 3g2
|
||||
video/h261 h261
|
||||
video/h263 h263
|
||||
video/h264 h264
|
||||
video/jpeg jpgv
|
||||
video/jpm jpm jpgm
|
||||
video/mj2 mj2 mjp2
|
||||
video/mp4 mp4 mp4v mpg4
|
||||
video/mpeg mpeg mpg mpe m1v m2v
|
||||
video/ogg ogv
|
||||
video/quicktime qt mov
|
||||
video/vnd.dece.hd uvh uvvh
|
||||
video/vnd.dece.mobile uvm uvvm
|
||||
video/vnd.dece.pd uvp uvvp
|
||||
video/vnd.dece.sd uvs uvvs
|
||||
video/vnd.dece.video uvv uvvv
|
||||
video/vnd.dvb.file dvb
|
||||
video/vnd.fvt fvt
|
||||
video/vnd.mpegurl mxu m4u
|
||||
video/vnd.ms-playready.media.pyv pyv
|
||||
video/vnd.uvvu.mp4 uvu uvvu
|
||||
video/vnd.vivo viv
|
||||
video/webm webm
|
||||
video/x-f4v f4v
|
||||
video/x-fli fli
|
||||
video/x-flv flv
|
||||
video/x-m4v m4v
|
||||
video/x-matroska mkv mk3d mks
|
||||
video/x-mng mng
|
||||
video/x-ms-asf asf asx
|
||||
video/x-ms-vob vob
|
||||
video/x-ms-wm wm
|
||||
video/x-ms-wmv wmv
|
||||
video/x-ms-wmx wmx
|
||||
video/x-ms-wvx wvx
|
||||
video/x-msvideo avi
|
||||
video/x-sgi-movie movie
|
||||
video/x-smv smv
|
||||
x-conference/x-cooltalk ice
|
||||
20
runtime/src/server/middleware/mime.ts
Normal file
20
runtime/src/server/middleware/mime.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import mime_raw from './mime-types.md';
|
||||
|
||||
const map: Map<string, string> = new Map();
|
||||
|
||||
mime_raw.split('\n').forEach((row: string) => {
|
||||
const match = /(.+?)\t+(.+)/.exec(row);
|
||||
if (!match) return;
|
||||
|
||||
const type = match[1];
|
||||
const extensions = match[2].split(' ');
|
||||
|
||||
extensions.forEach(ext => {
|
||||
map.set(ext, type);
|
||||
});
|
||||
});
|
||||
|
||||
export function lookup(file: string) {
|
||||
const match = /\.([^\.]+)$/.exec(file);
|
||||
return match && map.get(match[1]);
|
||||
}
|
||||
63
runtime/src/server/middleware/types.ts
Normal file
63
runtime/src/server/middleware/types.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { ClientRequest, ServerResponse } from 'http';
|
||||
|
||||
export type ServerRoute = {
|
||||
pattern: RegExp;
|
||||
handlers: Record<string, Handler>;
|
||||
params: (match: RegExpMatchArray) => Record<string, string>;
|
||||
};
|
||||
|
||||
export type Page = {
|
||||
pattern: RegExp;
|
||||
parts: Array<{
|
||||
name: string;
|
||||
component: Component;
|
||||
params?: (match: RegExpMatchArray) => Record<string, string>;
|
||||
preload?: (data: any) => any | Promise<any>;
|
||||
}>
|
||||
};
|
||||
|
||||
export type Manifest = {
|
||||
server_routes: ServerRoute[];
|
||||
pages: Page[];
|
||||
root: Component;
|
||||
root_preload?: (data: any) => any | Promise<any>;
|
||||
error: Component;
|
||||
}
|
||||
|
||||
export type Handler = (req: Req, res: Res, next: () => void) => void;
|
||||
|
||||
export type Props = {
|
||||
error?: { message: string };
|
||||
status?: number;
|
||||
child: {
|
||||
segment: string;
|
||||
component: Component;
|
||||
props: Props;
|
||||
};
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export interface Req extends ClientRequest {
|
||||
url: string;
|
||||
baseUrl: string;
|
||||
originalUrl: string;
|
||||
method: string;
|
||||
path: string;
|
||||
params: Record<string, string>;
|
||||
query: Record<string, string>;
|
||||
headers: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface Res extends ServerResponse {
|
||||
write: (data: any) => void;
|
||||
}
|
||||
|
||||
export { ServerResponse };
|
||||
|
||||
interface Component {
|
||||
render: (data: any) => {
|
||||
head: string;
|
||||
css: { code: string, map: any };
|
||||
html: string
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user