Compare commits

...

14 Commits

Author SHA1 Message Date
Rich Harris
03c5f5b446 -> v0.24.0 2018-10-27 12:40:27 -04:00
Rich Harris
6655b1b49d Merge pull request #503 from sveltejs/gh-490
redirect to external URLs
2018-10-27 12:36:10 -04:00
Rich Harris
eebf076f23 Merge pull request #501 from sveltejs/gh-497
consistent query parameter handling between client and server
2018-10-27 12:35:58 -04:00
Rich Harris
198be28f4b Merge pull request #504 from sveltejs/gh-480
change scroll[X|Y] to page[X|Y]Offset
2018-10-27 12:35:47 -04:00
Rich Harris
4f720446b2 change scroll[X|Y] to page[X|Y]Offset - closes #480 2018-10-27 12:26:53 -04:00
Rich Harris
e69cb3639a redirect to external URLs - closes #490 2018-10-27 12:14:28 -04:00
Rich Harris
1b1a86764f Merge pull request #502 from sveltejs/gh-495
strip leading slash from basepath
2018-10-27 11:50:23 -04:00
Rich Harris
f50f83c4a4 strip leading slash from basepath - fixes #495 2018-10-27 11:41:02 -04:00
Rich Harris
eadefd996b consistent query parameter handling between client and server - fixes #497 2018-10-27 11:36:18 -04:00
Rich Harris
ab52aabd1d Merge pull request #500 from sveltejs/dont-buffer-logs
don't buffer stdout/stderr
2018-10-27 11:18:50 -04:00
Rich Harris
c5a80543b3 reduce flakiness of unrelated test 2018-10-27 11:12:10 -04:00
Rich Harris
cfd95ac024 dont buffer stdout/stderr 2018-10-27 11:02:34 -04:00
Rich Harris
73ff95c677 Merge pull request #498 from mrkishi/build-dir
Add missing posixify to `build_dir`
2018-10-27 10:17:58 -04:00
mrkishi
382fe6b7b9 Add missing posixify 2018-10-26 11:03:29 -03:00
19 changed files with 129 additions and 50 deletions

View File

@@ -1,5 +1,14 @@
# sapper changelog
## 0.24.0
* Handle external URLs in `this.redirect` ([#490](https://github.com/sveltejs/sapper/issues/490))
* Strip leading `/` from basepath ([#495](https://github.com/sveltejs/sapper/issues/495))
* Treat duplicate query string parameters as arrays ([#497](https://github.com/sveltejs/sapper/issues/497))
* Don't buffer `stdout` and `stderr` ([#305](https://github.com/sveltejs/sapper/issues/305))
* Posixify `build_dir` ([#498](https://github.com/sveltejs/sapper/pull/498))
* Use `page[XY]Offset` instead of `scroll[XY]` ([#480](https://github.com/sveltejs/sapper/issues/480))
## 0.23.5
* Include lazily-imported CSS in main CSS chunk ([#492](https://github.com/sveltejs/sapper/pull/492))

View File

@@ -1,6 +1,6 @@
{
"name": "sapper",
"version": "0.23.5",
"version": "0.24.0",
"description": "Military-grade apps, engineered by Svelte",
"bin": {
"sapper": "./sapper"

View File

@@ -214,12 +214,9 @@ class Watcher extends EventEmitter {
// TODO watch the configs themselves?
const compilers: Compilers = await create_compilers(this.bundler, cwd, src, dest, false);
let log = '';
const emitFatal = () => {
this.emit('fatal', <FatalEvent>{
message: `Server crashed`,
log
message: `Server crashed`
});
this.crashed = true;
@@ -236,7 +233,6 @@ class Watcher extends EventEmitter {
handle_result: (result: CompileResult) => {
deferred.promise.then(() => {
const restart = () => {
log = '';
this.crashed = false;
ports.wait(this.port)
@@ -260,8 +256,7 @@ class Watcher extends EventEmitter {
if (this.crashed) return;
this.emit('fatal', <FatalEvent>{
message: `Server is not listening on port ${this.port}`,
log
message: `Server is not listening on port ${this.port}`
});
});
};
@@ -292,12 +287,10 @@ class Watcher extends EventEmitter {
});
this.proc.stdout.on('data', chunk => {
log += chunk;
this.emit('stdout', chunk);
});
this.proc.stderr.on('data', chunk => {
log += chunk;
this.emit('stderr', chunk);
});

View File

@@ -38,6 +38,8 @@ async function _export({
oninfo = noop,
onfile = noop
}: Opts = {}) {
basepath = basepath.replace(/^\//, '')
cwd = path.resolve(cwd);
static_files = path.resolve(cwd, static_files);
build_dir = path.resolve(cwd, build_dir);

View File

@@ -64,6 +64,14 @@ prog.command('dev')
let first = true;
watcher.on('stdout', data => {
process.stdout.write(data);
});
watcher.on('stderr', data => {
process.stderr.write(data);
});
watcher.on('ready', async (event: ReadyEvent) => {
if (first) {
console.log(colors.bold.cyan(`> Listening on http://localhost:${event.port}`));
@@ -73,16 +81,6 @@ prog.command('dev')
}
first = false;
}
// TODO clear screen?
event.process.stdout.on('data', data => {
process.stdout.write(data);
});
event.process.stderr.on('data', data => {
process.stderr.write(data);
});
});
watcher.on('invalid', (event: InvalidEvent) => {

View File

@@ -226,8 +226,8 @@ function generate_server(
error
};`.replace(/^\t\t/gm, '').trim();
const build_dir = path.relative(cwd, dest);
const src_dir = path.relative(cwd, src);
const build_dir = posixify(path.relative(cwd, dest));
const src_dir = posixify(path.relative(cwd, src));
return `// This file is generated by Sapper — do not edit it!\n` + template
.replace('__BUILD__DIR__', JSON.stringify(build_dir))
@@ -242,4 +242,4 @@ function get_file(path_to_routes: string, component: PageComponent) {
}
return posixify(`${path_to_routes}/${component.file}`);
}
}

View File

@@ -69,7 +69,6 @@ export type ErrorEvent = {
export type FatalEvent = {
message: string;
log?: string;
};
export type InvalidEvent = {

View File

@@ -87,11 +87,14 @@ export function select_route(url: URL): Target {
const match = page.pattern.exec(path);
if (match) {
const query: Record<string, string | true> = {};
const query: Record<string, string | string[]> = Object.create(null);
if (url.search.length > 0) {
url.search.slice(1).split('&').forEach(searchParam => {
const [, key, value] = /([^=]*)(?:=(.*))?/.exec(searchParam);
query[decodeURIComponent(key)] = decodeURIComponent((value || '').replace(/\+/g, ' '));
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].push(value);
else query[key] = value;
});
}
return { url, path, page, match, query };
@@ -101,8 +104,8 @@ export function select_route(url: URL): Target {
export function scroll_state() {
return {
x: scrollX,
y: scrollY
x: pageXOffset,
y: pageYOffset
};
}

View File

@@ -55,7 +55,7 @@ export type Target = {
path: string;
page: Page;
match: RegExpExecArray;
query: Record<string, string | true>;
query: Record<string, string | string[]>;
};
export type Redirect = {

View File

@@ -3,7 +3,7 @@ import * as path from 'path';
import cookie from 'cookie';
import devalue from 'devalue';
import fetch from 'node-fetch';
import { URL } from 'url';
import { URL, resolve } from 'url';
import { build_dir, dev, src_dir, IGNORE } from '../placeholders';
import { Manifest, Page, Props, Req, Res, Store } from './types';
@@ -160,7 +160,7 @@ export function get_page_handler(
try {
if (redirect) {
const location = `${req.baseUrl}/${redirect.location}`;
const location = resolve(req.baseUrl || '/', redirect.location);
res.statusCode = redirect.statusCode;
res.setHeader('Location', location);

View File

@@ -50,6 +50,20 @@ export class AppRunner {
}
});
await this.page.setRequestInterception(true);
this.page.on('request', interceptedRequest => {
if (/example\.com/.test(interceptedRequest.url())) {
interceptedRequest.respond({
status: 200,
contentType: 'text/html',
body: `<h1>external</h1>`
});
} else {
interceptedRequest.continue();
}
});
return {
page: this.page,
base: `http://localhost:${this.port}`,

View File

@@ -1,9 +1 @@
<h1>message: "{message}"</h1>
<script>
export default {
preload({ query }) {
return query;
}
};
</script>
<h1>{JSON.stringify(query)}</h1>

View File

@@ -3,5 +3,6 @@
<a href="a">a</a>
<a href="ambiguous/ok.json">ok</a>
<a href="echo-query?message">ok</a>
<a href="echo-query?p=one&p=two">ok</a>
<div class='hydrate-test'></div>

View File

@@ -204,7 +204,7 @@ describe('basics', function() {
assert.equal(
await title(),
'message: ""'
'{"message":""}'
);
});
@@ -217,7 +217,29 @@ describe('basics', function() {
assert.equal(
await title(),
'message: ""'
'{"message":""}'
);
});
it('accepts duplicated query string parameter on server', async () => {
await page.goto(`${base}/echo-query?p=one&p=two`);
assert.equal(
await title(),
'{"p":["one","two"]}'
);
});
it('accepts duplicated query string parameter on client', async () => {
await page.goto(base);
await start();
await prefetchRoutes();
await page.click('a[href="echo-query?p=one&p=two"]')
assert.equal(
await title(),
'{"p":["one","two"]}'
);
});

View File

@@ -127,9 +127,11 @@ describe('errors', function() {
await prefetchRoutes();
await page.click('[href="enhance-your-calm"]');
await wait(50);
assert.equal(await title(), '420');
await page.goBack();
await wait(50);
assert.equal(await title(), 'No error here');
});
});

View File

@@ -1,4 +1,5 @@
<h1>root</h1>
<a href="redirect-from">redirect from</a>
<a href="redirect-to-root">redirect to root</a>
<a href="redirect-to-root">redirect to root</a>
<a href="redirect-to-external">redirect to external</a>

View File

@@ -0,0 +1,9 @@
<h1>unredirected</h1>
<script>
export default {
preload() {
this.redirect(301, 'https://example.com');
}
};
</script>

View File

@@ -14,13 +14,14 @@ describe('redirects', function() {
// helpers
let start: () => Promise<void>;
let prefetchRoutes: () => Promise<void>;
let title: () => Promise<string>;
// hooks
before(async () => {
await build({ cwd: __dirname });
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
({ base, page, start, prefetchRoutes } = await runner.start());
({ base, page, start, prefetchRoutes, title } = await runner.start());
});
after(() => runner.end());
@@ -34,7 +35,7 @@ describe('redirects', function() {
);
assert.equal(
await page.$eval('h1', node => node.textContent),
await title(),
'redirected'
);
});
@@ -53,7 +54,7 @@ describe('redirects', function() {
);
assert.equal(
await page.$eval('h1', node => node.textContent),
await title(),
'redirected'
);
});
@@ -67,7 +68,7 @@ describe('redirects', function() {
);
assert.equal(
await page.$eval('h1', node => node.textContent),
await title(),
'root'
);
});
@@ -86,8 +87,41 @@ describe('redirects', function() {
);
assert.equal(
await page.$eval('h1', node => node.textContent),
await title(),
'root'
);
});
it('redirects to external URL on server', async () => {
await page.goto(`${base}/redirect-to-external`);
assert.equal(
page.url(),
`https://example.com/`
);
assert.equal(
await title(),
'external'
);
});
it('redirects to external URL in client', async () => {
await page.goto(base);
await start();
await prefetchRoutes();
await page.click('[href="redirect-to-external"]');
await wait(50);
assert.equal(
page.url(),
`https://example.com/`
);
assert.equal(
await title(),
'external'
);
});
});

View File

@@ -17,7 +17,7 @@ describe('with-basepath', function() {
await api.export({
cwd: __dirname,
basepath: 'custom-basepath'
basepath: '/custom-basepath'
});
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');