diff options
author | 2021-04-05 14:18:09 -0400 | |
---|---|---|
committer | 2021-04-05 14:18:09 -0400 | |
commit | c9bc6ffef7be0e068acb1b36475c4ab3d12fd5d5 (patch) | |
tree | d444fa26d0ce47bb3cebb5e6264d67778a2e106d /src | |
parent | d9733e8d42662d8708b5fc3bcb5c6c4db75df043 (diff) | |
download | astro-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.ts | 7 | ||||
-rw-r--r-- | src/runtime.ts | 98 | ||||
-rw-r--r-- | src/search.ts | 75 |
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 |