Merge branch 'master' into collision

This commit is contained in:
Rich Harris
2018-05-04 17:05:18 -04:00
committed by GitHub
21 changed files with 105 additions and 49 deletions

View File

@@ -1,5 +1,11 @@
# sapper changelog
## 0.11.0
* Create launcher file ([#240](https://github.com/sveltejs/sapper/issues/240))
* Only keep necessary parts of webpack stats ([#251](https://github.com/sveltejs/sapper/pull/251))
* Allow `NODE_ENV` to be overridden when building ([#241](https://github.com/sveltejs/sapper/issues/241))
## 0.10.7
* Allow routes to have a leading `.` ([#243](https://github.com/sveltejs/sapper/pull/243))

View File

@@ -1,6 +1,6 @@
{
"name": "sapper",
"version": "0.10.7",
"version": "0.11.0",
"description": "Military-grade apps, engineered by Svelte",
"main": "dist/middleware.ts.js",
"bin": {
@@ -34,35 +34,35 @@
"rimraf": "^2.6.2",
"sade": "^1.4.0",
"sander": "^0.6.0",
"source-map-support": "^0.5.4",
"source-map-support": "^0.5.5",
"tslib": "^1.9.0",
"url-parse": "^1.2.0",
"webpack-format-messages": "^1.0.2"
},
"devDependencies": {
"@std/esm": "^0.25.3",
"@types/glob": "^5.0.34",
"@types/mkdirp": "^0.5.2",
"@types/rimraf": "^2.0.2",
"compression": "^1.7.1",
"eslint": "^4.13.1",
"eslint-plugin-import": "^2.8.0",
"esm": "^3.0.28",
"express": "^4.16.3",
"get-port": "^3.2.0",
"mocha": "^5.0.4",
"nightmare": "^3.0.0",
"npm-run-all": "^4.1.2",
"polka": "^0.3.4",
"rollup": "^0.57.0",
"rollup-plugin-commonjs": "^9.1.0",
"rollup": "^0.58.2",
"rollup-plugin-commonjs": "^9.1.3",
"rollup-plugin-json": "^2.3.0",
"rollup-plugin-string": "^2.0.2",
"rollup-plugin-typescript": "^0.8.1",
"serve-static": "^1.13.2",
"svelte": "^1.57.4",
"svelte-loader": "^2.5.1",
"ts-node": "^5.0.1",
"typescript": "^2.6.2",
"svelte": "^2.4.4",
"svelte-loader": "^2.9.0",
"ts-node": "^6.0.2",
"typescript": "^2.8.3",
"walk-sync": "^0.3.2",
"webpack": "^4.1.0"
},

View File

@@ -21,10 +21,12 @@ prog.command('dev')
prog.command('build [dest]')
.describe('Create a production-ready version of your app')
.action(async (dest = 'build') => {
.option('-p, --port', 'Default of process.env.PORT', '3000')
.example(`build custom-dir -p 4567`)
.action(async (dest = 'build', opts: { port: string }) => {
console.log(`> Building...`);
process.env.NODE_ENV = 'production';
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
process.env.SAPPER_DEST = dest;
const start = Date.now();
@@ -32,7 +34,20 @@ prog.command('build [dest]')
try {
const { build } = await import('./cli/build');
await build();
console.error(`\n> Finished in ${elapsed(start)}. Type ${clorox.bold.cyan(dest === 'build' ? 'npx sapper start' : `npx sapper start ${dest}`)} to run the app.`);
const launcher = path.resolve(dest, 'index.js');
fs.writeFileSync(launcher, `
// generated by sapper build at ${new Date().toISOString()}
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
process.env.SAPPER_DEST = __dirname;
process.env.PORT = process.env.PORT || ${opts.port || 3000};
console.log('Starting server on port ' + process.env.PORT);
require('./server.js');
`.replace(/^\t+/gm, '').trim());
console.error(`\n> Finished in ${elapsed(start)}. Type ${clorox.bold.cyan(`node ${dest}`)} to run the app.`);
} catch (err) {
console.error(err ? err.details || err.stack || err.message || err : 'Unknown error');
}

View File

@@ -35,7 +35,9 @@ export async function build() {
const client_stats = await compile(client);
console.log(`${clorox.inverse(`\nbuilt client`)}`);
console.log(client_stats.toString({ colors: true }));
fs.writeFileSync(path.join(output, 'client_info.json'), JSON.stringify(client_stats.toJson()));
fs.writeFileSync(path.join(output, 'client_info.json'), JSON.stringify({
assets: client_stats.toJson().assetsByChunkName
}));
const server_stats = await compile(server);
console.log(`${clorox.inverse(`\nbuilt server`)}`);

View File

@@ -263,7 +263,9 @@ export async function dev(opts: { port: number, open: boolean }) {
},
result: info => {
fs.writeFileSync(path.join(dir, 'client_info.json'), JSON.stringify(info, null, ' '));
fs.writeFileSync(path.join(dir, 'client_info.json'), JSON.stringify({
assets: info.assetsByChunkName
}, null, ' '));
deferreds.client.fulfil();
const client_files = info.assets.map((chunk: { name: string }) => `client/${chunk.name}`);

View File

@@ -7,6 +7,8 @@ export default function create_routes({ files } = { files: glob.sync('**/*.*', {
const routes: Route[] = files
.filter((file: string) => !/(^|\/|\\)_/.test(file))
.map((file: string) => {
if (/(^|\/|\\)(_|\.(?!well-known))/.test(file)) return;
if (/]\[/.test(file)) {
throw new Error(`Invalid route ${file} — parameters must be separated`);
}

View File

@@ -90,7 +90,7 @@ export default function middleware({ routes, store }: {
cache_control: 'max-age=31536000'
}),
get_route_handler(client_info.assetsByChunkName, routes, store)
get_route_handler(client_info.assets, routes, store)
].filter(Boolean));
return middleware;

View File

@@ -214,7 +214,7 @@ let prefetching: {
export function prefetch(href: string) {
const selected = select_route(new URL(href, document.baseURI));
if (selected) {
if (selected && (!prefetching || href !== prefetching.href)) {
prefetching = {
href,
promise: selected.route.load().then(mod => prepare_route(mod.default, selected.data))
@@ -222,7 +222,16 @@ export function prefetch(href: string) {
}
}
function handle_touchstart_mouseover(event: MouseEvent | TouchEvent) {
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>findAnchor(<Node>event.target);
if (!a || a.rel !== 'prefetch') return;
@@ -248,8 +257,8 @@ export function init(_target: Node, _routes: Route[], opts?: { store?: (data: an
window.addEventListener('popstate', handle_popstate);
// prefetch
window.addEventListener('touchstart', handle_touchstart_mouseover);
window.addEventListener('mouseover', handle_touchstart_mouseover);
window.addEventListener('touchstart', trigger_prefetch);
window.addEventListener('mousemove', handle_mousemove);
inited = true;
}

View File

@@ -1,6 +1,6 @@
<:Head>
<title>{{status}}</title>
</:Head>
<svelte:head>
<title>{status}</title>
</svelte:head>
<h1>Not found</h1>
<p>{{error.message}}</p>
<p>{error.message}</p>

View File

@@ -1,6 +1,6 @@
<:Head>
<svelte:head>
<title>Internal server error</title>
</:Head>
</svelte:head>
<h1>Internal server error</h1>
<p>{{error.message}}</p>
<p>{error.message}</p>

View File

@@ -1,6 +1,6 @@
<:Head>
<svelte:head>
<title>About</title>
</:Head>
</svelte:head>
<h1>About this site</h1>

View File

@@ -1,11 +1,11 @@
<:Head>
<title>{{post.title}}</title>
</:Head>
<svelte:head>
<title>{post.title}</title>
</svelte:head>
<h1>{{post.title}}</h1>
<h1>{post.title}</h1>
<div class='content'>
{{{post.html}}}
{@html post.html}
</div>
<script>

View File

@@ -1,17 +1,17 @@
<:Head>
<svelte:head>
<title>Blog</title>
</:Head>
</svelte:head>
<h1>Recent posts</h1>
<ul>
{{#each posts as post}}
{#each posts as post}
<!-- we're using the non-standard `rel=prefetch` attribute to
tell Sapper to load the data for the page as soon as
the user hovers over the link or taps it, instead of
waiting for the 'click' event -->
<li><a rel='prefetch' href='blog/{{post.slug}}'>{{post.title}}</a></li>
{{/each}}
<li><a rel='prefetch' href='blog/{post.slug}'>{post.title}</a></li>
{/each}
</ul>
<script>

View File

@@ -1,4 +1,4 @@
<h1>{{message}}</h1>
<h1>{message}</h1>
<script>
export default {

View File

@@ -1,6 +1,6 @@
<:Head>
<svelte:head>
<title>Sapper project template</title>
</:Head>
</svelte:head>
<h1>Great success!</h1>
@@ -11,7 +11,7 @@
<a href='blog/nope'>broken link</a>
<a href='blog/throw-an-error'>error link</a>
<a href='credentials?creds=include'>credentials</a>
<a rel=prefetch class='{{page === "blog" ? "selected" : ""}}' href='blog'>blog</a>
<a rel=prefetch class='{page === "blog" ? "selected" : ""}' href='blog'>blog</a>
<div class='hydrate-test'></div>

View File

@@ -1,4 +1,4 @@
<h1>{{foo.bar()}}</h1>
<h1>{foo.bar()}</h1>
<script>
export default {

View File

@@ -1,4 +1,4 @@
<h1>{{set.has('x')}}</h1>
<h1>{set.has('x')}</h1>
<script>
export default {

View File

@@ -1,4 +1,4 @@
<p>URL is {{url}}</p>
<p>URL is {url}</p>
<script>
export default {

View File

@@ -1 +1 @@
<h1>{{$title}}</h1>
<h1>{$title}</h1>

View File

@@ -1,3 +1,4 @@
const fs = require('fs');
const path = require('path');
const assert = require('assert');
const Nightmare = require('nightmare');
@@ -38,6 +39,7 @@ describe('sapper', function() {
rimraf.sync('export');
rimraf.sync('build');
rimraf.sync('.sapper');
rimraf.sync('start.js');
this.timeout(process.env.CI ? 30000 : 10000);
@@ -148,7 +150,7 @@ function run({ mode, basepath = '' }) {
before(() => {
const promise = mode === 'production'
? exec(`node ${cli} build`).then(() => ports.find(3000))
? exec(`node ${cli} build -l`).then(() => ports.find(3000))
: ports.find(3000).then(port => {
exec(`node ${cli} dev`);
return ports.wait(port).then(() => port);
@@ -160,6 +162,10 @@ function run({ mode, basepath = '' }) {
const dir = mode === 'production' ? 'build' : '.sapper';
if (mode === 'production') {
assert.ok(fs.existsSync('build/index.js'));
}
proc = require('child_process').fork(`${dir}/server.js`, {
cwd: process.cwd(),
env: {
@@ -301,14 +307,17 @@ function run({ mode, basepath = '' }) {
});
});
it('reuses prefetch promise', () => {
it.skip('reuses prefetch promise', () => {
return nightmare
.goto(`${base}/blog`)
.init()
.then(() => {
return capture(() => {
return nightmare
.mouseover('[href="blog/what-is-sapper"]')
.evaluate(() => {
const a = document.querySelector('[href="blog/what-is-sapper"]');
a.dispatchEvent(new MouseEvent('mousemove'));
})
.wait(200);
});
})

View File

@@ -172,6 +172,17 @@ describe('create_routes', () => {
);
});
it('ignores files and directories with leading dots except .well-known', () => {
const routes = create_routes({
files: ['.well-known', '.unknown']
});
assert.deepEqual(
routes.map(r => r.file),
['.well-known']
);
});
it('matches /foo/:bar before /:baz/qux', () => {
const a = create_routes({
files: ['foo/[bar].html', '[baz]/qux.html']