diff --git a/.changeset/thick-ravens-chew.md b/.changeset/thick-ravens-chew.md new file mode 100644 index 0000000000..3e7710d428 --- /dev/null +++ b/.changeset/thick-ravens-chew.md @@ -0,0 +1,5 @@ +--- +"astro": patch +--- + +Replace `send` dependency with `sirv` diff --git a/comp.txt b/comp.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/astro/package.json b/packages/astro/package.json index 2b4a216bc8..760719d8fb 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -95,7 +95,7 @@ "resolve": "^1.20.0", "rollup": "^2.64.0", "semver": "^7.3.5", - "send": "^0.17.1", + "sirv": "^2.0.2", "serialize-javascript": "^6.0.0", "shiki": "^0.10.0", "shorthash": "^0.0.2", @@ -125,7 +125,6 @@ "@types/parse5": "^6.0.3", "@types/resolve": "^1.20.1", "@types/rimraf": "^3.0.2", - "@types/send": "^0.17.1", "@types/yargs-parser": "^20.2.1", "astro-scripts": "workspace:*", "chai": "^4.3.4", diff --git a/packages/astro/src/core/preview/index.ts b/packages/astro/src/core/preview/index.ts index d53fe837a6..5178b5d680 100644 --- a/packages/astro/src/core/preview/index.ts +++ b/packages/astro/src/core/preview/index.ts @@ -1,17 +1,14 @@ import type { AstroConfig } from '../../@types/astro'; import type { LogOptions } from '../logger'; -import type { Stats } from 'fs'; import type { AddressInfo } from 'net'; import http from 'http'; +import sirv from 'sirv'; import { performance } from 'perf_hooks'; -import send from 'send'; import { fileURLToPath } from 'url'; -import fs from 'fs'; import * as msg from '../messages.js'; import { error, info } from '../logger.js'; -import { subpathNotUsedTemplate, notFoundTemplate, default as template } from '../../template/4xx.js'; -import { appendForwardSlash, trimSlashes } from '../path.js'; +import { subpathNotUsedTemplate, notFoundTemplate } from '../../template/4xx.js'; import { getLocalAddress } from '../dev/util.js'; interface PreviewOptions { @@ -28,22 +25,8 @@ export interface PreviewServer { /** The primary dev action */ export default async function preview(config: AstroConfig, { logging }: PreviewOptions): Promise { const startServerTime = performance.now(); - const pageUrlFormat = config.buildOptions.pageUrlFormat; - const trailingSlash = config.devOptions.trailingSlash; - const forceTrailingSlash = trailingSlash === 'always'; - const blockTrailingSlash = trailingSlash === 'never'; - - /** Default file served from a directory. */ - const defaultFile = 'index.html'; - const defaultOrigin = 'http://localhost'; - - const sendOptions = { - extensions: pageUrlFormat === 'file' ? ['html'] : false, - index: false, - root: fileURLToPath(config.dist), - }; - + const trailingSlash = config.devOptions.trailingSlash /** Base request URL. */ let baseURL = new URL(config.buildOptions.site || '/', defaultOrigin); @@ -64,55 +47,28 @@ export default async function preview(config: AstroConfig, { logging }: PreviewO const isRoot = pathname === '/'; const hasTrailingSlash = isRoot || pathname.endsWith('/'); - let tryTrailingSlash = true; - let tryHtmlExtension = true; - - let url: URL; - - const onErr = (message: string) => { + function err(message: string) { res.statusCode = 404; res.end(notFoundTemplate(pathname, message)); }; - const onStat = (err: NodeJS.ErrnoException | null, stat: Stats) => { - switch (true) { - // retry nonexistent paths without an html extension - case err && tryHtmlExtension && hasTrailingSlash && !blockTrailingSlash: - case err && tryHtmlExtension && !hasTrailingSlash && !forceTrailingSlash && !pathname.endsWith('.html'): - tryHtmlExtension = false; - return fs.stat((url = new URL(url.pathname + '.html', url)), onStat); - - // 404 on nonexistent paths (that are yet handled) - case err !== null: - return onErr('Path not found'); - - // 404 on directories when a trailing slash is present but blocked - case stat.isDirectory() && hasTrailingSlash && blockTrailingSlash && !isRoot: - return onErr('Prohibited trailing slash'); - - // 404 on directories when a trailing slash is missing but forced - case stat.isDirectory() && !hasTrailingSlash && forceTrailingSlash && !isRoot: - return onErr('Required trailing slash'); - - // retry on directories when a default file is missing but allowed (that are yet handled) - case stat.isDirectory() && tryTrailingSlash: - tryTrailingSlash = false; - return fs.stat((url = new URL(url.pathname + (url.pathname.endsWith('/') ? defaultFile : '/' + defaultFile), url)), onStat); - - // 404 on existent directories (that are yet handled) - case stat.isDirectory(): - return onErr('Path not found'); - - // handle existent paths - default: - send(req, fileURLToPath(url), { - extensions: false, - index: false, - }).pipe(res); - } - }; - - fs.stat((url = new URL(trimSlashes(pathname), config.dist)), onStat); + switch(true) { + case hasTrailingSlash && trailingSlash == 'never' && !isRoot: + err('Prohibited trailing slash'); + break; + case !hasTrailingSlash && trailingSlash == 'always' && !isRoot: + err('Required trailing slash'); + break; + default: { + // HACK: rewrite req.url so that sirv finds the file + req.url = '/' + req.url?.replace(baseURL.pathname,'') + sirv(fileURLToPath(config.dist), { + maxAge: 0, + onNoMatch: () => { + err('Path not found') + } + })(req,res) + }} }); let { hostname, port } = config.devOptions;