Compare commits

..

7 Commits

Author SHA1 Message Date
Rich Harris
666c113297 -> v0.15.6 2018-08-06 22:36:17 -04:00
Rich Harris
84a58f34a0 add test for exporting with custom basepath 2018-08-06 22:35:02 -04:00
Rich Harris
75f5b5c721 Merge pull request #342 from aubergene/gh-338
Remove basepath from deferred urls and add trailing slash to root
2018-08-06 22:06:09 -04:00
Julian Burgess
a176a3b79b Remove basepath from deferred urls and add trailing slash to root request 2018-08-06 16:43:02 +01:00
Rich Harris
1627a5767a -> v0.15.5 2018-08-03 01:18:12 -04:00
Rich Harris
6ff3a9e9ab Merge branch 'master' of github.com:sveltejs/sapper 2018-08-03 01:16:43 -04:00
Rich Harris
3ce2bd30f9 Use npm ci instead of npm install (#336)
* dont write server_info.json either - second half of #318

* use npm ci

* update lockfile

* try this
2018-08-03 01:16:26 -04:00
12 changed files with 45 additions and 223 deletions

View File

@@ -18,4 +18,4 @@ addons:
install:
- export DISPLAY=':99.0'
- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
- npm install
- npm ci || npm i

View File

@@ -1,5 +1,9 @@
# sapper changelog
## 0.15.6
* Fix exporting with custom basepath ([#342](https://github.com/sveltejs/sapper/pull/342))
## 0.15.5
* Faster `export` with more explanatory output ([#335](https://github.com/sveltejs/sapper/pull/335))

View File

@@ -14,7 +14,7 @@ environment:
install:
- ps: Install-Product node $env:nodejs_version
- npm install
- npm ci
test_script:
- node --version && npm --version

View File

@@ -1,6 +1,6 @@
{
"name": "sapper",
"version": "0.15.4",
"version": "0.15.6",
"description": "Military-grade apps, engineered by Svelte",
"main": "dist/middleware.ts.js",
"bin": {

View File

@@ -224,8 +224,11 @@ class Watcher extends EventEmitter {
});
this.proc.on('message', message => {
if (!message.__sapper__) return;
this.emit(message.event, message);
if (message.__sapper__ && message.event === 'basepath') {
this.emit('basepath', {
basepath: message.basepath
});
}
});
this.proc.on('exit', emitFatal);

View File

@@ -51,9 +51,10 @@ async function execute(emitter: EventEmitter, {
const port = await ports.find(3000);
const origin = `http://localhost:${port}`;
const root = new URL(basepath || '', origin);
emitter.emit('info', {
message: `Crawling ${origin}`
message: `Crawling ${root.href}`
});
const proc = child_process.fork(path.resolve(`${build}/server.js`), [], {
@@ -71,8 +72,10 @@ async function execute(emitter: EventEmitter, {
const deferreds = new Map();
function get_deferred(pathname: string) {
pathname = pathname.replace(root.pathname, '');
if (!deferreds.has(pathname)) {
deferreds.set(pathname, new Deferred()) ;
deferreds.set(pathname, new Deferred());
}
return deferreds.get(pathname);
@@ -107,7 +110,7 @@ async function execute(emitter: EventEmitter, {
});
async function handle(url: URL) {
const pathname = url.pathname || '/';
const pathname = (url.pathname.replace(root.pathname, '') || '/');
if (seen.has(pathname)) return;
seen.add(pathname);
@@ -138,6 +141,9 @@ async function execute(emitter: EventEmitter, {
}
return ports.wait(port)
.then(() => handle(new URL(`/${basepath}`, origin))) // TODO all static routes
.then(() => {
// TODO all static routes
return handle(root);
})
.then(() => proc.kill());
}

View File

@@ -2,7 +2,6 @@ import * as path from 'path';
import * as colors from 'ansi-colors';
import * as child_process from 'child_process';
import prettyMs from 'pretty-ms';
import pb from 'pretty-bytes';
import { dev as _dev } from '../api/dev';
import * as events from '../api/interfaces';
@@ -45,32 +44,6 @@ export function dev(opts: { port: number, open: boolean }) {
if (event.log) console.log(event.log);
});
watcher.on('preload', (event) => {
if (event.size > 25000) {
console.log(colors.bold.yellow(`${event.url} — large amount of preloaded data`));
console.log(`${colors.bold(pb(event.size))} of data was preloaded in total, above the recommended limit of ${pb(25000)}`);
}
});
watcher.on('unused_data', (event) => {
console.log(colors.bold.yellow(`${event.url} — unused preloaded data`));
console.log(`More data was returned from \`preload\` than was used during the initial render. Consider only returning essential data.`);
event.discrepancies.forEach(discrepancy => {
console.log(`${colors.bold(discrepancy.file)} loaded ${colors.bold(pb(discrepancy.preloaded))}, of which ${discrepancy.rendered > 2 ? `only ${colors.bold(pb(discrepancy.rendered))}` : 'none'} was used. The following properties were not referenced:`);
const slice = discrepancy.props.length > 12
? discrepancy.props.slice(0, 10)
: discrepancy.props;
console.log(slice.map((prop: string) => `${prop}`).join('\n'));
if (discrepancy.props.length > slice.length) {
console.log(`...and ${discrepancy.props.length - slice.length} more`);
}
});
});
watcher.on('build', (event: events.BuildEvent) => {
if (event.errors.length) {
console.log(`${colors.bold.red(`${event.type}`)}`);

View File

@@ -156,7 +156,6 @@ function generate_server(
const props = [
`name: "${part.component.name}"`,
`file: "${part.component.file}"`,
`component: ${part.component.name}`
];

View File

@@ -8,9 +8,6 @@ import fetch from 'node-fetch';
import { lookup } from './middleware/mime';
import { locations, dev } from './config';
import sourceMapSupport from 'source-map-support';
import prettyBytes from 'pretty-bytes';
import { wrap_data } from './middleware/wrap_data';
import { list_unused_properties } from './middleware/list_unused_properties';
sourceMapSupport.install();
@@ -293,8 +290,6 @@ function get_page_handler(manifest: Manifest, store_getter: (req: Req) => Store)
const { server_routes, pages } = manifest;
const error_route = manifest.error;
const should_wrap_data = dev() || process.env.SAPPER_EXPORT;
function handle_error(req: Req, res: ServerResponse, statusCode: number, error: Error | string) {
handle_page({
pattern: null,
@@ -433,6 +428,11 @@ function get_page_handler(manifest: Manifest, store_getter: (req: Req) => Store)
return;
}
const serialized = {
preloaded: `[${preloaded.map(data => try_serialize(data)).join(',')}]`,
store: store && try_serialize(store.get())
};
const segments = req.path.split('/').filter(Boolean);
const props: Props = {
@@ -454,15 +454,6 @@ function get_page_handler(manifest: Manifest, store_getter: (req: Req) => Store)
}
});
// in dev and export modes, we wrap data in proxies to see
// how much of it is used in the initial render
const wrapped = should_wrap_data && wrap_data(preloaded);
// this is an easy way to 'reify' top-level values
const _preloaded = should_wrap_data
? wrapped.data.map((x: any) => x)
: preloaded;
let level = data.child;
for (let i = 0; i < page.parts.length; i += 1) {
const part = page.parts[i];
@@ -474,7 +465,7 @@ function get_page_handler(manifest: Manifest, store_getter: (req: Req) => Store)
component: part.component,
props: Object.assign({}, props, {
params: get_params(match)
}, _preloaded[i + 1])
}, preloaded[i + 1])
});
level.props.child = <Props["child"]>{
@@ -493,47 +484,6 @@ function get_page_handler(manifest: Manifest, store_getter: (req: Req) => Store)
.map(file => `<script src='${req.baseUrl}/client/${file}'></script>`)
.join('');
const unwrapped = should_wrap_data && wrapped.unwrap();
const preloaded_serialized = preloaded.map(try_serialize);
if (should_wrap_data && process.send) {
const discrepancies = [];
unwrapped.forEach((clone, i) => {
const loaded = preloaded_serialized[i];
if (!loaded) return;
const rendered = try_serialize(clone);
if (rendered !== loaded) {
const part = page.parts[i - 1];
const file = part ? part.file : '_layout.html';
discrepancies.push({
file,
preloaded: loaded.length,
rendered: rendered.length,
props: list_unused_properties(preloaded[i], clone)
});
}
});
if (discrepancies.length) {
process.send({
__sapper__: true,
event: 'unused_data',
url: req.url,
discrepancies
});
}
}
const serialized = {
preloaded: `[${preloaded_serialized.join(',')}]`,
store: store && try_serialize(store.get())
};
let inline_script = `__SAPPER__={${[
error && `error:1`,
`baseUrl:"${req.baseUrl}"`,
@@ -557,13 +507,6 @@ function get_page_handler(manifest: Manifest, store_getter: (req: Req) => Store)
res.end(body);
if (process.send) {
process.send({
__sapper__: true,
event: 'preload',
url: req.url,
size: serialized.preloaded.length
});
process.send({
__sapper__: true,
event: 'file',

View File

@@ -1,34 +0,0 @@
export function list_unused_properties(all: any, used: any) {
const props: string[] = [];
const seen = new Set();
function walk(keypath: string, a: any, b: any) {
if (seen.has(a)) return;
seen.add(a);
if (!a || typeof a !== 'object') return;
const is_array = Array.isArray(a);
for (const key in a) {
const child_keypath = keypath
? is_array ? `${keypath}[${key}]` : `${keypath}.${key}`
: key;
if (hasProp.call(b, key)) {
const a_child = a[key];
const b_child = b[key];
walk(child_keypath, a_child, b_child);
} else {
props.push(child_keypath);
}
}
}
walk(null, all, used);
return props;
}
const hasProp = Object.prototype.hasOwnProperty;

View File

@@ -1,85 +0,0 @@
type Obj = Record<string, any>;
export function wrap_data(data: any) {
const proxies = new Map();
const clones = new Map();
const handler = {
get(target: any, property: string): any {
const value = target[property];
const intercepted = intercept(value);
const target_clone = clones.get(target);
const child_clone = clones.get(value);
if (target_clone && target.hasOwnProperty(property)) {
target_clone[property] = child_clone || value;
}
return intercepted;
},
};
function get_or_create_proxy(obj: any) {
if (!proxies.has(obj)) {
proxies.set(obj, new Proxy(obj, handler));
}
return proxies.get(obj);
}
function intercept(obj: any) {
if (clones.has(obj)) return obj;
if (obj && typeof obj === 'object') {
if (Array.isArray(obj)) {
clones.set(obj, []);
return get_or_create_proxy(obj);
}
else if (isPlainObject(obj)) {
clones.set(obj, {});
return get_or_create_proxy(obj);
}
}
clones.set(obj, obj);
return obj;
}
return {
data: intercept(data),
unwrap: () => {
return clones.get(data);
}
};
}
const objectProtoOwnPropertyNames = Object.getOwnPropertyNames(Object.prototype).sort().join('\0')
function isPlainObject(obj: any) {
const proto = Object.getPrototypeOf(obj);
if (
proto !== Object.prototype &&
proto !== null &&
Object.getOwnPropertyNames(proto).sort().join('\0') !== objectProtoOwnPropertyNames
) {
return false;
}
if (Object.getOwnPropertySymbols(obj).length > 0) {
return false;
}
return true;
}
function pick(obj: Obj, props: string[]) {
const picked: Obj = {};
props.forEach(prop => {
picked[prop] = obj[prop];
});
return picked;
}

View File

@@ -59,9 +59,19 @@ describe('sapper', function() {
basepath: '/custom-basepath'
});
describe('export', () => {
testExport({});
testExport({ basepath: '/custom-basepath' });
});
function testExport({ basepath = '' }) {
describe(basepath ? `export --basepath ${basepath}` : 'export', () => {
before(() => {
return exec(`node ${cli} export`);
if (basepath) {
process.env.BASEPATH = basepath;
}
return exec(`node ${cli} export ${basepath ? `--basepath ${basepath}` : ''}`);
});
it('export all pages', () => {
@@ -96,7 +106,10 @@ describe('sapper', function() {
'service-worker.js',
'svelte-logo-192.png',
'svelte-logo-512.png',
];
].map(file => {
return basepath ? path.join(basepath.replace(/^\//, ''), file) : file;
});
// Client scripts that should show up in the extraction directory.
const expectedClientRegexes = [
/client\/[^/]+\/main(\.\d+)?\.js/,
@@ -126,7 +139,7 @@ describe('sapper', function() {
});
});
});
});
}
function run({ mode, basepath = '' }) {
describe(`mode=${mode}`, function () {