diff --git a/CHANGELOG.md b/CHANGELOG.md index d692da9..6baa194 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # sapper changelog +## 0.25.0 + +* Force refresh on `goto(current_url)` ([#484](https://github.com/sveltejs/sapper/pull/484)) +* Fix preloading navigation bug ([#532](https://github.com/sveltejs/sapper/issues/532)) +* Don't mutate opts.headers ([#528](https://github.com/sveltejs/sapper/issues/528)) +* Don't crawl hundreds of pages simultaneously ([#369](https://github.com/sveltejs/sapper/pull/369)) + +## 0.24.3 + +* Add service-worker-index.html shell file for offline support ([#422](https://github.com/sveltejs/sapper/issues/422)) +* Don't cache .map files ([#534](https://github.com/sveltejs/sapper/issues/534)) + +## 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)) diff --git a/package-lock.json b/package-lock.json index 51abda0..6ee03a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "sapper", - "version": "0.25.0-alpha2", + "version": "0.26.0-alpha.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -43,9 +43,9 @@ "dev": true }, "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==", "dev": true }, "@types/glob": { @@ -81,15 +81,15 @@ "dev": true }, "@types/node": { - "version": "10.12.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.19.tgz", - "integrity": "sha512-2NVovndCjJQj6fUUn9jCgpP4WSqr+u1SoUZMZyJkhGeBFsm6dE46l31S7lPUYt9uQ28XI+ibrJA1f5XyH5HNtA==", + "version": "10.12.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.21.tgz", + "integrity": "sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ==", "dev": true }, "@types/puppeteer": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-1.11.2.tgz", - "integrity": "sha512-eHBXpiZz0+PrnvHID68OjwcBjTbK1V49sa4lxjq+jiEe2eaLbRl2F77icNg0ewiB2fgdHJUHBD/9ubjfKrvTEw==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-1.11.3.tgz", + "integrity": "sha512-i/kNectDkLqU4y8FfeMAdMv4KGVVCPUVqp+bfa5+teq0JdoAwplu7k6t38+rpAAbEmPSywf6aqWbg9JbUDJqiQ==", "dev": true, "requires": { "@types/node": "*" @@ -897,9 +897,9 @@ "dev": true }, "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", "dev": true }, "binary-extensions": { @@ -1047,9 +1047,9 @@ "dev": true }, "builtin-modules": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.0.0.tgz", - "integrity": "sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, "builtin-status-codes": { @@ -1078,6 +1078,14 @@ "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + } } }, "cache-base": { @@ -1121,9 +1129,9 @@ } }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -1742,17 +1750,16 @@ } }, "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", + "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "dev": true, "requires": { - "es-to-primitive": "^1.2.0", + "es-to-primitive": "^1.1.1", "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" } }, "es-to-primitive": { @@ -1928,6 +1935,15 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } } } }, @@ -2922,15 +2938,15 @@ "dev": true }, "globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.1.tgz", + "integrity": "sha512-bqKcPhb+ZtrISivpu6oLmwIyINlPlzueO/BDCdfnzUeu7SYxnHTXmWP7uQI5PnQXc5yGXOscGBEGagloA2hcSw==", "dev": true }, "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, "growl": { @@ -3041,9 +3057,10 @@ } }, "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true }, "hmac-drbg": { "version": "1.0.1", @@ -3074,6 +3091,13 @@ "param-case": "2.1.x", "relateurl": "0.2.x", "uglify-js": "3.4.x" + }, + "dependencies": { + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + } } }, "https-browserify": { @@ -3169,21 +3193,21 @@ "dev": true }, "inquirer": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz", - "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", + "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", - "external-editor": "^3.0.0", + "external-editor": "^3.0.3", "figures": "^2.0.0", - "lodash": "^4.17.10", + "lodash": "^4.17.11", "mute-stream": "0.0.7", "run-async": "^2.2.0", - "rxjs": "^6.1.0", + "rxjs": "^6.4.0", "string-width": "^2.1.0", "strip-ansi": "^5.0.0", "through": "^2.3.6" @@ -3195,6 +3219,17 @@ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==", "dev": true }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, "strip-ansi": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", @@ -3237,12 +3272,12 @@ "dev": true }, "is-builtin-module": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.0.0.tgz", - "integrity": "sha512-/93sDihsAD652hrMEbJGbMAVBf1qc96kyThHQ0CAOONHaE3aROLpTjDe4WQ5aoC5ITHFxEq1z8XqSU7km+8amw==", + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "^3.0.0" + "builtin-modules": "^1.0.0" } }, "is-callable": { @@ -3457,21 +3492,10 @@ "dev": true }, "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true }, "jsonify": { "version": "0.0.0", @@ -3523,14 +3547,14 @@ "dev": true }, "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true, "requires": { - "big.js": "^5.2.2", + "big.js": "^3.1.3", "emojis-list": "^2.0.0", - "json5": "^1.0.1" + "json5": "^0.5.0" } }, "locate-path": { @@ -3617,18 +3641,18 @@ } }, "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.6", + "resolved": "https://registry.npmjs.org/matchit/-/matchit-1.0.6.tgz", + "integrity": "sha1-gl2gZGi9Mk8CGevijhKkG/tVJMQ=", "dev": true, "requires": { "@arr/every": "^1.0.0" } }, "math-random": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", + "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", "dev": true }, "md5.js": { @@ -3690,9 +3714,9 @@ } }, "mime": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", - "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", + "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", "dev": true }, "mimic-fn": { @@ -3724,7 +3748,7 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, @@ -3769,7 +3793,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { @@ -3797,7 +3821,7 @@ "dependencies": { "commander": { "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, @@ -3824,12 +3848,6 @@ "path-is-absolute": "^1.0.0" } }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -3997,13 +4015,13 @@ } }, "normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-ZVuHxWJv1bopjv/SD5uPhgwUhLqxdJ+SsdUQbGR9HWlXrvnd/C08Cn9Bq48PbvX3y5V97GIpAHpL5Bk9BwChGg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { "hosted-git-info": "^2.1.4", - "is-builtin-module": "^3.0.0", + "is-builtin-module": "^1.0.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } @@ -4532,9 +4550,9 @@ "dev": true }, "puppeteer": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.11.0.tgz", - "integrity": "sha512-iG4iMOHixc2EpzqRV+pv7o3GgmU2dNYEMkvKwSaQO/vMZURakwSOn/EYJ6OIRFYOque1qorzIBvrytPIQB3YzQ==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.12.0.tgz", + "integrity": "sha512-+riSxJFPQpwGZvNHFeB7vEefwfdHNSstQmjdzUKZxPp/Qt1Dw9iKRAewl8X0ntdXZz4UR4jODLiM03Iw9HDnyw==", "dev": true, "requires": { "debug": "^4.1.0", @@ -4560,9 +4578,9 @@ "dev": true }, "randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", + "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==", "dev": true, "requires": { "is-number": "^4.0.0", @@ -4996,12 +5014,12 @@ "dev": true }, "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", "dev": true, "requires": { - "path-parse": "^1.0.6" + "path-parse": "^1.0.5" } }, "resolve-from": { @@ -5060,6 +5078,14 @@ "@types/estree": "0.0.39", "@types/node": "*", "acorn": "^6.0.5" + }, + "dependencies": { + "acorn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.6.tgz", + "integrity": "sha512-5M3G/A4uBSMIlfJ+h9W125vJvPFH/zirISsW5qfxF5YzEvXJCtolLoQvM5yZft0DvMcUrPGKPOlgEu55I6iUtA==", + "dev": true + } } }, "rollup-plugin-commonjs": { @@ -5092,6 +5118,14 @@ "builtin-modules": "^3.0.0", "is-module": "^1.0.0", "resolve": "^1.8.1" + }, + "dependencies": { + "builtin-modules": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.0.0.tgz", + "integrity": "sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg==", + "dev": true + } } }, "rollup-plugin-replace": { @@ -5122,7 +5156,7 @@ }, "rollup-pluginutils": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz", "integrity": "sha1-HhVud4+UtyVb+hs9AXi+j1xVJAg=", "dev": true, "requires": { @@ -5141,6 +5175,14 @@ "require-relative": "^0.8.7", "rollup-pluginutils": "^2.3.3", "sourcemap-codec": "^1.4.4" + }, + "dependencies": { + "sourcemap-codec": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz", + "integrity": "sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg==", + "dev": true + } } }, "rollup-plugin-typescript": { @@ -5532,9 +5574,9 @@ "integrity": "sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg==" }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", + "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -5558,9 +5600,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.1", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz", + "integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==", "dev": true }, "split-string": { @@ -5825,9 +5867,9 @@ } }, "tiny-glob": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.6.tgz", - "integrity": "sha512-A7ewMqPu1B5PWwC3m7KVgAu96Ch5LA0w4SnEN/LbDREj/gAD0nPWboRbn8YoP9ISZXqeNAlMvKSKoEuhcfK3Pw==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.2.tgz", + "integrity": "sha512-o8rak1FRmr55Nd1Bdcfd+yetPGclFCVHXiKmoBHYULc+FQXBbqb9S3zKAWyqk+RdWvutlGUOw0kCHC0JLF/T4Q==", "dev": true, "requires": { "globalyzer": "^0.1.0", @@ -5940,9 +5982,9 @@ "dev": true }, "typescript": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz", - "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.1.tgz", + "integrity": "sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA==", "dev": true }, "uglify-js": { @@ -6570,6 +6612,12 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.0.0.tgz", "integrity": "sha512-+Wo/p5VRfxUgBUGy2j/6KX2mj9AYJWOHuhMjMcbBFc3y54o9/4buK1ksBvuiK01C3kby8DH9lSmJdSxw+4G/2Q==", "dev": true + }, + "yootils": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/yootils/-/yootils-0.0.14.tgz", + "integrity": "sha512-yWoA/a/4aVUp5nqfqdjbTdyXcR8d0OAbRQ8Ktu3ZsfQnArwLpS81oqZl3adIszX3p8NEhT0aNHARPsaTwBH/Qw==", + "dev": true } } } diff --git a/package.json b/package.json index beb95b5..97c282b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sapper", - "version": "0.25.0-alpha2", + "version": "0.26.0-alpha.1", "description": "Military-grade apps, engineered by Svelte", "bin": { "sapper": "./sapper" @@ -18,36 +18,36 @@ "test": "test" }, "dependencies": { - "html-minifier": "^3.5.20", + "html-minifier": "^3.5.21", "shimport": "0.0.14", - "source-map-support": "^0.5.9", - "sourcemap-codec": "^1.4.3", + "source-map-support": "^0.5.10", + "sourcemap-codec": "^1.4.4", "string-hash": "^1.1.3", "tslib": "^1.9.3" }, "devDependencies": { "@types/mkdirp": "^0.5.2", "@types/mocha": "^5.2.5", - "@types/node": "^10.12.0", - "@types/puppeteer": "^1.9.0", + "@types/node": "^10.12.21", + "@types/puppeteer": "^1.11.3", "@types/rimraf": "^2.0.2", "agadoo": "^1.0.1", - "cheap-watch": "^1.0.0", + "cheap-watch": "^1.0.2", "cookie": "^0.3.1", - "devalue": "^1.0.4", - "eslint": "^5.7.0", - "eslint-plugin-import": "^2.14.0", + "devalue": "^1.1.0", + "eslint": "^5.12.1", + "eslint-plugin-import": "^2.16.0", "kleur": "^3.0.1", "mkdirp": "^0.5.1", "mocha": "^5.2.0", - "node-fetch": "^2.2.0", + "node-fetch": "^2.3.0", "npm-run-all": "^4.1.5", "polka": "^0.5.1", "port-authority": "^1.0.5", "pretty-bytes": "^5.1.0", - "puppeteer": "^1.9.0", + "puppeteer": "^1.12.0", "require-relative": "^0.8.7", - "rimraf": "^2.6.2", + "rimraf": "^2.6.3", "rollup": "^1.1.2", "rollup-plugin-commonjs": "^9.2.0", "rollup-plugin-json": "^3.1.0", @@ -56,15 +56,16 @@ "rollup-plugin-string": "^2.0.2", "rollup-plugin-svelte": "^5.0.1", "rollup-plugin-typescript": "^1.0.0", - "sade": "^1.4.1", + "sade": "^1.4.2", "sander": "^0.6.0", "sirv": "^0.2.2", "svelte": "^3.0.0-alpha26", - "svelte-loader": "^2.11.0", + "svelte-loader": "^2.12.0", "ts-node": "^8.0.2", - "typescript": "^3.1.3", - "webpack": "^4.20.2", - "webpack-format-messages": "^2.0.3" + "typescript": "^3.3.1", + "webpack": "^4.29.0", + "webpack-format-messages": "^2.0.5", + "yootils": "0.0.14" }, "scripts": { "test": "mocha --opts mocha.opts", diff --git a/src/api/build.ts b/src/api/build.ts index 2fbeff0..85f88ca 100644 --- a/src/api/build.ts +++ b/src/api/build.ts @@ -42,7 +42,6 @@ export async function build({ routes = path.resolve(cwd, routes); output = path.resolve(cwd, output); static_files = path.resolve(cwd, static_files); - dest = path.resolve(cwd, dest); if (legacy && bundler === 'webpack') { throw new Error(`Legacy builds are not supported for projects using webpack`); @@ -119,10 +118,15 @@ export async function build({ let serviceworker_stats; if (serviceworker) { + + const client_files = client_result.chunks + .filter(chunk => !chunk.file.endsWith('.map')) // SW does not need to cache sourcemap files + .map(chunk => `client/${chunk.file}`); + create_serviceworker_manifest({ manifest_data, output, - client_files: client_result.chunks.map(chunk => `client/${chunk.file}`), + client_files, static_files }); diff --git a/src/api/export.ts b/src/api/export.ts index 482ba51..ed601e4 100644 --- a/src/api/export.ts +++ b/src/api/export.ts @@ -3,6 +3,7 @@ import * as path from 'path'; import * as sander from 'sander'; import * as url from 'url'; import fetch from 'node-fetch'; +import * as yootils from 'yootils'; import * as ports from 'port-authority'; import clean_html from './utils/clean_html'; import minify_html from './utils/minify_html'; @@ -94,7 +95,9 @@ async function _export({ const is_html = type === 'text/html'; if (is_html) { - file = file === '' ? 'index.html' : `${file}/index.html`; + if (pathname !== '/service-worker-index.html') { + file = file === '' ? 'index.html' : `${file}/index.html`; + } body = minify_html(body); } @@ -113,7 +116,10 @@ async function _export({ }); async function handle(url: URL) { - const pathname = (url.pathname.replace(root.pathname, '') || '/'); + let pathname = url.pathname; + if (pathname !== '/service-worker-index.html') { + pathname = pathname.replace(root.pathname, '') || '/' + } if (seen.has(pathname)) return; seen.add(pathname); @@ -138,11 +144,12 @@ async function _export({ const range = ~~(r.status / 100); if (range === 2) { - if (type === 'text/html') { - const urls: URL[] = []; - + if (type === 'text/html' && pathname !== '/service-worker-index.html') { const cleaned = clean_html(body); + const q = yootils.queue(8); + let promise; + const base_match = //m.exec(cleaned); const base_href = base_match && get_href(base_match[1]); const base = resolve(url.href, base_href); @@ -158,12 +165,12 @@ async function _export({ const url = resolve(base.href, href); if (url.protocol === protocol && url.host === host) { - urls.push(url); + promise = q.add(() => handle(url)); } } } - await Promise.all(urls.map(handle)); + await promise; } } @@ -181,6 +188,7 @@ async function _export({ return ports.wait(port) .then(() => handle(root)) + .then(() => handle(resolve(root.href, 'service-worker-index.html'))) .then(() => proc.kill()) .catch(err => { proc.kill(); diff --git a/src/core/create_manifests.ts b/src/core/create_manifests.ts index abb752f..2f28f17 100644 --- a/src/core/create_manifests.ts +++ b/src/core/create_manifests.ts @@ -48,17 +48,15 @@ export function create_serviceworker_manifest({ manifest_data, output, client_fi client_files: string[]; static_files: string; }) { - let files: string[]; + let files: string[] = ['/service-worker-index.html']; if (fs.existsSync(static_files)) { - files = walk(static_files); + files = files.concat(walk(static_files)); } else { // TODO remove in a future version if (fs.existsSync('assets')) { throw new Error(`As of Sapper 0.21, the assets/ directory should become static/`); } - - files = []; } let code = ` diff --git a/templates/src/app/app.ts b/templates/src/app/app.ts index 2c02d6d..d6c8e0a 100644 --- a/templates/src/app/app.ts +++ b/templates/src/app/app.ts @@ -38,7 +38,7 @@ const root_props: RootProps = { export let prefetching: { href: string; - promise: Promise<{ redirect?: Redirect, data?: any, nullable_depth?: number }>; + promise: Promise<{ redirect?: Redirect, data?: any, nullable_depth?: number, new_segments?: any }>; } = null; export function set_prefetching(href, promise) { prefetching = { href, promise }; @@ -141,10 +141,13 @@ export function navigate(target: Target, id: number, noscroll?: boolean, hash?: const token = current_token = {}; - return loaded.then(({ redirect, data, nullable_depth }) => { + return loaded.then(({ redirect, data, nullable_depth, new_segments }) => { if (redirect) { return goto(redirect.location, { replaceState: true }); } + if (new_segments) { + segments = new_segments; + } render(data, nullable_depth, scroll_history[id], noscroll, hash, token); if (document.activeElement) document.activeElement.blur(); }); @@ -231,6 +234,10 @@ export function prepare_page(target: Target): Promise<{ segments[changed_from] === new_segments[changed_from] ) changed_from += 1; + if (changed_from === new_segments.length) { + changed_from -= 1; + } + let redirect: Redirect = null; let error: { statusCode: number, message: Error | string } = null; @@ -297,11 +304,9 @@ export function prepare_page(target: Target): Promise<{ } }).then(results => { if (redirect) { - return { redirect }; + return { redirect, new_segments }; } - segments = new_segments; - const get_params = page.parts[page.parts.length - 1].params || (() => ({})); const params = get_params(target.match); @@ -316,6 +321,7 @@ export function prepare_page(target: Target): Promise<{ return { nullable_depth: 0, + new_segments, data: Object.assign({}, props, { child: { component: ErrorComponent, @@ -329,7 +335,7 @@ export function prepare_page(target: Target): Promise<{ const data = { path, child: Object.assign({}, root_props.child, { - segment: segments[0] + segment: new_segments[0] }) }; if (changed(query, root_props.query)) data.query = query; @@ -360,10 +366,10 @@ export function prepare_page(target: Target): Promise<{ } level = level.props.child; - level.segment = segments[i + 1]; + level.segment = new_segments[i + 1]; } - return { data, nullable_depth }; + return { data, nullable_depth, new_segments }; }); } @@ -400,4 +406,4 @@ function detach(node: Node) { function changed(a: Record, b: Record) { return JSON.stringify(a) !== JSON.stringify(b); -} \ No newline at end of file +} diff --git a/templates/src/server/middleware/get_page_handler.ts b/templates/src/server/middleware/get_page_handler.ts index aa7b5e3..a2ccc19 100644 --- a/templates/src/server/middleware/get_page_handler.ts +++ b/templates/src/server/middleware/get_page_handler.ts @@ -1,9 +1,9 @@ -import * as fs from 'fs'; -import * as path from 'path'; +import fs from 'fs'; +import path from 'path'; import cookie from 'cookie'; import devalue from 'devalue'; import fetch from 'node-fetch'; -import { URL, resolve } from 'url'; +import URL from 'url'; import * as stores from '../../shared/stores'; import { build_dir, dev, src_dir, IGNORE } from '../placeholders'; import { Manifest, Page, Props, Req, Res } from './types'; @@ -36,6 +36,7 @@ export function get_page_handler( } async function handle_page(page: Page, req: Req, res: Res, status = 200, error: Error | string = null) { + const isSWIndexHtml = req.path === '/service-worker-index.html'; const build_info: { bundler: 'rollup' | 'webpack', shimport: string | null, @@ -49,7 +50,7 @@ export function get_page_handler( // preload main.js and current route // TODO detect other stuff we can preload? images, CSS, fonts? let preloaded_chunks = Array.isArray(build_info.assets.main) ? build_info.assets.main : [build_info.assets.main]; - if (!error) { + if (!error && !isSWIndexHtml) { page.parts.forEach(part => { if (!part) return; @@ -95,7 +96,7 @@ export function get_page_handler( preload_error = { statusCode, message }; }, fetch: (url: string, opts?: any) => { - const parsed = new URL(url, `http://127.0.0.1:${process.env.PORT}${req.baseUrl ? req.baseUrl + '/' :''}`); + const parsed = new URL.URL(url, `http://127.0.0.1:${process.env.PORT}${req.baseUrl ? req.baseUrl + '/' :''}`); if (opts) { opts = Object.assign({}, opts); @@ -106,7 +107,7 @@ export function get_page_handler( ); if (include_cookies) { - if (!opts.headers) opts.headers = {}; + opts.headers = Object.assign({}, opts.headers); const cookies = Object.assign( {}, @@ -146,17 +147,22 @@ export function get_page_handler( match = error ? null : page.pattern.exec(req.path); - preloaded = await Promise.all([root_preloaded].concat(page.parts.map(part => { - if (!part) return null; + let toPreload = [root_preloaded]; + if (!isSWIndexHtml) { + toPreload = toPreload.concat(page.parts.map(part => { + if (!part) return null; - return part.preload - ? part.preload.call(preload_context, { - path: req.path, - query: req.query, - params: part.params ? part.params(match) : {} - }) - : {}; - }))); + return part.preload + ? part.preload.call(preload_context, { + path: req.path, + query: req.query, + params: part.params ? part.params(match) : {} + }) + : {}; + })) + } + + preloaded = await Promise.all(toPreload); } catch (err) { preload_error = { statusCode: 500, message: err }; preloaded = []; // appease TypeScript @@ -164,7 +170,7 @@ export function get_page_handler( try { if (redirect) { - const location = resolve(req.baseUrl || '/', redirect.location); + const location = URL.resolve(req.baseUrl || '/', redirect.location); res.statusCode = redirect.statusCode; res.setHeader('Location', location); @@ -200,23 +206,29 @@ export function get_page_handler( }); let level = data.child; - for (let i = 0; i < page.parts.length; i += 1) { - const part = page.parts[i]; - if (!part) continue; + if (isSWIndexHtml) { + level.props = Object.assign({}, props, { + params: {} + }) + } else { + for (let i = 0; i < page.parts.length; i += 1) { + const part = page.parts[i]; + if (!part) continue; - const get_params = part.params || (() => ({})); + const get_params = part.params || (() => ({})); - Object.assign(level, { - component: part.component, - props: Object.assign({}, props, { - params: get_params(match) - }, preloaded[i + 1]) - }); + Object.assign(level, { + component: part.component, + props: Object.assign({}, props, { + params: get_params(match) + }, preloaded[i + 1]) + }); - level.props.child = { - segment: segments[i + 1] - }; - level = level.props.child; + level.props.child = { + segment: segments[i + 1] + }; + level = level.props.child; + } } stores.page.set({ @@ -314,6 +326,12 @@ export function get_page_handler( return function find_route(req: Req, res: Res, next: () => void) { if (req[IGNORE]) return next(); + if (req.path === '/service-worker-index.html') { + const homePage = pages.find(page => page.pattern.test('/')); + handle_page(homePage, req, res); + return; + } + if (!server_routes.some(route => route.pattern.test(req.path))) { for (const page of pages) { if (page.pattern.test(req.path)) { diff --git a/templates/src/server/middleware/index.ts b/templates/src/server/middleware/index.ts index ae6dcb7..f2e3a88 100644 --- a/templates/src/server/middleware/index.ts +++ b/templates/src/server/middleware/index.ts @@ -1,5 +1,5 @@ -import * as fs from 'fs'; -import * as path from 'path'; +import fs from 'fs'; +import path from 'path'; import { build_dir, dev, manifest, IGNORE } from '../placeholders'; import { Handler, Req, Res, Store } from './types'; import { get_server_route_handler } from './get_server_route_handler'; diff --git a/test/apps/AppRunner.ts b/test/apps/AppRunner.ts index 84dd6b6..4bfdcce 100644 --- a/test/apps/AppRunner.ts +++ b/test/apps/AppRunner.ts @@ -64,11 +64,11 @@ export class AppRunner { base: `http://localhost:${this.port}`, // helpers - start: () => this.page.evaluate(() => start()), - prefetchRoutes: () => this.page.evaluate(() => prefetchRoutes()), - prefetch: (href: string) => this.page.evaluate((href: string) => prefetch(href), href), - goto: (href: string) => this.page.evaluate((href: string) => goto(href), href), - title: () => this.page.$eval('h1', node => node.textContent) + start: () => this.page.evaluate(() => start()).then(() => void 0), + prefetchRoutes: () => this.page.evaluate(() => prefetchRoutes()).then(() => void 0), + prefetch: (href: string) => this.page.evaluate((href: string) => prefetch(href), href).then(() => void 0), + goto: (href: string) => this.page.evaluate((href: string) => goto(href), href).then(() => void 0), + title: () => this.page.$eval('h1', node => node.textContent).then(serializable => String(serializable)) }; } diff --git a/test/apps/basics/test.ts b/test/apps/basics/test.ts index c27b519..c2d28d3 100644 --- a/test/apps/basics/test.ts +++ b/test/apps/basics/test.ts @@ -261,7 +261,7 @@ describe('basics', function() { await page.goto(`${base}/unsafe-replacement`); await start(); - const html = await page.evaluate(() => document.body.innerHTML); + const html = String(await page.evaluate(() => document.body.innerHTML)); assert.equal(html.indexOf('%sapper'), -1); }); }); \ No newline at end of file diff --git a/test/apps/export/test.ts b/test/apps/export/test.ts index 9bb8339..897bc5f 100644 --- a/test/apps/export/test.ts +++ b/test/apps/export/test.ts @@ -30,6 +30,7 @@ describe('export', function() { 'blog/index.html', 'global.css', 'index.html', + 'service-worker-index.html', 'service-worker.js' ]); }); diff --git a/test/apps/layout/test.ts b/test/apps/layout/test.ts index 1cadbe1..36eda4e 100644 --- a/test/apps/layout/test.ts +++ b/test/apps/layout/test.ts @@ -28,7 +28,7 @@ describe('layout', function() { await page.goto(`${base}/foo/bar/baz`); await start(); - const text1 = await page.evaluate(() => document.querySelector('#sapper').textContent); + const text1 = String(await page.evaluate(() => document.querySelector('#sapper').textContent)); assert.deepEqual(text1.split('\n').filter(Boolean), [ 'y: bar 1', 'z: baz 1', @@ -39,7 +39,7 @@ describe('layout', function() { await page.click('[href="foo/bar/qux"]'); await wait(50); - const text2 = await page.evaluate(() => document.querySelector('#sapper').textContent); + const text2 = String(await page.evaluate(() => document.querySelector('#sapper').textContent)); assert.deepEqual(text2.split('\n').filter(Boolean), [ 'y: bar 1', 'z: qux 2', diff --git a/test/apps/preloading/src/routes/index.html b/test/apps/preloading/src/routes/index.html index e269e81..c214f0c 100644 --- a/test/apps/preloading/src/routes/index.html +++ b/test/apps/preloading/src/routes/index.html @@ -1,4 +1,6 @@

Great success!

slow preload -foo \ No newline at end of file +foo +prefetch qwe +prefetch xyz diff --git a/test/apps/preloading/src/routes/prefetch/[slug]/index.html b/test/apps/preloading/src/routes/prefetch/[slug]/index.html new file mode 100644 index 0000000..d13188c --- /dev/null +++ b/test/apps/preloading/src/routes/prefetch/[slug]/index.html @@ -0,0 +1 @@ +

{params.slug}

diff --git a/test/apps/preloading/src/routes/prefetch/_layout.html b/test/apps/preloading/src/routes/prefetch/_layout.html new file mode 100644 index 0000000..f11eb2a --- /dev/null +++ b/test/apps/preloading/src/routes/prefetch/_layout.html @@ -0,0 +1 @@ + diff --git a/test/apps/preloading/src/routes/prefetch/index.html b/test/apps/preloading/src/routes/prefetch/index.html new file mode 100644 index 0000000..7771d91 --- /dev/null +++ b/test/apps/preloading/src/routes/prefetch/index.html @@ -0,0 +1 @@ +

prefetch

diff --git a/test/apps/preloading/test.ts b/test/apps/preloading/test.ts index 8445c86..d30a8d1 100644 --- a/test/apps/preloading/test.ts +++ b/test/apps/preloading/test.ts @@ -81,4 +81,31 @@ describe('preloading', function() { assert.equal(page.url(), `${base}/foo`); assert.equal(await title(), 'foo'); }); -}); \ No newline at end of file + + it('navigates to prefetched urls', async () => { + await page.goto(base); + await start(); + await prefetchRoutes(); + + await page.hover('a[href="prefetch/qwe"]'); + await wait(100); + await page.hover('a[href="prefetch/xyz"]'); + await wait(100); + + await page.click('a[href="prefetch/qwe"]'); + await wait(50); + + assert.equal( + await title(), + 'qwe' + ); + + await page.goto(`${base}/prefetch`); + await wait(50); + + assert.equal( + await title(), + 'prefetch' + ); + }); +}); diff --git a/test/apps/scroll/test.ts b/test/apps/scroll/test.ts index 6bae656..11130ff 100644 --- a/test/apps/scroll/test.ts +++ b/test/apps/scroll/test.ts @@ -31,7 +31,7 @@ describe('scroll', function() { await start(); const scrollY = await page.evaluate(() => window.scrollY); - assert.ok(scrollY > 0, scrollY); + assert.ok(scrollY > 0, String(scrollY)); }); it('scrolls to any deeplink if it was already active', async () => { @@ -39,17 +39,17 @@ describe('scroll', function() { await start(); let scrollY = await page.evaluate(() => window.scrollY); - assert.ok(scrollY > 0, scrollY); + assert.ok(scrollY > 0, String(scrollY)); scrollY = await page.evaluate(() => { window.scrollTo(0, 0) return window.scrollY }); - assert.ok(scrollY === 0, scrollY); + assert.ok(scrollY === 0, String(scrollY)); await page.click('[href="tall-page#foo"]'); scrollY = await page.evaluate(() => window.scrollY); - assert.ok(scrollY > 0, scrollY); + assert.ok(scrollY > 0, String(scrollY)); }); it('resets scroll when a link is clicked', async () => { diff --git a/test/apps/with-basepath/test.ts b/test/apps/with-basepath/test.ts index 93bbcbf..5741d1b 100644 --- a/test/apps/with-basepath/test.ts +++ b/test/apps/with-basepath/test.ts @@ -56,6 +56,7 @@ describe('with-basepath', function() { assert.deepEqual(non_client_assets, [ 'custom-basepath/global.css', 'custom-basepath/index.html', + 'custom-basepath/service-worker-index.html', 'custom-basepath/service-worker.js' ]); }); diff --git a/test/apps/with-sourcemaps-webpack/src/client.js b/test/apps/with-sourcemaps-webpack/src/client.js new file mode 100644 index 0000000..6cce7e6 --- /dev/null +++ b/test/apps/with-sourcemaps-webpack/src/client.js @@ -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); \ No newline at end of file diff --git a/test/apps/with-sourcemaps-webpack/src/routes/_error.html b/test/apps/with-sourcemaps-webpack/src/routes/_error.html new file mode 100644 index 0000000..4cd55d2 --- /dev/null +++ b/test/apps/with-sourcemaps-webpack/src/routes/_error.html @@ -0,0 +1,3 @@ +

{status}

+ +

{error.message}

\ No newline at end of file diff --git a/test/apps/with-sourcemaps-webpack/src/routes/index.html b/test/apps/with-sourcemaps-webpack/src/routes/index.html new file mode 100644 index 0000000..abaff72 --- /dev/null +++ b/test/apps/with-sourcemaps-webpack/src/routes/index.html @@ -0,0 +1,3 @@ +

Great success!

+ +

Woot!

\ No newline at end of file diff --git a/test/apps/with-sourcemaps-webpack/src/server.js b/test/apps/with-sourcemaps-webpack/src/server.js new file mode 100644 index 0000000..7f090b8 --- /dev/null +++ b/test/apps/with-sourcemaps-webpack/src/server.js @@ -0,0 +1,8 @@ +import polka from 'polka'; +import * as sapper from '@sapper/server'; + +const { PORT } = process.env; + +polka() + .use(sapper.middleware()) + .listen(PORT); diff --git a/test/apps/with-sourcemaps-webpack/src/service-worker.js b/test/apps/with-sourcemaps-webpack/src/service-worker.js new file mode 100644 index 0000000..67da6f0 --- /dev/null +++ b/test/apps/with-sourcemaps-webpack/src/service-worker.js @@ -0,0 +1,82 @@ +import { timestamp, files, shell, routes } from '@sapper/service-worker'; + +const ASSETS = `cache${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 = shell.concat(ASSETS); +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${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; + } + }) + ); +}); diff --git a/test/apps/with-sourcemaps-webpack/src/template.html b/test/apps/with-sourcemaps-webpack/src/template.html new file mode 100644 index 0000000..0eb1f3b --- /dev/null +++ b/test/apps/with-sourcemaps-webpack/src/template.html @@ -0,0 +1,14 @@ + + + + + + %sapper.base% + %sapper.styles% + %sapper.head% + + +
%sapper.html%
+ %sapper.scripts% + + diff --git a/test/apps/with-sourcemaps-webpack/test.ts b/test/apps/with-sourcemaps-webpack/test.ts new file mode 100644 index 0000000..cb0a3ca --- /dev/null +++ b/test/apps/with-sourcemaps-webpack/test.ts @@ -0,0 +1,43 @@ +import * as puppeteer from 'puppeteer'; +import { build } from '../../../api'; +import * as assert from "assert"; +import { AppRunner } from '../AppRunner'; +import * as fs from "fs"; +import * as path from "path"; + +describe('with-sourcemaps-webpack', function() { + this.timeout(10000); + + let runner: AppRunner; + let page: puppeteer.Page; + let base: string; + + // helpers + let start: () => Promise; + let prefetchRoutes: () => Promise; + let prefetch: (href: string) => Promise; + let goto: (href: string) => Promise; + + // hooks + before(async () => { + await build({ cwd: __dirname, bundler: 'webpack' }); + + runner = new AppRunner(__dirname, '__sapper__/build/server/server.js'); + ({ base, page, start, prefetchRoutes, prefetch, goto } = await runner.start()); + }); + + it('does not put sourcemap files in service worker shell', async () => { + const serviceWorker = await import(`${__dirname}/__sapper__/service-worker.js`); + const shell: string[] = serviceWorker.shell; + + assert.equal(shell.filter(_ => _.endsWith('.map')).length, 0, + 'sourcemap files are not cached in SW'); + + const clientShellDir = path.resolve(`${__dirname}/__sapper__/build`, path.dirname(shell[0])); + const sourcemapFiles = fs.readdirSync(clientShellDir).filter(_ => _.endsWith('.map')); + assert.ok(sourcemapFiles.length > 0, 'sourcemap files exist'); + }); + + after(() => runner.end()); + +}); \ No newline at end of file diff --git a/test/apps/with-sourcemaps-webpack/webpack.config.js b/test/apps/with-sourcemaps-webpack/webpack.config.js new file mode 100644 index 0000000..eca656b --- /dev/null +++ b/test/apps/with-sourcemaps-webpack/webpack.config.js @@ -0,0 +1,73 @@ +const webpack = require('webpack'); +const config = require('../../../config/webpack.js'); + +const mode = process.env.NODE_ENV; +const dev = mode === 'development'; + +module.exports = { + client: { + entry: config.client.entry(), + output: config.client.output(), + resolve: { + extensions: ['.mjs', '.js', '.json', '.html'], + mainFields: ['svelte', 'module', 'browser', 'main'] + }, + module: { + rules: [ + { + test: /\.html$/, + use: { + loader: 'svelte-loader', + options: { + dev, + hydratable: true, + hotReload: true + } + } + } + ] + }, + mode, + plugins: [ + dev && new webpack.HotModuleReplacementPlugin(), + new webpack.DefinePlugin({ + 'process.browser': true, + 'process.env.NODE_ENV': JSON.stringify(mode) + }), + ].filter(Boolean), + devtool: dev ? 'inline-source-map' : 'source-map' + }, + + server: { + entry: config.server.entry(), + output: config.server.output(), + target: 'node', + resolve: { + extensions: ['.mjs', '.js', '.json', '.html'], + mainFields: ['svelte', 'module', 'browser', 'main'] + }, + module: { + rules: [ + { + test: /\.html$/, + use: { + loader: 'svelte-loader', + options: { + css: false, + generate: 'ssr', + dev + } + } + } + ] + }, + mode: process.env.NODE_ENV + }, + + serviceworker: { + entry: config.serviceworker.entry(), + output: config.serviceworker.output(), + mode: process.env.NODE_ENV, + devtool: 'sourcemap' + } +}; diff --git a/test/apps/with-sourcemaps/rollup.config.js b/test/apps/with-sourcemaps/rollup.config.js new file mode 100644 index 0000000..c057e2e --- /dev/null +++ b/test/apps/with-sourcemaps/rollup.config.js @@ -0,0 +1,64 @@ +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: Object.assign({}, config.client.output(), { sourcemap: true }), + plugins: [ + replace({ + 'process.browser': true, + 'process.env.NODE_ENV': JSON.stringify(mode) + }), + svelte({ + dev, + hydratable: true, + emitCss: true + }), + resolve() + ], + + // temporary, pending Rollup 1.0 + experimentalCodeSplitting: true + }, + + 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'], + + // temporary, pending Rollup 1.0 + experimentalCodeSplitting: true + }, + + serviceworker: { + input: config.serviceworker.input(), + output: config.serviceworker.output(), + plugins: [ + resolve(), + replace({ + 'process.browser': true, + 'process.env.NODE_ENV': JSON.stringify(mode) + }) + ] + } +}; \ No newline at end of file diff --git a/test/apps/with-sourcemaps/src/client.js b/test/apps/with-sourcemaps/src/client.js new file mode 100644 index 0000000..0865a4a --- /dev/null +++ b/test/apps/with-sourcemaps/src/client.js @@ -0,0 +1,9 @@ +import * as sapper from '../__sapper__/client.js'; + +window.start = () => sapper.start({ + target: document.querySelector('#sapper') +}); + +window.prefetchRoutes = () => sapper.prefetchRoutes(); +window.prefetch = href => sapper.prefetch(href); +window.goto = href => sapper.goto(href); \ No newline at end of file diff --git a/test/apps/with-sourcemaps/src/routes/_error.html b/test/apps/with-sourcemaps/src/routes/_error.html new file mode 100644 index 0000000..4cd55d2 --- /dev/null +++ b/test/apps/with-sourcemaps/src/routes/_error.html @@ -0,0 +1,3 @@ +

{status}

+ +

{error.message}

\ No newline at end of file diff --git a/test/apps/with-sourcemaps/src/routes/index.html b/test/apps/with-sourcemaps/src/routes/index.html new file mode 100644 index 0000000..abaff72 --- /dev/null +++ b/test/apps/with-sourcemaps/src/routes/index.html @@ -0,0 +1,3 @@ +

Great success!

+ +

Woot!

\ No newline at end of file diff --git a/test/apps/with-sourcemaps/src/server.js b/test/apps/with-sourcemaps/src/server.js new file mode 100644 index 0000000..0e7741c --- /dev/null +++ b/test/apps/with-sourcemaps/src/server.js @@ -0,0 +1,8 @@ +import polka from 'polka'; +import * as sapper from '../__sapper__/server.js'; + +const { PORT } = process.env; + +polka() + .use(sapper.middleware()) + .listen(PORT); diff --git a/test/apps/with-sourcemaps/src/service-worker.js b/test/apps/with-sourcemaps/src/service-worker.js new file mode 100644 index 0000000..9d2ac9d --- /dev/null +++ b/test/apps/with-sourcemaps/src/service-worker.js @@ -0,0 +1,82 @@ +import { timestamp, files, shell, routes } from '../__sapper__/service-worker.js'; + +const ASSETS = `cache${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 = shell.concat(ASSETS); +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${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; + } + }) + ); +}); diff --git a/test/apps/with-sourcemaps/src/template.html b/test/apps/with-sourcemaps/src/template.html new file mode 100644 index 0000000..0eb1f3b --- /dev/null +++ b/test/apps/with-sourcemaps/src/template.html @@ -0,0 +1,14 @@ + + + + + + %sapper.base% + %sapper.styles% + %sapper.head% + + +
%sapper.html%
+ %sapper.scripts% + + diff --git a/test/apps/with-sourcemaps/test.ts b/test/apps/with-sourcemaps/test.ts new file mode 100644 index 0000000..1b61c28 --- /dev/null +++ b/test/apps/with-sourcemaps/test.ts @@ -0,0 +1,43 @@ +import * as puppeteer from 'puppeteer'; +import { build } from '../../../api'; +import * as assert from "assert"; +import { AppRunner } from '../AppRunner'; +import * as fs from 'fs'; +import * as path from "path"; + +describe('with-sourcemaps', function() { + this.timeout(10000); + + let runner: AppRunner; + let page: puppeteer.Page; + let base: string; + + // helpers + let start: () => Promise; + let prefetchRoutes: () => Promise; + let prefetch: (href: string) => Promise; + let goto: (href: string) => Promise; + + // hooks + before(async () => { + await build({ cwd: __dirname }); + + runner = new AppRunner(__dirname, '__sapper__/build/server/server.js'); + ({ base, page, start, prefetchRoutes, prefetch, goto } = await runner.start()); + }); + + it('does not put sourcemap files in service worker shell', async () => { + const serviceWorker = await import(`${__dirname}/__sapper__/service-worker.js`); + const shell: string[] = serviceWorker.shell; + + assert.equal(shell.filter(_ => _.endsWith('.map')).length, 0, + 'sourcemap files are not cached in SW'); + + const clientShellDir = path.resolve(`${__dirname}/__sapper__/build`, path.dirname(shell[0])); + const sourcemapFiles = fs.readdirSync(clientShellDir).filter(_ => _.endsWith('.map')); + assert.ok(sourcemapFiles.length > 0, 'sourcemap files exist'); + }); + + after(() => runner.end()); + +}); \ No newline at end of file