summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Matthew Phillips <matthew@matthewphillips.info> 2021-04-05 14:18:09 -0400
committerGravatar GitHub <noreply@github.com> 2021-04-05 14:18:09 -0400
commitc9bc6ffef7be0e068acb1b36475c4ab3d12fd5d5 (patch)
treed444fa26d0ce47bb3cebb5e6264d67778a2e106d /src
parentd9733e8d42662d8708b5fc3bcb5c6c4db75df043 (diff)
downloadastro-c9bc6ffef7be0e068acb1b36475c4ab3d12fd5d5.tar.gz
astro-c9bc6ffef7be0e068acb1b36475c4ab3d12fd5d5.tar.zst
astro-c9bc6ffef7be0e068acb1b36475c4ab3d12fd5d5.zip
Improve searching for pages (#60)
This improves the algorithm for searching for pages. It now works like: 1. If pathname ends with / 1. Look for PATHNAME/index.astro 1. Look for PATHNAME/index.md 1. else 1. Look for PATHNAME.astro 1. Look for PATHNAME.md 1. Look for PATHNAME/index.astro 1. 301 1. Look for PATHNAME/index.md 1. 301 1. 404
Diffstat (limited to 'src')
-rw-r--r--src/build.ts7
-rw-r--r--src/runtime.ts98
-rw-r--r--src/search.ts75
3 files changed, 123 insertions, 57 deletions
diff --git a/src/build.ts b/src/build.ts
index 831d0be0e..aef96b918 100644
--- a/src/build.ts
+++ b/src/build.ts
@@ -44,9 +44,10 @@ async function writeFilep(outPath: URL, bytes: string | Buffer, encoding: 'utf-8
/** Utility for writing a build result to disk */
async function writeResult(result: LoadResult, outPath: URL, encoding: null | 'utf-8') {
- if (result.statusCode !== 200) {
- error(logging, 'build', result.error || result.statusCode);
- //return 1;
+ if (result.statusCode === 500 || result.statusCode === 404) {
+ error(logging, 'build', result.error || result.statusCode);
+ } else if(result.statusCode !== 200) {
+ error(logging, 'build', `Unexpected load result (${result.statusCode}) for ${outPath.pathname}`);
} else {
const bytes = result.contents;
await writeFilep(outPath, bytes, encoding);
diff --git a/src/runtime.ts b/src/runtime.ts
index c12fb3e14..62bbdb09c 100644
--- a/src/runtime.ts
+++ b/src/runtime.ts
@@ -3,6 +3,7 @@ import type { AstroConfig, RuntimeMode } from './@types/astro';
import type { LogOptions } from './logger';
import type { CompileError } from './parser/utils/error.js';
import { debug, info } from './logger.js';
+import { searchForPage } from './search.js';
import { existsSync } from 'fs';
import { loadConfiguration, logger as snowpackLogger, startServer as startSnowpackServer } from 'snowpack';
@@ -25,9 +26,10 @@ type LoadResultSuccess = {
contentType?: string | false;
};
type LoadResultNotFound = { statusCode: 404; error: Error };
+type LoadResultRedirect = { statusCode: 301 | 302; location: string; };
type LoadResultError = { statusCode: 500 } & ({ type: 'parse-error'; error: CompileError } | { type: 'unknown'; error: Error });
-export type LoadResult = LoadResultSuccess | LoadResultNotFound | LoadResultError;
+export type LoadResult = LoadResultSuccess | LoadResultNotFound | LoadResultRedirect | LoadResultError;
// Disable snowpack from writing to stdout/err.
snowpackLogger.level = 'silent';
@@ -38,15 +40,12 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
const { astroRoot } = config.astroConfig;
const fullurl = new URL(rawPathname || '/', 'https://example.org/');
+
const reqPath = decodeURI(fullurl.pathname);
- const selectedPage = reqPath.substr(1) || 'index';
info(logging, 'access', reqPath);
- const selectedPageLoc = new URL(`./pages/${selectedPage}.astro`, astroRoot);
- const selectedPageMdLoc = new URL(`./pages/${selectedPage}.md`, astroRoot);
-
- // Non-Astro pages (file resources)
- if (!existsSync(selectedPageLoc) && !existsSync(selectedPageMdLoc)) {
+ const searchResult = searchForPage(fullurl, astroRoot);
+ if(searchResult.statusCode === 404) {
try {
const result = await frontendSnowpack.loadUrl(reqPath);
@@ -66,61 +65,52 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
}
}
- for (const url of [`/_astro/pages/${selectedPage}.astro.js`, `/_astro/pages/${selectedPage}.md.js`]) {
- try {
- const mod = await backendSnowpackRuntime.importModule(url);
- debug(logging, 'resolve', `${reqPath} -> ${url}`);
- let html = (await mod.exports.__renderPage({
- request: {
- host: fullurl.hostname,
- path: fullurl.pathname,
- href: fullurl.toString(),
- },
- children: [],
- props: {},
- })) as string;
-
- // inject styles
- // TODO: handle this in compiler
- const styleTags = Array.isArray(mod.css) && mod.css.length ? mod.css.reduce((markup, href) => `${markup}\n<link rel="stylesheet" type="text/css" href="${href}" />`, '') : ``;
- if (html.indexOf('</head>') !== -1) {
- html = html.replace('</head>', `${styleTags}</head>`);
- } else {
- html = styleTags + html;
- }
+ if(searchResult.statusCode === 301) {
+ return { statusCode: 301, location: searchResult.pathname };
+ }
- return {
- statusCode: 200,
- contents: html,
- };
- } catch (err) {
- // if this is a 404, try the next URL (will be caught at the end)
- const notFoundError = err.toString().startsWith('Error: Not Found');
- if (notFoundError) {
- continue;
- }
+ const snowpackURL = searchResult.location.snowpackURL;
+
+ try {
+ const mod = await backendSnowpackRuntime.importModule(snowpackURL);
+ debug(logging, 'resolve', `${reqPath} -> ${snowpackURL}`);
+ let html = (await mod.exports.__renderPage({
+ request: {
+ host: fullurl.hostname,
+ path: fullurl.pathname,
+ href: fullurl.toString(),
+ },
+ children: [],
+ props: {},
+ })) as string;
+
+ // inject styles
+ // TODO: handle this in compiler
+ const styleTags = Array.isArray(mod.css) && mod.css.length ? mod.css.reduce((markup, href) => `${markup}\n<link rel="stylesheet" type="text/css" href="${href}" />`, '') : ``;
+ if (html.indexOf('</head>') !== -1) {
+ html = html.replace('</head>', `${styleTags}</head>`);
+ } else {
+ html = styleTags + html;
+ }
- if (err.code === 'parse-error') {
- return {
- statusCode: 500,
- type: 'parse-error',
- error: err,
- };
- }
+ return {
+ statusCode: 200,
+ contents: html,
+ };
+ } catch (err) {
+ if (err.code === 'parse-error') {
return {
statusCode: 500,
- type: 'unknown',
+ type: 'parse-error',
error: err,
};
}
+ return {
+ statusCode: 500,
+ type: 'unknown',
+ error: err,
+ };
}
-
- // couldnā€˜t find match; 404
- return {
- statusCode: 404,
- type: 'unknown',
- error: new Error(`Could not locate ${selectedPage}`),
- };
}
export interface AstroRuntime {
diff --git a/src/search.ts b/src/search.ts
new file mode 100644
index 000000000..d9e2fa00c
--- /dev/null
+++ b/src/search.ts
@@ -0,0 +1,75 @@
+import { existsSync } from 'fs';
+
+interface PageLocation {
+ fileURL: URL;
+ snowpackURL: string;
+}
+
+function findAnyPage(candidates: Array<string>, astroRoot: URL): PageLocation | false {
+ for(let candidate of candidates) {
+ const url = new URL(`./pages/${candidate}`, astroRoot);
+ if(existsSync(url)) {
+ return {
+ fileURL: url,
+ snowpackURL: `/_astro/pages/${candidate}.js`
+ };
+ }
+ }
+ return false;
+}
+
+type SearchResult = {
+ statusCode: 200;
+ location: PageLocation;
+ pathname: string;
+} | {
+ statusCode: 301;
+ location: null;
+ pathname: string;
+} | {
+ statusCode: 404;
+};
+
+export function searchForPage(url: URL, astroRoot: URL): SearchResult {
+ const reqPath = decodeURI(url.pathname);
+ const base = reqPath.substr(1);
+
+ // Try to find index.astro/md paths
+ if(reqPath.endsWith('/')) {
+ const candidates = [`${base}index.astro`, `${base}index.md`];
+ const location = findAnyPage(candidates, astroRoot);
+ if(location) {
+ return {
+ statusCode: 200,
+ location,
+ pathname: reqPath
+ };
+ }
+ } else {
+ // Try to find the page by its name.
+ const candidates = [`${base}.astro`, `${base}.md`];
+ let location = findAnyPage(candidates, astroRoot);
+ if(location) {
+ return {
+ statusCode: 200,
+ location,
+ pathname: reqPath
+ };
+ }
+ }
+
+ // Try to find name/index.astro/md
+ const candidates = [`${base}/index.astro`, `${base}/index.md`];
+ const location = findAnyPage(candidates, astroRoot);
+ if(location) {
+ return {
+ statusCode: 301,
+ location: null,
+ pathname: reqPath + '/'
+ };
+ }
+
+ return {
+ statusCode: 404
+ };
+} \ No newline at end of file