Skip to content

Commit

Permalink
Add basic smoke tests for adapter-node
Browse files Browse the repository at this point in the history
* verify the server starts
* verify it can serve a 404

---

Additional changes:

- change server.js to be a function that creates the server but does not start it.
- update build rules to build from a new index.js file that builds and
starts the app
- update the package type to module to enable esm imports
- expose a way to call into server with a render function
- add a guard clause around no static asset folder existing.
- change imports in adapter-node to work with import and not require

BUG sveltejs#639
  • Loading branch information
samccone committed Mar 31, 2021
1 parent 6cd6960 commit 1ac6245
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 47 deletions.
13 changes: 13 additions & 0 deletions examples/sandbox/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"noEmit": true,
"noImplicitAny": true,
"target": "es2020",
"module": "es2020",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true
},
"include": ["src/**/*", "test/**/*"]
}
File renamed without changes.
8 changes: 6 additions & 2 deletions packages/adapter-node/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
{
"name": "@sveltejs/adapter-node",
"version": "1.0.0-next.12",
"main": "index.js",
"main": "./index.cjs",
"type": "module",
"files": [
"files"
],
"scripts": {
"dev": "rollup -cw",
"build": "rollup -c",
"test": "uvu tests",
"lint": "eslint --ignore-path .gitignore \"**/*.{ts,js,svelte}\" && npm run check-format",
"format": "prettier --write . --config ../../.prettierrc --ignore-path .gitignore",
"check-format": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore",
Expand All @@ -17,9 +19,11 @@
"@rollup/plugin-json": "^4.1.0",
"@sveltejs/kit": "workspace:*",
"compression": "^1.7.4",
"node-fetch": "^2.6.1",
"polka": "^1.0.0-next.13",
"rollup": "^2.41.1",
"sirv": "^1.0.11",
"typescript": "^4.2.3"
"typescript": "^4.2.3",
"uvu": "^0.5.1"
}
}
2 changes: 1 addition & 1 deletion packages/adapter-node/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';

export default {
input: 'src/server.js',
input: 'src/index.js',
output: {
file: 'files/server.js',
format: 'esm',
Expand Down
15 changes: 15 additions & 0 deletions packages/adapter-node/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createServer } from './server';
/*eslint import/no-unresolved: [2, { ignore: ['\.\/app\.js$'] }]*/
import * as app from './app.js';

const { PORT = 3000 } = process.env; // TODO configure via svelte.config.js

const instance = createServer({ render: app.render }).listen(PORT, (err) => {
if (err) {
console.log('error', err);
} else {
console.log(`Listening on port ${PORT}`);
}
});

export { instance };
88 changes: 46 additions & 42 deletions packages/adapter-node/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,55 @@ import { URL, fileURLToPath } from 'url';
// eslint-disable-next-line import/no-unresolved
import { get_body } from '@sveltejs/kit/http';
// App is a dynamic file built from the application layer.
/*eslint import/no-unresolved: [2, { ignore: ['\.\/app\.js$'] }]*/
import * as app from './app.js';

const __dirname = dirname(fileURLToPath(import.meta.url));

const { PORT = 3000 } = process.env; // TODO configure via svelte.config.js

const mutable = (dir) =>
sirv(dir, {
etag: true,
maxAge: 0
});

const noop_handler = (_req, _res, next) => next();

const prerendered_handler = fs.existsSync('prerendered') ? mutable('prerendered') : noop_handler;

const assets_handler = sirv(join(__dirname, '/assets'), {
maxAge: 31536000,
immutable: true
});

polka()
.use(compression({ threshold: 0 }), assets_handler, prerendered_handler, async (req, res) => {
const parsed = new URL(req.url || '', 'http://localhost');
const rendered = await app.render({
method: req.method,
headers: req.headers, // TODO: what about repeated headers, i.e. string[]
path: parsed.pathname,
body: await get_body(req),
query: parsed.searchParams
const paths = {
assets: join(__dirname, '/assets'),
prerendered: join(__dirname, '/prerendered')
};

export function createServer({ render }) {
const mutable = (dir) =>
sirv(dir, {
etag: true,
maxAge: 0
});

if (rendered) {
res.writeHead(rendered.status, rendered.headers);
res.end(rendered.body);
} else {
res.statusCode = 404;
res.end('Not found');
const prerendered_handler = fs.existsSync(paths.prerendered)
? mutable(paths.prerendered)
: noop_handler;

const assets_handler = fs.existsSync(paths.assets)
? sirv(paths.assets, {
maxAge: 31536000,
immutable: true
})
: noop_handler;

const server = polka().use(
compression({ threshold: 0 }),
assets_handler,
prerendered_handler,
async (req, res) => {
const parsed = new URL(req.url || '', 'http://localhost');
const rendered = await render({
method: req.method,
headers: req.headers, // TODO: what about repeated headers, i.e. string[]
path: parsed.pathname,
body: await get_body(req),
query: parsed.searchParams
});

if (rendered) {
res.writeHead(rendered.status, rendered.headers);
res.end(rendered.body);
} else {
res.statusCode = 404;
res.end('Not found');
}
}
})
.listen(PORT, (err) => {
if (err) {
console.log('error', err);
} else {
console.log(`Listening on port ${PORT}`);
}
});
);

return server;
}
33 changes: 33 additions & 0 deletions packages/adapter-node/tests/smoke.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { test } from 'uvu';
import { createServer } from '../src/server.js';
import * as assert from 'uvu/assert';
import fetch from 'node-fetch';

const { PORT = 3000 } = process.env;

function startServer() {
const server = createServer({ render: () => {} });
return new Promise((fulfil, reject) => {
server.listen(PORT, (err) => {
if (err) {
reject(err);
}
fulfil(server);
});
});
}

test('starts a server', async () => {
const server = await startServer();
assert.ok('server started');
server.server.close();
});

test('serves a 404', async () => {
const server = await startServer();
const res = await fetch(`http://localhost:${PORT}/nothing`);
assert.equal(res.status, 404);
server.server.close();
});

test.run();
2 changes: 1 addition & 1 deletion packages/adapter-node/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"moduleResolution": "node",
"allowSyntheticDefaultImports": true
},
"include": ["./index.js", "src"]
"include": ["index.cjs", "src"]
}
5 changes: 4 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1ac6245

Please sign in to comment.