mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-13 19:45:26 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f66c7dcb0d | ||
|
|
06f1a0e6c0 | ||
|
|
7726325b4b | ||
|
|
02cef046aa | ||
|
|
c2aeac34b6 | ||
|
|
abd2f7fd39 | ||
|
|
e7cf9bf1b6 | ||
|
|
1fdd0e3ad2 | ||
|
|
af0a7e04f9 | ||
|
|
ed19a19fed | ||
|
|
8ebfcc9a54 | ||
|
|
af2a792508 | ||
|
|
14e809af6e | ||
|
|
03c5f5b446 | ||
|
|
6655b1b49d | ||
|
|
eebf076f23 | ||
|
|
198be28f4b | ||
|
|
4f720446b2 | ||
|
|
e69cb3639a | ||
|
|
1b1a86764f | ||
|
|
f50f83c4a4 | ||
|
|
eadefd996b | ||
|
|
ab52aabd1d | ||
|
|
c5a80543b3 | ||
|
|
cfd95ac024 | ||
|
|
73ff95c677 | ||
|
|
382fe6b7b9 |
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,5 +1,23 @@
|
||||
# sapper changelog
|
||||
|
||||
## 0.24.2
|
||||
|
||||
* Support Rollup 1.0 ([#541](https://github.com/sveltejs/sapper/pull/541))
|
||||
|
||||
## 0.24.1
|
||||
|
||||
* Include CSS chunks in webpack build info to avoid duplication ([#529](https://github.com/sveltejs/sapper/pull/529))
|
||||
* Fix preload `as` for styles ([#530](https://github.com/sveltejs/sapper/pull/530))
|
||||
|
||||
## 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))
|
||||
|
||||
127
package-lock.json
generated
127
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sapper",
|
||||
"version": "0.23.1",
|
||||
"version": "0.24.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -1702,12 +1702,6 @@
|
||||
"integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
|
||||
"dev": true
|
||||
},
|
||||
"duplexer": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
|
||||
"integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
|
||||
"dev": true
|
||||
},
|
||||
"duplexify": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz",
|
||||
@@ -2043,22 +2037,6 @@
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
|
||||
"dev": true
|
||||
},
|
||||
"event-stream": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.6.tgz",
|
||||
"integrity": "sha512-dGXNg4F/FgVzlApjzItL+7naHutA3fDqbV/zAZqDDlXTjiMnQmZKu+prImWKszeBM5UQeGvAl3u1wBiKeDh61g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"duplexer": "^0.1.1",
|
||||
"flatmap-stream": "^0.1.0",
|
||||
"from": "^0.1.7",
|
||||
"map-stream": "0.0.7",
|
||||
"pause-stream": "^0.0.11",
|
||||
"split": "^1.0.1",
|
||||
"stream-combiner": "^0.2.2",
|
||||
"through": "^2.3.8"
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
||||
@@ -2281,12 +2259,6 @@
|
||||
"write": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"flatmap-stream": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/flatmap-stream/-/flatmap-stream-0.1.1.tgz",
|
||||
"integrity": "sha512-lAq4tLbm3sidmdCN8G3ExaxH7cUCtP5mgDvrYowsx84dcYkJJ4I28N7gkxA6+YlSXzaGLJYIDEi9WGfXzMiXdw==",
|
||||
"dev": true
|
||||
},
|
||||
"flush-write-stream": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz",
|
||||
@@ -2321,12 +2293,6 @@
|
||||
"map-cache": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"from": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
|
||||
"integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=",
|
||||
"dev": true
|
||||
},
|
||||
"from2": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
|
||||
@@ -2396,12 +2362,14 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -2416,17 +2384,20 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@@ -2543,7 +2514,8 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@@ -2555,6 +2527,7 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@@ -2569,6 +2542,7 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@@ -2576,12 +2550,14 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.2.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.1",
|
||||
"yallist": "^3.0.0"
|
||||
@@ -2600,6 +2576,7 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@@ -2680,7 +2657,8 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@@ -2692,6 +2670,7 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -2813,6 +2792,7 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@@ -3648,12 +3628,6 @@
|
||||
"integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
|
||||
"dev": true
|
||||
},
|
||||
"map-stream": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
|
||||
"integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=",
|
||||
"dev": true
|
||||
},
|
||||
"map-visit": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
|
||||
@@ -4059,17 +4033,17 @@
|
||||
}
|
||||
},
|
||||
"npm-run-all": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.3.tgz",
|
||||
"integrity": "sha512-aOG0N3Eo/WW+q6sUIdzcV2COS8VnTZCmdji0VQIAZF3b+a3YWb0AD0vFIyjKec18A7beLGbaQ5jFTNI2bPt9Cg==",
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
|
||||
"integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.0",
|
||||
"chalk": "^2.1.0",
|
||||
"cross-spawn": "^6.0.4",
|
||||
"ansi-styles": "^3.2.1",
|
||||
"chalk": "^2.4.1",
|
||||
"cross-spawn": "^6.0.5",
|
||||
"memorystream": "^0.3.1",
|
||||
"minimatch": "^3.0.4",
|
||||
"ps-tree": "^1.1.0",
|
||||
"pidtree": "^0.3.0",
|
||||
"read-pkg": "^3.0.0",
|
||||
"shell-quote": "^1.6.1",
|
||||
"string.prototype.padend": "^3.0.0"
|
||||
@@ -4399,15 +4373,6 @@
|
||||
"pify": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"pause-stream": {
|
||||
"version": "0.0.11",
|
||||
"resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
|
||||
"integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"through": "~2.3"
|
||||
}
|
||||
},
|
||||
"pbkdf2": {
|
||||
"version": "3.0.17",
|
||||
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
|
||||
@@ -4427,6 +4392,12 @@
|
||||
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
|
||||
"dev": true
|
||||
},
|
||||
"pidtree": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz",
|
||||
"integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==",
|
||||
"dev": true
|
||||
},
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
@@ -4539,15 +4510,6 @@
|
||||
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
|
||||
"dev": true
|
||||
},
|
||||
"ps-tree": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.0.tgz",
|
||||
"integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"event-stream": "~3.3.0"
|
||||
}
|
||||
},
|
||||
"pseudomap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||
@@ -5665,15 +5627,6 @@
|
||||
"integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==",
|
||||
"dev": true
|
||||
},
|
||||
"split": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
|
||||
"integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"through": "2"
|
||||
}
|
||||
},
|
||||
"split-string": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
|
||||
@@ -5729,16 +5682,6 @@
|
||||
"readable-stream": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"stream-combiner": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz",
|
||||
"integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"duplexer": "~0.1.1",
|
||||
"through": "~2.3.4"
|
||||
}
|
||||
},
|
||||
"stream-each": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sapper",
|
||||
"version": "0.23.5",
|
||||
"version": "0.24.2",
|
||||
"description": "Military-grade apps, engineered by Svelte",
|
||||
"bin": {
|
||||
"sapper": "./sapper"
|
||||
@@ -40,7 +40,7 @@
|
||||
"mkdirp": "^0.5.1",
|
||||
"mocha": "^5.2.0",
|
||||
"node-fetch": "^2.2.0",
|
||||
"npm-run-all": "^4.1.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"polka": "^0.5.1",
|
||||
"port-authority": "^1.0.5",
|
||||
"pretty-bytes": "^5.1.0",
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
18
src/cli.ts
18
src/cli.ts
@@ -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) => {
|
||||
|
||||
@@ -142,12 +142,14 @@ export default class RollupCompiler {
|
||||
|
||||
const bundle = await rollup.rollup({
|
||||
input,
|
||||
inlineDynamicImports: true,
|
||||
external: (id: string) => {
|
||||
return (id[0] !== '.' && !path.isAbsolute(id)) || id.slice(-5, id.length) === '.json';
|
||||
}
|
||||
});
|
||||
|
||||
const { code } = await bundle.generate({ format: 'cjs' });
|
||||
const resp = await bundle.generate({ format: 'cjs' });
|
||||
const { code } = resp.output ? resp.output[0] : resp;
|
||||
|
||||
// temporarily override require
|
||||
const defaultLoader = require.extensions['.js'];
|
||||
|
||||
@@ -55,14 +55,25 @@ export default class WebpackResult implements CompileResult {
|
||||
}
|
||||
|
||||
to_json(manifest_data: ManifestData, dirs: Dirs): BuildInfo {
|
||||
const extract_css = (assets: string[] | string) => {
|
||||
assets = Array.isArray(assets) ? assets : [assets];
|
||||
return assets.find(asset => /\.css$/.test(asset));
|
||||
};
|
||||
|
||||
return {
|
||||
bundler: 'webpack',
|
||||
shimport: null, // webpack has its own loader
|
||||
assets: this.assets,
|
||||
css: {
|
||||
// TODO
|
||||
main: null,
|
||||
chunks: {}
|
||||
main: extract_css(this.assets.main),
|
||||
chunks: Object
|
||||
.keys(this.assets)
|
||||
.filter(chunkName => chunkName !== 'main')
|
||||
.reduce((chunks: { [key: string]: string }, chukName) => {
|
||||
const assets = this.assets[chukName];
|
||||
chunks[chukName] = extract_css(assets);
|
||||
return chunks;
|
||||
}, {})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,6 @@ export type ErrorEvent = {
|
||||
|
||||
export type FatalEvent = {
|
||||
message: string;
|
||||
log?: string;
|
||||
};
|
||||
|
||||
export type InvalidEvent = {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -67,7 +67,10 @@ export function get_page_handler(
|
||||
} else {
|
||||
const link = preloaded_chunks
|
||||
.filter(file => file && !file.match(/\.map$/))
|
||||
.map(file => `<${req.baseUrl}/client/${file}>;rel="preload";as="script"`)
|
||||
.map((file) => {
|
||||
const as = /\.css$/.test(file) ? 'style' : 'script';
|
||||
return `<${req.baseUrl}/client/${file}>;rel="preload";as="${as}"`;
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
res.setHeader('Link', link);
|
||||
@@ -160,7 +163,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);
|
||||
@@ -335,4 +338,4 @@ function escape_html(html: string) {
|
||||
};
|
||||
|
||||
return html.replace(/["'&<>]/g, c => `&${chars[c]};`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@ declare const prefetchRoutes: () => Promise<void>;
|
||||
declare const prefetch: (href: string) => Promise<void>;
|
||||
declare const goto: (href: string) => Promise<void>;
|
||||
|
||||
type StartOpts = {
|
||||
requestInterceptor?: (interceptedRequst: puppeteer.Request) => any
|
||||
};
|
||||
|
||||
export class AppRunner {
|
||||
cwd: string;
|
||||
entry: string;
|
||||
@@ -24,7 +28,7 @@ export class AppRunner {
|
||||
this.messages = [];
|
||||
}
|
||||
|
||||
async start() {
|
||||
async start({ requestInterceptor }: StartOpts = {}) {
|
||||
this.port = await ports.find(3000);
|
||||
|
||||
this.proc = fork(this.entry, [], {
|
||||
@@ -50,6 +54,11 @@ export class AppRunner {
|
||||
}
|
||||
});
|
||||
|
||||
if (requestInterceptor) {
|
||||
await this.page.setRequestInterception(true);
|
||||
this.page.on('request', requestInterceptor);
|
||||
}
|
||||
|
||||
return {
|
||||
page: this.page,
|
||||
base: `http://localhost:${this.port}`,
|
||||
|
||||
@@ -1,9 +1 @@
|
||||
<h1>message: "{message}"</h1>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
preload({ query }) {
|
||||
return query;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<h1>{JSON.stringify(query)}</h1>
|
||||
@@ -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>
|
||||
@@ -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"]}'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -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>
|
||||
9
test/apps/redirects/src/routes/redirect-to-external.html
Normal file
9
test/apps/redirects/src/routes/redirect-to-external.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<h1>unredirected</h1>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
preload() {
|
||||
this.redirect(301, 'https://example.com');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -14,13 +14,26 @@ 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({
|
||||
requestInterceptor: (interceptedRequest) => {
|
||||
if (/example\.com/.test(interceptedRequest.url())) {
|
||||
interceptedRequest.respond({
|
||||
status: 200,
|
||||
contentType: 'text/html',
|
||||
body: `<h1>external</h1>`
|
||||
});
|
||||
} else {
|
||||
interceptedRequest.continue();
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
@@ -34,7 +47,7 @@ describe('redirects', function() {
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await title(),
|
||||
'redirected'
|
||||
);
|
||||
});
|
||||
@@ -53,7 +66,7 @@ describe('redirects', function() {
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await title(),
|
||||
'redirected'
|
||||
);
|
||||
});
|
||||
@@ -67,7 +80,7 @@ describe('redirects', function() {
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await title(),
|
||||
'root'
|
||||
);
|
||||
});
|
||||
@@ -86,8 +99,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'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user