Compare commits

...

24 Commits

Author SHA1 Message Date
Richard Harris
a86b613e12 -> v0.27.1 2019-05-21 00:17:22 -04:00
Rich Harris
fcd66fb736 Merge pull request #704 from sveltejs/gh-701
always re-run preload functions when query string changes
2019-05-21 00:12:09 -04:00
Richard Harris
b6e2736eb0 always re-run preload functions when query string changes - fixes #701 2019-05-20 23:45:39 -04:00
Richard Harris
aa31e3ca6a refactor slightly 2019-05-20 23:11:50 -04:00
Richard Harris
8b310dd458 Merge branch 'issues/688' of https://github.com/btakita/sapper into btakita-issues/688 2019-05-20 23:08:58 -04:00
Brian Takita
5460896228 Checking the current_branch[i] route match against the current request's route match.
current_branch[i].match.slice(1, i+2) compared to match.slice(1, i+2)

Fixes https://github.com/sveltejs/sapper/issues/688
2019-05-20 23:03:08 -04:00
Rich Harris
bcf6ac3a6f Merge pull request #692 from sveltejs/gh-331
leave anchor scrolling on initial load to browser
2019-05-20 22:37:26 -04:00
Rich Harris
b8d99aaa90 Merge pull request #632 from pngwn/master
Add support for custom route file extensions.
2019-05-20 22:33:56 -04:00
Rich Harris
2bb173f140 Merge pull request #683 from cristianl/pr-make-live-optional
Allow disabling live reload
2019-05-20 22:24:06 -04:00
Rich Harris
594cb0a356 Merge pull request #694 from jarrodldavis/fix/restart-server
Fix restarting server in dev
2019-05-20 22:23:29 -04:00
Rich Harris
9ff87af23b Merge pull request #698 from mrkishi/gh-689
Fix export queue
2019-05-20 22:20:59 -04:00
Conduitry
75afc691f4 site: fix doc hash link destinations (#696) 2019-05-18 15:41:59 -04:00
mrkishi
9dd63ab760 fix export queue 2019-05-17 16:50:08 -03:00
pngwn
e7b1aa373a Add support for custom route file extensions. 2019-05-17 19:19:04 +01:00
Antony Jones
ce50c2ff98 License is MIT, not LIL 2019-05-16 11:42:20 -04:00
Jarrod Davis
6add2518aa Fix restarting server in dev
Wait for the server process to exit before starting a new one, so that
the server debugger port is free when the new process begins.
2019-05-15 02:28:41 -06:00
Conduitry
ab939b8cb5 site: update links to Discord chat 2019-05-15 00:34:48 -04:00
Conduitry
b59d9003f9 leave anchor scrolling on initial load to browser (#331) 2019-05-13 08:56:21 -04:00
Cristian Lorsson
1fc169e7b8 Reload only when live reload is enabled 2019-05-10 15:08:32 -03:00
Rich Harris
7aa3e90f87 Merge pull request #677 from mrkishi/infinite-loop
Abort infinite loop for preload errors
2019-05-10 09:45:11 -04:00
Rich Harris
b27c9ecd59 Merge pull request #681 from Troush/patch-1
Typo fix for proper routing link
2019-05-10 09:13:56 -04:00
Alex
8653a799eb Typo fix for proper routing link 2019-05-10 14:50:42 +03:00
mrkishi
62969d59f6 Abort infinite loop for preload errors 2019-05-09 10:40:23 -03:00
Richard Harris
5c07080207 -> v0.27.0 2019-05-09 09:38:21 -04:00
59 changed files with 930 additions and 256 deletions

View File

@@ -1,5 +1,17 @@
# sapper changelog
## 0.27.1
* Prevent infinite loop if `preload` errors ([#677](https://github.com/sveltejs/sapper/pull/677))
* Allow disabling of live reload ([#683](https://github.com/sveltejs/sapper/pull/683))
* Let browser handle initial scroll ([#331](https://github.com/sveltejs/sapper/issues/331))
* Allow custom route file extensions via `--ext` ([#632](https://github.com/sveltejs/sapper/pull/632))
* Wait for server to restart before attaching debugger ([#694](https://github.com/sveltejs/sapper/pull/694))
* Fix export queue ([#698](https://github.com/sveltejs/sapper/pull/698))
* Rerun `preload` functions when query changes ([#701](https://github.com/sveltejs/sapper/issues/701))
* Navigate when spread route changes ([#688](https://github.com/sveltejs/sapper/issues/688))
## 0.27.0
* Change license from LIL to MIT ([#652](https://github.com/sveltejs/sapper/pull/652))

View File

@@ -74,4 +74,4 @@ npm run test
## License
[LIL](LICENSE)
[MIT](LICENSE)

259
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "sapper",
"version": "0.26.1",
"version": "0.27.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -49,9 +49,9 @@
"dev": true
},
"@types/node": {
"version": "10.14.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz",
"integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg==",
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.0.tgz",
"integrity": "sha512-Jrb/x3HT4PTJp6a4avhmJCDEVrPdqLfl3e8GGMbpkGGdwAV5UGlIs4vVEfsHHfylZVOKZWpOqmqFH8CbfOZ6kg==",
"dev": true
},
"@types/puppeteer": {
@@ -466,11 +466,12 @@
}
},
"assert": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
"integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
"integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
"dev": true,
"requires": {
"object-assign": "^4.1.1",
"util": "0.10.3"
},
"dependencies": {
@@ -997,9 +998,9 @@
"dev": true
},
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
"dev": true
},
"concat-map": {
@@ -1983,14 +1984,6 @@
"dev": true,
"requires": {
"is-buffer": "~2.0.3"
},
"dependencies": {
"is-buffer": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
"integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==",
"dev": true
}
}
},
"flat-cache": {
@@ -2083,8 +2076,7 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"aproba": {
"version": "1.2.0",
@@ -2105,14 +2097,12 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -2127,20 +2117,17 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"core-util-is": {
"version": "1.0.2",
@@ -2257,8 +2244,7 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"ini": {
"version": "1.3.5",
@@ -2270,7 +2256,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -2285,7 +2270,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -2293,14 +2277,12 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -2319,7 +2301,6 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -2400,8 +2381,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"object-assign": {
"version": "4.1.1",
@@ -2413,7 +2393,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@@ -2499,8 +2478,7 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -2536,7 +2514,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -2556,7 +2533,6 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -2600,14 +2576,12 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
"dev": true,
"optional": true
"dev": true
}
}
},
@@ -2645,9 +2619,9 @@
"dev": true
},
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
"integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
@@ -2739,6 +2713,12 @@
"kind-of": "^4.0.0"
},
"dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
@@ -2956,6 +2936,12 @@
"kind-of": "^3.0.2"
},
"dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -2983,9 +2969,9 @@
}
},
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
"integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==",
"dev": true
},
"is-callable": {
@@ -3003,6 +2989,12 @@
"kind-of": "^3.0.2"
},
"dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -3081,6 +3073,12 @@
"kind-of": "^3.0.2"
},
"dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -3375,9 +3373,9 @@
}
},
"matchit": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/matchit/-/matchit-1.0.7.tgz",
"integrity": "sha512-6GQP+4ukhBEL4pQPQlipd51XnpOlycit/3o6p4XhhZt2+9hc7JlHr7NuWbTLQ2MdSzcxR603L7LF4T8x1e1mXA==",
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/matchit/-/matchit-1.0.8.tgz",
"integrity": "sha512-CwPPICzozd/ezCzpVwGYG5bMVieaapnA0vvHDQnmQ2u2vZtVLynoPmvFsZjL67hFOvTBhhpqSR0bq3uloDP/Rw==",
"dev": true,
"requires": {
"@arr/every": "^1.0.0"
@@ -3596,6 +3594,20 @@
"locate-path": "^3.0.0"
}
},
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
@@ -3744,14 +3756,6 @@
"requires": {
"object.getownpropertydescriptors": "^2.0.3",
"semver": "^5.7.0"
},
"dependencies": {
"semver": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"dev": true
}
}
},
"node-fetch": {
@@ -3931,6 +3935,12 @@
"is-descriptor": "^0.1.0"
}
},
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -3943,9 +3953,9 @@
}
},
"object-keys": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz",
"integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true
},
"object-visit": {
@@ -4523,9 +4533,9 @@
"dev": true
},
"resolve": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
"integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz",
"integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
@@ -4607,18 +4617,6 @@
"magic-string": "^0.25.2",
"resolve": "^1.10.0",
"rollup-pluginutils": "^2.6.0"
},
"dependencies": {
"rollup-pluginutils": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.6.0.tgz",
"integrity": "sha512-aGQwspEF8oPKvg37u3p7h0cYNwmJR1sCBMZGZ5b9qy8HGtETknqjzcxrDRrcAnJNXN18lBH4Q9vZYth/p4n8jQ==",
"dev": true,
"requires": {
"estree-walker": "^0.6.0",
"micromatch": "^3.1.10"
}
}
}
},
"rollup-plugin-json": {
@@ -4628,18 +4626,6 @@
"dev": true,
"requires": {
"rollup-pluginutils": "^2.5.0"
},
"dependencies": {
"rollup-pluginutils": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.6.0.tgz",
"integrity": "sha512-aGQwspEF8oPKvg37u3p7h0cYNwmJR1sCBMZGZ5b9qy8HGtETknqjzcxrDRrcAnJNXN18lBH4Q9vZYth/p4n8jQ==",
"dev": true,
"requires": {
"estree-walker": "^0.6.0",
"micromatch": "^3.1.10"
}
}
}
},
"rollup-plugin-node-resolve": {
@@ -4662,18 +4648,6 @@
"requires": {
"magic-string": "^0.25.2",
"rollup-pluginutils": "^2.6.0"
},
"dependencies": {
"rollup-pluginutils": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.6.0.tgz",
"integrity": "sha512-aGQwspEF8oPKvg37u3p7h0cYNwmJR1sCBMZGZ5b9qy8HGtETknqjzcxrDRrcAnJNXN18lBH4Q9vZYth/p4n8jQ==",
"dev": true,
"requires": {
"estree-walker": "^0.6.0",
"micromatch": "^3.1.10"
}
}
}
},
"rollup-plugin-string": {
@@ -4713,21 +4687,13 @@
"dev": true
},
"rollup-pluginutils": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.4.1.tgz",
"integrity": "sha512-wesMQ9/172IJDIW/lYWm0vW0LiKe5Ekjws481R7z9WTRtmO59cqyM/2uUlxvf6yzm/fElFmHUobeQOYz46dZJw==",
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.6.0.tgz",
"integrity": "sha512-aGQwspEF8oPKvg37u3p7h0cYNwmJR1sCBMZGZ5b9qy8HGtETknqjzcxrDRrcAnJNXN18lBH4Q9vZYth/p4n8jQ==",
"dev": true,
"requires": {
"estree-walker": "^0.6.0",
"micromatch": "^3.1.10"
},
"dependencies": {
"estree-walker": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.0.tgz",
"integrity": "sha512-peq1RfVAVzr3PU/jL31RaOjUKLoZJpObQWJJ+LgfcxDUifyLZ1RjPQZTl0pzj2uJ45b7A7XpyppXvxdEqzo4rw==",
"dev": true
}
}
},
"run-async": {
@@ -4799,9 +4765,9 @@
}
},
"semver": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"dev": true
},
"serialize-javascript": {
@@ -4894,9 +4860,9 @@
"dev": true
},
"sirv": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/sirv/-/sirv-0.4.0.tgz",
"integrity": "sha512-k/dcRW7Ry2VgERDiLyyq3peGZEnqP2EcTcG5646QjoLwz7lnDA50i20m/3Rn7J4FLtimyoKQsG4E2BItctwjhg==",
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/sirv/-/sirv-0.4.2.tgz",
"integrity": "sha512-dQbZnsMaIiTQPZmbGmktz+c74zt/hyrJEB4tdp2Jj0RNv9J6B/OWR5RyrZEvIn9fyh9Zlg2OlE2XzKz6wMKGAw==",
"dev": true,
"requires": {
"@polka/url": "^0.5.0",
@@ -5031,6 +4997,12 @@
"kind-of": "^3.2.0"
},
"dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -5114,9 +5086,9 @@
}
},
"spdx-license-ids": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz",
"integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==",
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz",
"integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==",
"dev": true
},
"split-string": {
@@ -5287,9 +5259,9 @@
}
},
"svelte": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.2.0.tgz",
"integrity": "sha512-i/FSWUcqVw9JBo9bY69/ZI89WCCpyhdWjvAroOroI4qnQboh0WGlgjnFOEKOTvlv1XONd8cVpRGbOYYE3Ec6TQ==",
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.2.2.tgz",
"integrity": "sha512-B6ePpPodCQodVFDwv/uByaWs0KvXojaXZiB/db1x7WXbyxgLJ8xZF3j1lk5M83Ox0AWlX0fQnCTAaZmfT6xqdw==",
"dev": true
},
"svelte-dev-helper": {
@@ -5309,9 +5281,9 @@
}
},
"table": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz",
"integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==",
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/table/-/table-5.3.0.tgz",
"integrity": "sha512-6V2qlZHIbbZQGzoP3Ghcj/IQDPhBvQYjZE4W4JEyFMkbzHziIzG6jxmAD87BZ1ZXgwPwgu3MzvCUGMOFRN7wlw==",
"dev": true,
"requires": {
"ajv": "^6.9.1",
@@ -5454,6 +5426,12 @@
"kind-of": "^3.0.2"
},
"dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -5524,9 +5502,9 @@
"dev": true
},
"uglify-js": {
"version": "3.5.10",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.10.tgz",
"integrity": "sha512-/GTF0nosyPLbdJBd+AwYiZ+Hu5z8KXWnO0WCGt1BQ/u9Iamhejykqmz5o1OHJ53+VAk6xVxychonnApDjuqGsw==",
"version": "3.5.11",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.11.tgz",
"integrity": "sha512-izPJg8RsSyqxbdnqX36ExpbH3K7tDBsAU/VfNv89VkMFy3z39zFjunQGsSHOlGlyIfGLGprGeosgQno3bo2/Kg==",
"requires": {
"commander": "~2.20.0",
"source-map": "~0.6.1"
@@ -5721,9 +5699,9 @@
}
},
"webpack": {
"version": "4.30.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.30.0.tgz",
"integrity": "sha512-4hgvO2YbAFUhyTdlR4FNyt2+YaYBYHavyzjCMbZzgglo02rlKi/pcsEzwCuCpsn1ryzIl1cq/u8ArIKu8JBYMg==",
"version": "4.31.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.31.0.tgz",
"integrity": "sha512-n6RVO3X0LbbipoE62akME9K/JI7qYrwwufs20VvgNNpqUoH4860KkaxJTbGq5bgkVZF9FqyyTG/0WPLH3PVNJA==",
"dev": true,
"requires": {
"@webassemblyjs/ast": "1.8.5",
@@ -6108,7 +6086,8 @@
},
"yootils": {
"version": "0.0.15",
"resolved": "github:bwbroersma/yootils#77a0949b90387af0bff8081cf596a752a1a3e08e",
"resolved": "https://registry.npmjs.org/yootils/-/yootils-0.0.15.tgz",
"integrity": "sha512-GvGLuJ7XHJPGEUQ52vh8fh+vPjfikuGcu7yBswfrsNsHqnAoytOVuSb69eM0j8wQIjMz0U3kY3YsfwMhJgfG9w==",
"dev": true
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "sapper",
"version": "0.27.0",
"version": "0.27.1",
"description": "Military-grade apps, engineered by Svelte",
"bin": {
"sapper": "./sapper"
@@ -26,7 +26,7 @@
},
"devDependencies": {
"@types/mocha": "^5.2.6",
"@types/node": "^10.14.6",
"@types/node": "^12.0.0",
"@types/puppeteer": "^1.12.4",
"agadoo": "^1.0.1",
"cheap-watch": "^1.0.2",
@@ -52,16 +52,16 @@
"rollup-plugin-sucrase": "^2.1.0",
"rollup-plugin-svelte": "^5.0.3",
"sade": "^1.4.2",
"sirv": "^0.4.0",
"sirv": "^0.4.2",
"sucrase": "^3.10.1",
"svelte": "^3.2.0",
"svelte": "^3.2.2",
"svelte-loader": "^2.13.3",
"webpack": "^4.30.0",
"webpack": "^4.31.0",
"webpack-format-messages": "^2.0.5",
"yootils": "0.0.15"
},
"peerDependencies": {
"svelte": "^3.2.0"
"svelte": "^3.2.2"
},
"scripts": {
"test": "mocha --opts mocha.opts",

View File

@@ -22,6 +22,7 @@ let root_component: Component;
let current_token: {};
let root_preloaded: Promise<any>;
let current_branch = [];
let current_query = '{}';
const stores = {
page: writable({}),
@@ -250,10 +251,28 @@ async function render(redirect: Redirect, branch: any[], props: any, page: Page)
}
current_branch = branch;
current_query = JSON.stringify(page.query);
ready = true;
session_dirty = false;
}
function part_changed(i, segment, match, stringified_query) {
// TODO only check query string changes for preload functions
// that do in fact depend on it (using static analysis or
// runtime instrumentation)
if (stringified_query !== current_query) return true;
const previous = current_branch[i];
if (!previous) return false;
if (segment !== previous.segment) return true;
if (previous.match) {
if (JSON.stringify(previous.match.slice(1, i + 2)) !== JSON.stringify(match.slice(1, i + 2))) {
return true;
}
}
}
export async function hydrate_target(target: Target): Promise<{
redirect?: Redirect;
props?: any;
@@ -292,11 +311,15 @@ export async function hydrate_target(target: Target): Promise<{
let l = 1;
try {
const stringified_query = JSON.stringify(page.query);
const match = route.pattern.exec(page.path);
let segment_dirty = false;
branch = await Promise.all(route.parts.map(async (part, i) => {
const segment = segments[i];
if (current_branch[i] && current_branch[i].segment !== segment) segment_dirty = true;
if (part_changed(i, segment, match, stringified_query)) segment_dirty = true;
props.segments[l] = segments[i + 1]; // TODO make this less confusing
if (!part) return { segment };
@@ -324,7 +347,7 @@ export async function hydrate_target(target: Target): Promise<{
preloaded = initial_data.preloaded[i + 1];
}
return (props[`level${j}`] = { component, props: preloaded, segment, part: part.i });
return (props[`level${j}`] = { component, props: preloaded, segment, match, part: part.i });
}));
} catch (error) {
props.error = error;

View File

@@ -40,7 +40,7 @@ export default function start(opts: {
if (initial_data.error) return handle_error(url);
const target = select_target(url);
if (target) return navigate(target, uid, false, hash);
if (target) return navigate(target, uid, true, hash);
});
}

View File

@@ -26,6 +26,15 @@ export function get_page_handler(
const { server_routes, pages } = manifest;
const error_route = manifest.error;
function bail(req: Req, res: Res, err: Error) {
console.error(err);
const message = dev ? escape_html(err.message) : 'Internal server error';
res.statusCode = 500;
res.end(`<pre>${message}</pre>`);
}
function handle_error(req: Req, res: Res, statusCode: number, error: Error | string) {
handle_page({
pattern: null,
@@ -169,6 +178,10 @@ export function get_page_handler(
preloaded = await Promise.all(toPreload);
} catch (err) {
if (error) {
return bail(req, res, err)
}
preload_error = { statusCode: 500, message: err };
preloaded = []; // appease TypeScript
}
@@ -314,11 +327,8 @@ export function get_page_handler(
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>`);
bail(req, res, err)
} else {
handle_error(req, res, 500, err);
}

View File

@@ -4,7 +4,7 @@ title: Introduction
### Before we begin
> Sapper is in early development, and some things may change before we hit version 1.0. This document is a work-in-progress. If you get stuck, reach out for help in the [Discord chatroom](https://discord.gg/yy75DKs).
> Sapper is in early development, and some things may change before we hit version 1.0. This document is a work-in-progress. If you get stuck, reach out for help in the [Discord chatroom](https://svelte.dev/chat).
>
> See the [migration guides](migrating) for help upgrading from older versions.
@@ -32,7 +32,7 @@ For web developers, the stakes are generally lower than for combat engineers. Bu
[Next.js](https://github.com/zeit/next.js) is a React framework from [Zeit](https://zeit.co), and is the inspiration for Sapper. There are a few notable differences, however:
* Sapper is powered by Svelte instead of React, so it's faster and your apps are smaller
* Instead of route masking, we encode route parameters in filenames (see the [routing](docs#routing) section below)
* Instead of route masking, we encode route parameters in filenames (see the [routing](docs#Routing) section below)
* As well as *pages*, you can create *server routes* in your `src/routes` directory. This makes it very easy to, for example, add a JSON API such as the one powering this very page (try visiting [/docs.json](/docs.json))
* Links are just `<a>` elements, rather than framework-specific `<Link>` components. That means, for example, that [this link right here](/), despite being inside a blob of markdown, works with the router as you'd expect

View File

@@ -24,9 +24,9 @@ If you take a look inside the [sapper-template](https://github.com/sveltejs/sapp
When you first run Sapper, it will create an additional `__sapper__` directory containing generated files.
You'll notice a few extra files and a `cypress` directory which relates to [testing](docs#testing) — we don't need to worry about those right now.
You'll notice a few extra files and a `cypress` directory which relates to [testing](docs#Testing) — we don't need to worry about those right now.
> You *can* create these files from scratch, but it's much better to use the template. See [getting started](docs#getting-started) for instructions on how to easily clone it
> You *can* create these files from scratch, but it's much better to use the template. See [getting started](docs#Getting_started) for instructions on how to easily clone it
### package.json
@@ -35,9 +35,9 @@ Your package.json contains your app's dependencies and defines a number of scrip
* `npm run dev` — start the app in development mode, and watch source files for changes
* `npm run build` — build the app in production mode
* `npm run export` — bake out a static version, if applicable (see [exporting](docs#exporting))
* `npm run export` — bake out a static version, if applicable (see [exporting](docs#Exporting))
* `npm start` — start the app in production mode after you've built it
* `npm test` — run the tests (see [testing](docs#testing))
* `npm test` — run the tests (see [testing](docs#Testing))
### src
@@ -56,7 +56,7 @@ sapper.start({
});
```
In many cases, that's the entirety of your entry module, though you can do as much or as little here as you wish. See the [client API](docs#client-api) section for more information on functions you can import.
In many cases, that's the entirety of your entry module, though you can do as much or as little here as you wish. See the [client API](docs#Client_API) section for more information on functions you can import.
#### src/server.js
@@ -88,7 +88,7 @@ Because every app needs a slightly different service worker (sometimes it's appr
This file is a template for responses from the server. Sapper will inject content that replaces the following tags:
* `%sapper.base%` — a `<base>` element (see [base URLs](docs#base-urls))
* `%sapper.base%` — a `<base>` element (see [base URLs](docs#Base_URLs))
* `%sapper.styles%` — critical CSS for the page being requested
* `%sapper.head%` — HTML representing page-specific `<head>` contents, like `<title>`
* `%sapper.html%` — HTML representing the body of the page being rendered
@@ -97,7 +97,7 @@ This file is a template for responses from the server. Sapper will inject conten
### src/routes
This is the meat of your app — the pages and server routes. See the section on [routing](docs#routing) for the juicy details.
This is the meat of your app — the pages and server routes. See the section on [routing](docs#Routing) for the juicy details.
### static

View File

@@ -70,7 +70,7 @@ Dynamic parameters are encoded using `[brackets]`. For example, here's how you c
</div>
```
> See the section on [preloading](docs#preloading) for more info about `preload` and `this.fetch`
> See the section on [preloading](docs#Preloading) for more info about `preload` and `this.fetch`
### Server routes

View File

@@ -36,7 +36,7 @@ Programmatically navigates to the given `href`. If the destination is a Sapper r
* `href` — the page to prefetch
Programmatically prefetches the given page, which means a) ensuring that the code for the page is loaded, and b) calling the page's `preload` method with the appropriate options. This is the same behaviour that Sapper triggers when the user taps or mouses over an `<a>` element with [rel=prefetch](docs#prefetching).
Programmatically prefetches the given page, which means a) ensuring that the code for the page is loaded, and b) calling the page's `preload` method with the appropriate options. This is the same behaviour that Sapper triggers when the user taps or mouses over an `<a>` element with [rel=prefetch](docs#Prefetching).

View File

@@ -2,7 +2,7 @@
title: Preloading
---
As seen in the [routing](docs#routing) section, page components can have an optional `preload` function that will load some data that the page depends on. This is similar to `getInitialProps` in Next.js or `asyncData` in Nuxt.js.
As seen in the [routing](docs#Routing) section, page components can have an optional `preload` function that will load some data that the page depends on. This is similar to `getInitialProps` in Next.js or `asyncData` in Nuxt.js.
```html
<script context="module">

View File

@@ -21,7 +21,7 @@ express() // or Polka, or a similar framework
Sapper will detect the base path and configure both the server-side and client-side routers accordingly.
If you're [exporting](docs#exporting) your app, you will need to tell the exporter where to begin crawling:
If you're [exporting](docs#Exporting) your app, you will need to tell the exporter where to begin crawling:
```bash
sapper export --basepath my-base-path

View File

@@ -292,7 +292,7 @@ Once your `App.html` has been created and your server and client apps updated, y
##### app/template.html
* Your `<head>` element must contain `%sapper.base%` (see ([base URLs](docs#base-urls))
* Your `<head>` element must contain `%sapper.base%` (see ([base URLs](docs#Base_URLs))
* Remove references to your service worker; this is now handled by `%sapper.scripts%`
##### Pages

View File

@@ -61,7 +61,7 @@
<p>Please try reloading the page.</p>
{/if}
<p>If the error persists, please drop by <a href="https://discord.gg/yy75DKs">Discord chatroom</a> and let us know, or raise an issue on <a href="https://github.com/sveltejs/svelte">GitHub</a>. Thanks!</p>
<p>If the error persists, please drop by <a href="https://svelte.dev/chat">Discord chatroom</a> and let us know, or raise an issue on <a href="https://github.com/sveltejs/svelte">GitHub</a>. Thanks!</p>
{/if}
{:else}
<h1>It looks like you're offline</h1>

View File

@@ -26,7 +26,7 @@
<NavItem external="https://svelte.dev">Svelte</NavItem>
<NavItem external="https://discord.gg/yy75DKs" title="Discord Chat">
<NavItem external="https://svelte.dev/chat" title="Discord Chat">
<Icon name="message-square"/>
</NavItem>

View File

@@ -19,6 +19,7 @@ type Opts = {
static?: string;
legacy?: boolean;
bundler?: 'rollup' | 'webpack';
ext?: string;
oncompile?: ({ type, result }: { type: string, result: CompileResult }) => void;
};
@@ -32,6 +33,7 @@ export async function build({
bundler,
legacy = false,
ext,
oncompile = noop
}: Opts = {}) {
bundler = validate_bundler(bundler);
@@ -68,7 +70,7 @@ export async function build({
fs.writeFileSync(`${dest}/template.html`, minify_html(template));
const manifest_data = create_manifest_data(routes);
const manifest_data = create_manifest_data(routes, ext);
// create src/node_modules/@sapper/app.mjs and server.mjs
create_app({

View File

@@ -28,7 +28,8 @@ type Opts = {
hot?: boolean,
'devtools-port'?: number,
bundler?: 'rollup' | 'webpack',
port?: number
port?: number,
ext: string
};
export function dev(opts: Opts) {
@@ -47,7 +48,7 @@ class Watcher extends EventEmitter {
}
port: number;
closed: boolean;
dev_port: number;
live: boolean;
hot: boolean;
@@ -67,6 +68,7 @@ class Watcher extends EventEmitter {
unique_warnings: Set<string>;
unique_errors: Set<string>;
}
ext: string;
constructor({
cwd = '.',
@@ -80,7 +82,8 @@ class Watcher extends EventEmitter {
hot,
'devtools-port': devtools_port,
bundler,
port = +process.env.PORT
port = +process.env.PORT,
ext
}: Opts) {
super();
@@ -95,7 +98,7 @@ class Watcher extends EventEmitter {
output: path.resolve(cwd, output),
static: path.resolve(cwd, static_files)
};
this.ext = ext;
this.port = port;
this.closed = false;
@@ -161,7 +164,7 @@ class Watcher extends EventEmitter {
let manifest_data: ManifestData;
try {
manifest_data = create_manifest_data(routes);
manifest_data = create_manifest_data(routes, this.ext);
create_app({
bundler: this.bundler,
manifest_data,
@@ -189,7 +192,7 @@ class Watcher extends EventEmitter {
},
() => {
try {
const new_manifest_data = create_manifest_data(routes);
const new_manifest_data = create_manifest_data(routes, this.ext);
create_app({
bundler: this.bundler,
manifest_data, // TODO is this right? not new_manifest_data?
@@ -206,15 +209,19 @@ class Watcher extends EventEmitter {
});
}
}
),
fs.watch(`${src}/template.html`, () => {
this.dev_server.send({
action: 'reload'
});
})
)
);
if (this.live) {
this.filewatchers.push(
fs.watch(`${src}/template.html`, () => {
this.dev_server.send({
action: 'reload'
});
})
);
}
let deferred = new Deferred();
// TODO watch the configs themselves?
@@ -252,7 +259,7 @@ class Watcher extends EventEmitter {
this.dev_server.send({
status: 'completed'
});
} else {
} else if (this.live) {
this.dev_server.send({
action: 'reload'
});
@@ -267,48 +274,54 @@ class Watcher extends EventEmitter {
});
};
const start_server = () => {
// we need to give the child process its own DevTools port,
// otherwise Node will try to use the parent's (and fail)
const debugArgRegex = /--inspect(?:-brk|-port)?|--debug-port/;
const execArgv = process.execArgv.slice();
if (execArgv.some((arg: string) => !!arg.match(debugArgRegex))) {
execArgv.push(`--inspect-port=${this.devtools_port}`);
}
this.proc = child_process.fork(`${dest}/server/server.js`, [], {
cwd: process.cwd(),
env: Object.assign({
PORT: this.port
}, process.env),
stdio: ['ipc'],
execArgv
});
this.proc.stdout.on('data', chunk => {
this.emit('stdout', chunk);
});
this.proc.stderr.on('data', chunk => {
this.emit('stderr', chunk);
});
this.proc.on('message', message => {
if (message.__sapper__ && message.event === 'basepath') {
this.emit('basepath', {
basepath: message.basepath
});
}
});
this.proc.on('exit', emitFatal);
};
if (this.proc) {
this.proc.removeListener('exit', emitFatal);
this.proc.kill();
this.proc.on('exit', restart);
this.proc.on('exit', () => {
start_server();
restart();
});
} else {
start_server();
restart();
}
// we need to give the child process its own DevTools port,
// otherwise Node will try to use the parent's (and fail)
const debugArgRegex = /--inspect(?:-brk|-port)?|--debug-port/;
const execArgv = process.execArgv.slice();
if (execArgv.some((arg: string) => !!arg.match(debugArgRegex))) {
execArgv.push(`--inspect-port=${this.devtools_port}`);
}
this.proc = child_process.fork(`${dest}/server/server.js`, [], {
cwd: process.cwd(),
env: Object.assign({
PORT: this.port
}, process.env),
stdio: ['ipc'],
execArgv
});
this.proc.stdout.on('data', chunk => {
this.emit('stdout', chunk);
});
this.proc.stderr.on('data', chunk => {
this.emit('stderr', chunk);
});
this.proc.on('message', message => {
if (message.__sapper__ && message.event === 'basepath') {
this.emit('basepath', {
basepath: message.basepath
});
}
});
this.proc.on('exit', emitFatal);
});
}
});

View File

@@ -132,19 +132,23 @@ async function _export({
if (seen.has(pathname)) return;
seen.add(pathname);
const timeout_deferred = new Deferred();
const the_timeout = setTimeout(() => {
timeout_deferred.reject(new Error(`Timed out waiting for ${url.href}`));
}, timeout);
const r = await q.add(async () => {
const timeout_deferred = new Deferred();
const the_timeout = setTimeout(() => {
timeout_deferred.reject(new Error(`Timed out waiting for ${url.href}`));
}, timeout);
const r = await Promise.race([
q.add(() => fetch(url.href, {
redirect: 'manual'
})),
timeout_deferred.promise
]);
const r = await Promise.race([
fetch(url.href, {
redirect: 'manual'
}),
timeout_deferred.promise
]);
clearTimeout(the_timeout); // prevent it hanging at the end
clearTimeout(the_timeout); // prevent it hanging at the end
return r;
}) as Response;
let type = r.headers.get('Content-Type');
@@ -152,6 +156,8 @@ async function _export({
const range = ~~(r.status / 100);
let tasks = [];
if (range === 2) {
if (type === 'text/html') {
// parse link rel=preload headers and embed them in the HTML
@@ -173,8 +179,6 @@ async function _export({
let match;
let pattern = /<a ([\s\S]+?)>/gm;
let promise;
while (match = pattern.exec(cleaned)) {
const attrs = match[1];
const href = get_href(attrs);
@@ -183,12 +187,10 @@ async function _export({
const url = resolve(base.href, href);
if (url.protocol === protocol && url.host === host) {
promise = handle(url);
tasks.push(handle(url));
}
}
}
await promise;
}
}
}
@@ -199,10 +201,12 @@ async function _export({
type = 'text/html';
body = `<script>window.location.href = "${location.replace(origin, '')}"</script>`;
await handle(resolve(root.href, location));
tasks.push(handle(resolve(root.href, location)));
}
save(pathname, r.status, type, body);
await Promise.all(tasks);
}
try {

View File

@@ -31,6 +31,7 @@ prog.command('dev')
.option('--static', 'Static files directory', 'static')
.option('--output', 'Sapper output directory', 'src/node_modules/@sapper')
.option('--build-dir', 'Development build directory', '__sapper__/dev')
.option('--ext', 'Custom Route Extension', '.svelte .html')
.action(async (opts: {
port: number,
open: boolean,
@@ -43,7 +44,8 @@ prog.command('dev')
routes: string,
static: string,
output: string,
'build-dir': string
'build-dir': string,
ext: string
}) => {
const { dev } = await import('./api/dev');
@@ -59,7 +61,8 @@ prog.command('dev')
'dev-port': opts['dev-port'],
live: opts.live,
hot: opts.hot,
bundler: opts.bundler
bundler: opts.bundler,
ext: opts.ext
});
let first = true;
@@ -151,6 +154,7 @@ prog.command('build [dest]')
.option('--src', 'Source directory', 'src')
.option('--routes', 'Routes directory', 'src/routes')
.option('--output', 'Sapper output directory', 'src/node_modules/@sapper')
.option('--ext', 'Custom Route Extension', '.svelte .html')
.example(`build custom-dir -p 4567`)
.action(async (dest = '__sapper__/build', opts: {
port: string,
@@ -159,12 +163,13 @@ prog.command('build [dest]')
cwd: string,
src: string,
routes: string,
output: string
output: string,
ext: string
}) => {
console.log(`> Building...`);
try {
await _build(opts.bundler, opts.legacy, opts.cwd, opts.src, opts.routes, opts.output, dest);
await _build(opts.bundler, opts.legacy, opts.cwd, opts.src, opts.routes, opts.output, dest, opts.ext);
const launcher = path.resolve(dest, 'index.js');
@@ -199,6 +204,7 @@ prog.command('export [dest]')
.option('--static', 'Static files directory', 'static')
.option('--output', 'Sapper output directory', 'src/node_modules/@sapper')
.option('--build-dir', 'Intermediate build directory', '__sapper__/build')
.option('--ext', 'Custom Route Extension', '.svelte .html')
.action(async (dest = '__sapper__/export', opts: {
build: boolean,
legacy: boolean,
@@ -212,11 +218,12 @@ prog.command('export [dest]')
static: string,
output: string,
'build-dir': string,
ext: string
}) => {
try {
if (opts.build) {
console.log(`> Building...`);
await _build(opts.bundler, opts.legacy, opts.cwd, opts.src, opts.routes, opts.output, opts['build-dir']);
await _build(opts.bundler, opts.legacy, opts.cwd, opts.src, opts.routes, opts.output, opts['build-dir'], opts.ext);
console.error(`\n> Built in ${elapsed(start)}`);
}
@@ -265,7 +272,8 @@ async function _build(
src: string,
routes: string,
output: string,
dest: string
dest: string,
ext: string
) {
const { build } = await import('./api/build');
@@ -276,7 +284,7 @@ async function _build(
src,
routes,
dest,
ext,
oncompile: event => {
let banner = `built ${event.type}`;
let c = (txt: string) => colors.cyan(txt);

View File

@@ -4,9 +4,10 @@ import svelte from 'svelte/compiler';
import { Page, PageComponent, ServerRoute, ManifestData } from '../interfaces';
import { posixify, reserved_words } from '../utils';
const component_extensions = ['.svelte', '.html']; // TODO make this configurable (to include e.g. .svelte.md?)
export default function create_manifest_data(cwd: string, extensions: string = '.svelte .html'): ManifestData {
const component_extensions = extensions.split(' ');
export default function create_manifest_data(cwd: string): ManifestData {
// TODO remove in a future version
if (!fs.existsSync(cwd)) {
throw new Error(`As of Sapper 0.21, the routes/ directory should become src/routes/`);

View File

@@ -1,8 +1,18 @@
<script context="module">
export function preload({ query, params }) {
const { rest } = params;
return { rest };
}
</script>
<script>
import { stores } from '@sapper/app';
const { page } = stores();
export let rest;
</script>
<h1>{$page.params.rest.join(',')}</h1>
<h2>{rest.join(',')}</h2>
<a href="xyz/abc/qwe/deep.json">deep</a>
<a href="xyz/abc">back</a>

View File

@@ -1,8 +1,20 @@
<script context="module">
export function preload({ query, params }) {
const { rest } = params;
return { rest };
}
</script>
<script>
import { stores } from '@sapper/app';
const { page } = stores();
export let rest;
</script>
<h1>{$page.params.rest.join(',')}</h1>
<h2>{rest.join(',')}</h2>
<a href="xyz/abc/deep">deep</a>
<a href="xyz/abc">deep</a>
<a href="xyz/abc/def">deep</a>
<a href="xyz/abc/def/ghi">deep</a>

View File

@@ -1,7 +1,7 @@
import * as assert from 'assert';
import * as http from 'http';
import { build } from '../../../api';
import { AppRunner } from '../AppRunner';
import {build} from '../../../api';
import {AppRunner} from '../AppRunner';
declare let deleted: { id: number };
declare let el: any;
@@ -267,7 +267,6 @@ describe('basics', function() {
await r.sapper.start();
assert.equal(await r.text('h1'), 'foo');
await r.page.click('[href="dirs/bar"]');
await r.wait();
assert.equal(await r.text('h1'), 'bar');
@@ -278,11 +277,26 @@ describe('basics', function() {
await r.sapper.start();
assert.equal(await r.text('h1'), 'abc,xyz');
await r.page.click('[href="xyz/abc/def/ghi"]');
await r.wait();
assert.equal(await r.text('h1'), 'xyz,abc,def,ghi');
assert.equal(await r.text('h2'), 'xyz,abc,def,ghi');
await r.page.click('[href="xyz/abc/def"]');
await r.wait();
assert.equal(await r.text('h1'), 'xyz,abc,def');
assert.equal(await r.text('h2'), 'xyz,abc,def');
await r.page.click('[href="xyz/abc/def"]');
await r.wait();
assert.equal(await r.text('h1'), 'xyz,abc,def');
assert.equal(await r.text('h2'), 'xyz,abc,def');
await r.page.click('[href="xyz/abc"]');
await r.wait();
assert.equal(await r.text('h1'), 'xyz,abc');
assert.equal(await r.text('h2'), 'xyz,abc');
await r.page.click('[href="xyz/abc/deep"]');
await r.wait();
assert.equal(await r.text('h1'), 'xyz,abc');
assert.equal(await r.text('h2'), 'xyz,abc');
await r.page.click('[href="xyz/abc/qwe/deep.json"]');
await r.wait();
assert.equal(

View File

@@ -0,0 +1,60 @@
import resolve from 'rollup-plugin-node-resolve';
import replace from 'rollup-plugin-replace';
import svelte from 'rollup-plugin-svelte';
const mode = process.env.NODE_ENV;
const dev = mode === 'development';
const config = require('../../../config/rollup.js');
export default {
client: {
input: config.client.input(),
output: config.client.output(),
plugins: [
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode)
}),
svelte({
extensions: ['.whokilledthemuffinman', '.jesuslivesineveryone', '.mdx', '.svelte', '.html'],
dev,
hydratable: true,
emitCss: true
}),
resolve()
]
},
server: {
input: config.server.input(),
output: config.server.output(),
plugins: [
replace({
'process.browser': false,
'process.env.NODE_ENV': JSON.stringify(mode)
}),
svelte({
extensions: ['.whokilledthemuffinman', '.jesuslivesineveryone', '.mdx', '.svelte', '.html'],
generate: 'ssr',
dev
}),
resolve({
preferBuiltins: true
})
],
external: ['sirv', 'polka']
},
serviceworker: {
input: config.serviceworker.input(),
output: config.serviceworker.output(),
plugins: [
resolve(),
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode)
})
]
}
};

View File

@@ -0,0 +1,9 @@
import * as sapper from '@sapper/app';
window.start = () => sapper.start({
target: document.querySelector('#sapper')
});
window.prefetchRoutes = () => sapper.prefetchRoutes();
window.prefetch = href => sapper.prefetch(href);
window.goto = href => sapper.goto(href);

View File

@@ -0,0 +1,6 @@
<script>
import { stores } from '@sapper/app';
const { page } = stores();
</script>
<h1>{$page.params.slug.toUpperCase()}</h1>

View File

@@ -0,0 +1,3 @@
<h1>hi</h1>
<p>hi</p>

View File

@@ -0,0 +1 @@
<h1>a</h1>

View File

@@ -0,0 +1 @@
<h1>Tremendous!</h1>

View File

@@ -0,0 +1,8 @@
<h1>Great success!</h1>
<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

@@ -0,0 +1 @@
<h1>Bazooom!</h1>

View File

@@ -0,0 +1,9 @@
import polka from 'polka';
import * as sapper from '@sapper/server';
import { start } from '../../common.js';
const app = polka()
.use(sapper.middleware())
start(app);

View File

@@ -0,0 +1,81 @@
import * as sapper from '@sapper/service-worker';
const ASSETS = `cache${sapper.timestamp}`;
// `app.shell` is an array of all the files generated by webpack,
// `app.files` is an array of everything in the `static` directory
const to_cache = sapper.shell.concat(sapper.files);
const cached = new Set(to_cache);
self.addEventListener('install', event => {
event.waitUntil(
caches
.open(ASSETS)
.then(cache => cache.addAll(to_cache))
.then(() => {
self.skipWaiting();
})
);
});
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(async keys => {
// delete old caches
for (const key of keys) {
if (key !== ASSETS) await caches.delete(key);
}
self.clients.claim();
})
);
});
self.addEventListener('fetch', event => {
if (event.request.method !== 'GET') return;
const url = new URL(event.request.url);
// don't try to handle e.g. data: URIs
if (!url.protocol.startsWith('http')) return;
// ignore dev server requests
if (url.hostname === self.location.hostname && url.port !== self.location.port) return;
// always serve assets and webpack-generated files from cache
if (url.host === self.location.host && cached.has(url.pathname)) {
event.respondWith(caches.match(event.request));
return;
}
// for pages, you might want to serve a shell `index.html` file,
// which Sapper has generated for you. It's not right for every
// app, but if it's right for yours then uncomment this section
/*
event.respondWith(caches.match('/index.html'));
return;
}
*/
if (event.request.cache === 'only-if-cached') return;
// for everything else, try the network first, falling back to
// cache if the user is offline. (If the pages never change, you
// might prefer a cache-first approach to a network-first one.)
event.respondWith(
caches
.open(`offline${sapper.timestamp}`)
.then(async cache => {
try {
const response = await fetch(event.request);
cache.put(event.request, response.clone());
return response;
} catch(err) {
const response = await cache.match(event.request);
if (response) return response;
throw err;
}
})
);
});

View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset='utf-8'>
%sapper.base%
%sapper.styles%
%sapper.head%
</head>
<body>
<div id='sapper'>%sapper.html%</div>
%sapper.scripts%
</body>
</html>

View File

@@ -0,0 +1,61 @@
import * as assert from 'assert';
import { build } from '../../../api';
import { AppRunner } from '../AppRunner';
describe('custom extensions', function() {
this.timeout(10000);
let r: AppRunner;
// hooks
before('build app', () => build({ cwd: __dirname , ext: '.jesuslivesineveryone .whokilledthemuffinman .mdx .svelte' }));
before('start runner', async () => {
r = await new AppRunner().start(__dirname);
});
after(() => r && r.end());
it('works with arbitrary extensions', async () => {
await r.load(`/`);
assert.equal(
await r.text('h1'),
'Great success!'
);
});
it('works with other arbitrary extensions', async () => {
await r.load(`/const`);
assert.equal(
await r.text('h1'),
'Tremendous!'
);
await r.load(`/a`);
assert.equal(
await r.text('h1'),
'a'
);
await r.load(`/test-slug`);
assert.equal(
await r.text('h1'),
'TEST-SLUG'
);
await r.load(`/unsafe-replacement`);
assert.equal(
await r.text('h1'),
'Bazooom!'
);
});
});

View File

@@ -0,0 +1,58 @@
import resolve from 'rollup-plugin-node-resolve';
import replace from 'rollup-plugin-replace';
import svelte from 'rollup-plugin-svelte';
const mode = process.env.NODE_ENV;
const dev = mode === 'development';
const config = require('../../../config/rollup.js');
export default {
client: {
input: config.client.input(),
output: config.client.output(),
plugins: [
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode)
}),
svelte({
dev,
hydratable: true,
emitCss: true
}),
resolve()
]
},
server: {
input: config.server.input(),
output: config.server.output(),
plugins: [
replace({
'process.browser': false,
'process.env.NODE_ENV': JSON.stringify(mode)
}),
svelte({
generate: 'ssr',
dev
}),
resolve({
preferBuiltins: true
})
],
external: ['sirv', 'polka']
},
serviceworker: {
input: config.serviceworker.input(),
output: config.serviceworker.output(),
plugins: [
resolve(),
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode)
})
]
}
};

View File

@@ -0,0 +1,9 @@
import * as sapper from '@sapper/app';
window.start = () => sapper.start({
target: document.querySelector('#sapper')
});
window.prefetchRoutes = () => sapper.prefetchRoutes();
window.prefetch = href => sapper.prefetch(href);
window.goto = href => sapper.goto(href);

View File

@@ -0,0 +1,3 @@
<h1>{status}</h1>
<p>{error.message}</p>

View File

@@ -0,0 +1,15 @@
<script context="module">
export function preload({ params }) {
if (params.x === 'a') {
return new Promise(resolve => setTimeout(resolve, 100));
}
return params;
}
</script>
<script>
export let x;
</script>
<a href="b-{x}">b-{x}</a>

View File

@@ -0,0 +1,5 @@
<script>
export let x;
</script>
<p>b-{x}</p>

View File

@@ -0,0 +1,3 @@
<a href="a-a">1</a>
<a href="a-b">2</a>

View File

@@ -0,0 +1,13 @@
import sirv from 'sirv';
import polka from 'polka';
import * as sapper from '@sapper/server';
import { start, dev } from '../../common.js';
const app = polka()
.use(
sirv('static', { dev }),
sapper.middleware()
);
start(app);

View File

@@ -0,0 +1,82 @@
import * as sapper from '@sapper/service-worker';
const ASSETS = `cache${sapper.timestamp}`;
// `shell` is an array of all the files generated by webpack,
// `files` is an array of everything in the `static` directory
const to_cache = sapper.shell.concat(sapper.files);
const cached = new Set(to_cache);
self.addEventListener('install', event => {
event.waitUntil(
caches
.open(ASSETS)
.then(cache => cache.addAll(to_cache))
.then(() => {
self.skipWaiting();
})
);
});
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(async keys => {
// delete old caches
for (const key of keys) {
if (key !== ASSETS) await caches.delete(key);
}
self.clients.claim();
})
);
});
self.addEventListener('fetch', event => {
if (event.request.method !== 'GET') return;
const url = new URL(event.request.url);
// don't try to handle e.g. data: URIs
if (!url.protocol.startsWith('http')) return;
// ignore dev server requests
if (url.hostname === self.location.hostname && url.port !== self.location.port) return;
// always serve assets and webpack-generated files from cache
if (url.host === self.location.host && cached.has(url.pathname)) {
event.respondWith(caches.match(event.request));
return;
}
// for pages, you might want to serve a shell `index.html` file,
// which Sapper has generated for you. It's not right for every
// app, but if it's right for yours then uncomment this section
/*
if (url.origin === self.origin && routes.find(route => route.pattern.test(url.pathname))) {
event.respondWith(caches.match('/index.html'));
return;
}
*/
if (event.request.cache === 'only-if-cached') return;
// for everything else, try the network first, falling back to
// cache if the user is offline. (If the pages never change, you
// might prefer a cache-first approach to a network-first one.)
event.respondWith(
caches
.open(`offline${sapper.timestamp}`)
.then(async cache => {
try {
const response = await fetch(event.request);
cache.put(event.request, response.clone());
return response;
} catch(err) {
const response = await cache.match(event.request);
if (response) return response;
throw err;
}
})
);
});

View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset='utf-8'>
%sapper.base%
%sapper.styles%
%sapper.head%
</head>
<body>
<div id='sapper'>%sapper.html%</div>
%sapper.scripts%
</body>
</html>

View File

@@ -0,0 +1,3 @@
body {
font-family: 'Comic Sans MS';
}

View File

@@ -0,0 +1,13 @@
import * as api from '../../../api';
describe('export-queue', function() {
this.timeout(10000);
// hooks
before('build app', () => api.build({ cwd: __dirname }));
// tests
it('exports a site with inconsistent load time', async () => {
await api.export({ cwd: __dirname });
});
});

View File

@@ -16,4 +16,5 @@
</script>
<span>z: {$page.params.z} {count}</span>
<a href="foo/bar/qux">click me</a>
<a href="foo/bar/qux">goto foo/bar/qux</a>
<a href="foo/abc/def">goto foo/abc/def</a>

View File

@@ -23,7 +23,8 @@ describe('layout', function() {
assert.deepEqual(text1.split('\n').map(str => str.trim()).filter(Boolean), [
'y: bar 1',
'z: baz 1',
'click me',
'goto foo/bar/qux',
'goto foo/abc/def',
'child segment: baz'
]);
@@ -32,7 +33,8 @@ describe('layout', function() {
assert.deepEqual(text2.split('\n').map(str => str.trim()).filter(Boolean), [
'y: bar 1',
'z: baz 1',
'click me',
'goto foo/bar/qux',
'goto foo/abc/def',
'child segment: baz'
]);
@@ -43,9 +45,22 @@ describe('layout', function() {
assert.deepEqual(text3.split('\n').map(str => str.trim()).filter(Boolean), [
'y: bar 1',
'z: qux 2',
'click me',
'goto foo/bar/qux',
'goto foo/abc/def',
'child segment: qux'
]);
await r.page.click('[href="foo/abc/def"]');
await r.wait();
const text4 = await r.text('#sapper');
assert.deepEqual(text4.split('\n').map(str => str.trim()).filter(Boolean), [
'y: abc 2',
'z: def 3',
'goto foo/bar/qux',
'goto foo/abc/def',
'child segment: def'
]);
});
it('survives the tests with no server errors', () => {

View File

@@ -0,0 +1,17 @@
<script context="module">
export function preload(page) {
return {
query: page.query
};
}
</script>
<script>
export let query;
</script>
<pre>{JSON.stringify(query)}</pre>
<a href="echo?foo=1">foo=1</a>
<a href="echo?foo=2">foo=2</a>
<a href="echo?foo=3">foo=3</a>

View File

@@ -109,4 +109,23 @@ describe('preloading', function() {
it('survives the tests with no server errors', () => {
assert.deepEqual(r.errors, []);
});
it('re-runs preload when page.query changes', async () => {
await r.load('/echo?foo=1');
await r.sapper.start();
await r.sapper.prefetchRoutes();
assert.equal(
await r.text('pre'),
`{"foo":"1"}`
);
await r.page.click('a[href="echo?foo=2"]');
await r.wait();
assert.equal(
await r.text('pre'),
`{"foo":"2"}`
);
});
});

View File

@@ -177,4 +177,66 @@ describe('manifest_data', () => {
pattern: /^\/foo\/?$/
}]);
});
it('works with custom extensions' , () => {
const { components, pages, server_routes } = create_manifest_data(path.join(__dirname, 'samples/custom-extension'), '.jazz .beebop .funk .html');
const index = { name: 'index', file: 'index.funk', has_preload: false };
const about = { name: 'about', file: 'about.jazz', has_preload: false };
const blog = { name: 'blog', file: 'blog/index.html', has_preload: false };
const blog_$slug = { name: 'blog_$slug', file: 'blog/[slug].beebop', has_preload: false };
assert.deepEqual(components, [
index,
about,
blog,
blog_$slug
]);
assert.deepEqual(pages, [
{
pattern: /^\/$/,
parts: [
{ component: index, params: [] }
]
},
{
pattern: /^\/about\/?$/,
parts: [
{ component: about, params: [] }
]
},
{
pattern: /^\/blog\/?$/,
parts: [
{ component: blog, params: [] }
]
},
{
pattern: /^\/blog\/([^\/]+?)\/?$/,
parts: [
null,
{ component: blog_$slug, params: ['slug'] }
]
}
]);
assert.deepEqual(server_routes, [
{
name: 'route_blog_json',
pattern: /^\/blog.json$/,
file: 'blog/index.json.js',
params: []
},
{
name: 'route_blog_$slug_json',
pattern: /^\/blog\/([^\/]+?).json$/,
file: 'blog/[slug].json.js',
params: ['slug']
}
]);
});
});