From ab154eaa494df296f4a7b655de9f31327a67d8d6 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 17 Aug 2021 15:55:12 -0700 Subject: [PATCH] Use the Vite server rather than creating our own --- packages/kit/src/core/dev/index.js | 456 ++++++++++++++--------------- 1 file changed, 221 insertions(+), 235 deletions(-) diff --git a/packages/kit/src/core/dev/index.js b/packages/kit/src/core/dev/index.js index 68dfc7037eacc..4dd7d1ab6bb8b 100644 --- a/packages/kit/src/core/dev/index.js +++ b/packages/kit/src/core/dev/index.js @@ -14,7 +14,6 @@ import { getRawBody } from '../node/index.js'; import { copy_assets, get_svelte_packages, resolve_entry } from '../utils.js'; import { deep_merge, print_config_conflicts } from '../config/index.js'; import { svelte } from '@sveltejs/vite-plugin-svelte'; -import { get_server } from '../server/index.js'; import { __fetch_polyfill } from '../../install-fetch.js'; import { SVELTE_KIT, SVELTE_KIT_ASSETS } from '../constants.js'; @@ -102,11 +101,6 @@ class Watcher extends EventEmitter { } }; - /** @type {(req: import("http").IncomingMessage, res: import("http").ServerResponse) => void} */ - let handler = (req, res) => {}; - - this.server = await get_server(this.https, vite_config, (req, res) => handler(req, res)); - const alias = vite_config.resolve && vite_config.resolve.alias; // don't warn on overriding defaults @@ -114,6 +108,14 @@ class Watcher extends EventEmitter { const svelte_packages = get_svelte_packages(this.cwd); + const kit_plugin = await create_plugin(this.config, this.dir, this.cwd, () => { + if (!this.manifest) { + throw new Error('Manifest is not available'); + } + + return this.manifest; + }); + /** @type {[any, string[]]} */ const [merged_config, conflicts] = deep_merge(modified_vite_config, { configFile: false, @@ -157,15 +159,10 @@ class Watcher extends EventEmitter { compilerOptions: { hydratable: !!this.config.kit.hydrate } - }) + }), + kit_plugin ], publicDir: this.config.kit.files.assets, - server: { - middlewareMode: true, - hmr: { - ...(this.https ? { server: this.server, port: this.port } : {}) - } - }, ssr: { // @ts-expect-error - ssr is considered in alpha, so not yet exposed by Vite noExternal: [...((vite_config.ssr && vite_config.ssr.noExternal) || []), ...svelte_packages] @@ -176,18 +173,7 @@ class Watcher extends EventEmitter { print_config_conflicts(conflicts, 'kit.vite.'); this.vite = await vite.createServer(merged_config); - - const get_manifest = () => { - if (!this.manifest) { - throw new Error('Manifest is not available'); - } - - return this.manifest; - }; - - handler = await create_handler(this.vite, this.config, this.dir, this.cwd, get_manifest); - - this.server.listen(this.port, this.host || '0.0.0.0'); + this.vite.listen(this.port); } update() { @@ -234,7 +220,7 @@ class Watcher extends EventEmitter { } close() { - if (!this.vite || !this.server || !this.cheapwatch) { + if (!this.vite || !this.cheapwatch) { throw new Error('Cannot close server before it is initialized'); } @@ -242,7 +228,6 @@ class Watcher extends EventEmitter { this.closed = true; this.vite.close(); - this.server.close(); this.cheapwatch.close(); } } @@ -271,13 +256,12 @@ function get_params(array) { } /** - * @param {vite.ViteDevServer} vite * @param {import('types/config').ValidatedConfig} config * @param {string} dir * @param {string} cwd * @param {() => import('types/internal').SSRManifest} get_manifest */ -async function create_handler(vite, config, dir, cwd, get_manifest) { +async function create_plugin(config, dir, cwd, get_manifest) { /** * @type {amp_validator.Validator?} */ @@ -296,228 +280,230 @@ async function create_handler(vite, config, dir, cwd, get_manifest) { } }; - /** - * @param {import('http').IncomingMessage} req - * @param {import('http').ServerResponse} res - */ - return (req, res) => { - vite.middlewares(req, res, async () => { - try { - if (!req.url || !req.method) throw new Error('Incomplete request'); - if (req.url === '/favicon.ico') return not_found(res); - - const parsed = new URL(req.url, 'http://localhost/'); - if (!parsed.pathname.startsWith(config.kit.paths.base)) return not_found(res); - - /** @type {Partial} */ - const user_hooks = resolve_entry(config.kit.files.hooks) - ? await vite.ssrLoadModule(`/${config.kit.files.hooks}`) - : {}; - - /** @type {import('types/internal').Hooks} */ - const hooks = { - getSession: user_hooks.getSession || (() => ({})), - handle: user_hooks.handle || (({ request, resolve }) => resolve(request)), - handleError: - user_hooks.handleError || - (({ /** @type {Error & { frame?: string }} */ error, request }) => { - console.error(colors.bold().red(error.message)); - if (error.frame) { - console.error(colors.gray(error.frame)); - } - if (error.stack) { - console.error(colors.gray(error.stack)); - } - }), - externalFetch: user_hooks.externalFetch || fetch - }; + return { + name: 'vite-plugin-svelte-kit', + /** + * + * @param {import('vite').ViteDevServer} vite + */ + configureServer(vite) { + vite.middlewares.use(async (req, res, next) => { + try { + if (!req.url || !req.method) throw new Error('Incomplete request'); + if (req.url === '/favicon.ico') return not_found(res); + const parsed = new URL(req.url, 'http://localhost/'); + if (!parsed.pathname.startsWith(config.kit.paths.base)) return not_found(res); + + /** @type {Partial} */ + const user_hooks = resolve_entry(config.kit.files.hooks) + ? await vite.ssrLoadModule(`/${config.kit.files.hooks}`) + : {}; + + /** @type {import('types/internal').Hooks} */ + const hooks = { + getSession: user_hooks.getSession || (() => ({})), + handle: user_hooks.handle || (({ request, resolve }) => resolve(request)), + handleError: + user_hooks.handleError || + (({ /** @type {Error & { frame?: string }} */ error, request }) => { + console.error(colors.bold().red(error.message)); + if (error.frame) { + console.error(colors.gray(error.frame)); + } + if (error.stack) { + console.error(colors.gray(error.stack)); + } + }), + externalFetch: user_hooks.externalFetch || fetch + }; - if (/** @type {any} */ (hooks).getContext) { - // TODO remove this for 1.0 - throw new Error( - 'The getContext hook has been removed. See https://kit.svelte.dev/docs#hooks' - ); - } + if (/** @type {any} */ (hooks).getContext) { + // TODO remove this for 1.0 + throw new Error( + 'The getContext hook has been removed. See https://kit.svelte.dev/docs#hooks' + ); + } - if (/** @type {any} */ (hooks).serverFetch) { - // TODO remove this for 1.0 - throw new Error('The serverFetch hook has been renamed to externalFetch.'); - } + if (/** @type {any} */ (hooks).serverFetch) { + // TODO remove this for 1.0 + throw new Error('The serverFetch hook has been renamed to externalFetch.'); + } - const root = (await vite.ssrLoadModule(`/${dir}/generated/root.svelte`)).default; + const root = (await vite.ssrLoadModule(`/${dir}/generated/root.svelte`)).default; - const paths = await vite.ssrLoadModule(`/${SVELTE_KIT}/dev/runtime/paths.js`); + const paths = await vite.ssrLoadModule(`/${SVELTE_KIT}/dev/runtime/paths.js`); - paths.set_paths({ - base: config.kit.paths.base, - assets: config.kit.paths.assets ? SVELTE_KIT_ASSETS : config.kit.paths.base - }); + paths.set_paths({ + base: config.kit.paths.base, + assets: config.kit.paths.assets ? SVELTE_KIT_ASSETS : config.kit.paths.base + }); - let body; + let body; - try { - body = await getRawBody(req); - } catch (err) { - res.statusCode = err.status || 400; - return res.end(err.reason || 'Invalid request body'); - } + try { + body = await getRawBody(req); + } catch (err) { + res.statusCode = err.status || 400; + return res.end(err.reason || 'Invalid request body'); + } - const host = /** @type {string} */ (config.kit.host || - req.headers[config.kit.hostHeader || 'host']); - - const rendered = await respond( - { - headers: /** @type {import('types/helper').Headers} */ (req.headers), - method: req.method, - host, - path: parsed.pathname.replace(config.kit.paths.base, ''), - query: parsed.searchParams, - rawBody: body - }, - { - amp: config.kit.amp, - dev: true, - entry: { - file: `/${SVELTE_KIT}/dev/runtime/internal/start.js`, - css: [], - js: [] + const host = /** @type {string} */ (config.kit.host || + req.headers[config.kit.hostHeader || 'host']); + + const rendered = await respond( + { + headers: /** @type {import('types/helper').Headers} */ (req.headers), + method: req.method, + host, + path: parsed.pathname.replace(config.kit.paths.base, ''), + query: parsed.searchParams, + rawBody: body }, - floc: config.kit.floc, - get_stack: (error) => { - vite.ssrFixStacktrace(error); - return error.stack; - }, - handle_error: (error, request) => { - vite.ssrFixStacktrace(error); - hooks.handleError({ error, request }); - }, - hooks, - hydrate: config.kit.hydrate, - paths: { - base: config.kit.paths.base, - assets: config.kit.paths.assets ? SVELTE_KIT_ASSETS : config.kit.paths.base - }, - load_component: async (id) => { - const url = path.resolve(cwd, id); - - const module = /** @type {SSRComponent} */ (await vite.ssrLoadModule(url)); - const node = await vite.moduleGraph.getModuleByUrl(url); - - if (!node) throw new Error(`Could not find node for ${url}`); - - const deps = new Set(); - find_deps(node, deps); - - const styles = new Set(); - - for (const dep of deps) { - const parsed = new URL(dep.url, 'http://localhost/'); - const query = parsed.searchParams; - - // TODO what about .scss files, etc? - if ( - dep.file.endsWith('.css') || - (query.has('svelte') && query.get('type') === 'style') - ) { - try { - const mod = await vite.ssrLoadModule(dep.url); - styles.add(mod.default); - } catch { - // this can happen with dynamically imported modules, I think - // because the Vite module graph doesn't distinguish between - // static and dynamic imports? TODO investigate, submit fix + { + amp: config.kit.amp, + dev: true, + entry: { + file: `/${SVELTE_KIT}/dev/runtime/internal/start.js`, + css: [], + js: [] + }, + floc: config.kit.floc, + get_stack: (error) => { + vite.ssrFixStacktrace(error); + return error.stack; + }, + handle_error: (error, request) => { + vite.ssrFixStacktrace(error); + hooks.handleError({ error, request }); + }, + hooks, + hydrate: config.kit.hydrate, + paths: { + base: config.kit.paths.base, + assets: config.kit.paths.assets ? SVELTE_KIT_ASSETS : config.kit.paths.base + }, + load_component: async (id) => { + const url = path.resolve(cwd, id); + + const module = /** @type {SSRComponent} */ (await vite.ssrLoadModule(url)); + const node = await vite.moduleGraph.getModuleByUrl(url); + + if (!node) throw new Error(`Could not find node for ${url}`); + + const deps = new Set(); + find_deps(node, deps); + + const styles = new Set(); + + for (const dep of deps) { + const parsed = new URL(dep.url, 'http://localhost/'); + const query = parsed.searchParams; + + // TODO what about .scss files, etc? + if ( + dep.file.endsWith('.css') || + (query.has('svelte') && query.get('type') === 'style') + ) { + try { + const mod = await vite.ssrLoadModule(dep.url); + styles.add(mod.default); + } catch { + // this can happen with dynamically imported modules, I think + // because the Vite module graph doesn't distinguish between + // static and dynamic imports? TODO investigate, submit fix + } } } - } - let entry = `/${id}`; - if (!entry.endsWith('.svelte')) { - entry += '?import'; - } - return { - module, - entry, - css: [], - js: [], - styles: Array.from(styles) - }; - }, - manifest: get_manifest(), - prerender: config.kit.prerender.enabled, - read: (file) => fs.readFileSync(path.join(config.kit.files.assets, file)), - root, - router: config.kit.router, - ssr: config.kit.ssr, - target: config.kit.target, - template: ({ head, body }) => { - let rendered = fs - .readFileSync(config.kit.files.template, 'utf8') - .replace('%svelte.head%', () => head) - .replace('%svelte.body%', () => body); - - if (config.kit.amp && validator) { - const result = validator.validateString(rendered); - - if (result.status !== 'PASS') { - const lines = rendered.split('\n'); - - /** @param {string} str */ - const escape = (str) => - str.replace(/&/g, '&').replace(//g, '>'); - - rendered = ` - - - - - -

AMP validation failed

- - ${result.errors - .map( - (error) => ` -

${error.severity}

-

Line ${error.line}, column ${error.col}: ${error.message} (${ - error.code - })

-
${escape(lines[error.line - 1])}
- ` - ) - .join('\n\n')} - `; + let entry = `/${id}`; + if (!entry.endsWith('.svelte')) { + entry += '?import'; + } + return { + module, + entry, + css: [], + js: [], + styles: Array.from(styles) + }; + }, + manifest: get_manifest(), + prerender: config.kit.prerender.enabled, + read: (file) => fs.readFileSync(path.join(config.kit.files.assets, file)), + root, + router: config.kit.router, + ssr: config.kit.ssr, + target: config.kit.target, + template: ({ head, body }) => { + let rendered = fs + .readFileSync(config.kit.files.template, 'utf8') + .replace('%svelte.head%', () => head) + .replace('%svelte.body%', () => body); + + if (config.kit.amp && validator) { + const result = validator.validateString(rendered); + + if (result.status !== 'PASS') { + const lines = rendered.split('\n'); + + /** @param {string} str */ + const escape = (str) => + str.replace(/&/g, '&').replace(//g, '>'); + + rendered = ` + + + + + +

AMP validation failed

+ + ${result.errors + .map( + (error) => ` +

${error.severity}

+

Line ${error.line}, column ${error.col}: ${error.message} (${ + error.code + })

+
${escape(lines[error.line - 1])}
+ ` + ) + .join('\n\n')} + `; + } } - } - return rendered; - }, - trailing_slash: config.kit.trailingSlash + return rendered; + }, + trailing_slash: config.kit.trailingSlash + } + ); + + if (rendered) { + res.writeHead(rendered.status, rendered.headers); + if (rendered.body) res.write(rendered.body); + res.end(); + } else { + not_found(res); } - ); - - if (rendered) { - res.writeHead(rendered.status, rendered.headers); - if (rendered.body) res.write(rendered.body); - res.end(); - } else { - not_found(res); + } catch (e) { + vite.ssrFixStacktrace(e); + res.statusCode = 500; + res.end(e.stack); } - } catch (e) { - vite.ssrFixStacktrace(e); - res.statusCode = 500; - res.end(e.stack); - } - }); + }); + } }; }