Skip to content

Commit

Permalink
Fix prerendered page handling on Deno (withastro#6284)
Browse files Browse the repository at this point in the history
* fix(deno): handle prerendered pages

* test(deno): add prerender test

* fix: defensively access vite.build.rollupOptions.external

* fix(deno): support other formats of rollupOptions.external

* fix(deno): crawl prerendered files for match

* fix(deno): ignore deno error in server file

* fix(deno): cross-platform serve file
  • Loading branch information
natemoo-re committed Feb 17, 2023
1 parent ed92730 commit 61113dd
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/shaggy-moons-judge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/deno': patch
---

Fix prerendered page behavior
2 changes: 1 addition & 1 deletion examples/deno/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "deno run --allow-net --allow-read ./dist/server/entry.mjs",
"preview": "deno run --allow-net --allow-read --allow-env ./dist/server/entry.mjs",
"astro": "astro"
},
"dependencies": {
Expand Down
2 changes: 2 additions & 0 deletions examples/deno/src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
---
import Layout from '../components/Layout.astro';
export const prerender = true;
---

<Layout title="Welcome to Astro (on Deno).">
Expand Down
1 change: 1 addition & 0 deletions packages/integrations/deno/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"exports": {
".": "./dist/index.js",
"./server.js": "./dist/server.js",
"./__deno_imports.js": "./dist/__deno_imports.js",
"./package.json": "./package.json"
},
"scripts": {
Expand Down
10 changes: 10 additions & 0 deletions packages/integrations/deno/src/__deno_imports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// This file is a shim for any Deno-specific imports!
// It will be replaced in the final Deno build.
//
// This allows us to prerender pages in Node.
export class Server {
listenAndServe() {}
}

export function serveFile() {}
export function fromFileUrl() {}
39 changes: 36 additions & 3 deletions packages/integrations/deno/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ const SHIM = `globalThis.process = {
env: Deno.env.toObject(),
};`;

const DENO_VERSION = `0.177.0`

// We shim deno-specific imports so we can run the code in Node
// to prerender pages. In the final Deno build, this import is
// replaced with the Deno-specific contents listed below.
const DENO_IMPORTS_SHIM = `@astrojs/deno/__deno_imports.js`;
const DENO_IMPORTS = `export { Server } from "https://deno.land/std@${DENO_VERSION}/http/server.ts"
export { serveFile } from 'https://deno.land/std@${DENO_VERSION}/http/file_server.ts';
export { fromFileUrl } from "https://deno.land/std@${DENO_VERSION}/path/mod.ts";`

export function getAdapter(args?: Options): AstroAdapter {
return {
name: '@astrojs/deno',
Expand All @@ -29,6 +39,18 @@ export function getAdapter(args?: Options): AstroAdapter {
};
}

const denoImportsShimPlugin = {
name: '@astrojs/deno:shim',
setup(build: esbuild.PluginBuild) {
build.onLoad({ filter: /__deno_imports\.js$/ }, async (args) => {
return {
contents: DENO_IMPORTS,
loader: 'js',
}
})
},
}

export default function createIntegration(args?: Options): AstroIntegration {
let _buildConfig: BuildConfig;
let _vite: any;
Expand All @@ -49,8 +71,11 @@ export default function createIntegration(args?: Options): AstroIntegration {
'astro:build:setup': ({ vite, target }) => {
if (target === 'server') {
_vite = vite;
vite.resolve = vite.resolve || {};
vite.resolve.alias = vite.resolve.alias || {};
vite.resolve = vite.resolve ?? {};
vite.resolve.alias = vite.resolve.alias ?? {};
vite.build = vite.build ?? {};
vite.build.rollupOptions = vite.build.rollupOptions ?? {};
vite.build.rollupOptions.external = vite.build.rollupOptions.external ?? [];

const aliases = [{ find: 'react-dom/server', replacement: 'react-dom/server.browser' }];

Expand All @@ -61,10 +86,15 @@ export default function createIntegration(args?: Options): AstroIntegration {
(vite.resolve.alias as Record<string, string>)[alias.find] = alias.replacement;
}
}

vite.ssr = {
noExternal: true,
};

if (Array.isArray(vite.build.rollupOptions.external)) {
vite.build.rollupOptions.external.push(DENO_IMPORTS_SHIM);
} else if (typeof vite.build.rollupOptions.external !== 'function') {
vite.build.rollupOptions.external = [vite.build.rollupOptions.external, DENO_IMPORTS_SHIM]
}
}
},
'astro:build:done': async () => {
Expand All @@ -80,6 +110,9 @@ export default function createIntegration(args?: Options): AstroIntegration {
format: 'esm',
bundle: true,
external: ['@astrojs/markdown-remark'],
plugins: [
denoImportsShimPlugin
],
banner: {
js: SHIM,
},
Expand Down
34 changes: 30 additions & 4 deletions packages/integrations/deno/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import type { SSRManifest } from 'astro';
import { App } from 'astro/app';

// @ts-ignore
import { Server } from 'https://deno.land/[email protected]/http/server.ts';
// @ts-ignore
import { fetch } from 'https://deno.land/x/file_fetch/mod.ts';
import { Server, serveFile, fromFileUrl } from '@astrojs/deno/__deno_imports.js';

interface Options {
port?: number;
Expand All @@ -16,6 +14,17 @@ interface Options {
let _server: Server | undefined = undefined;
let _startPromise: Promise<void> | undefined = undefined;

async function* getPrerenderedFiles(clientRoot: URL): AsyncGenerator<URL> {
// @ts-ignore
for await (const ent of Deno.readDir(clientRoot)) {
if (ent.isDirectory) {
yield* getPrerenderedFiles(new URL(`./${ent.name}/`, clientRoot))
} else if (ent.name.endsWith('.html')) {
yield new URL(`./${ent.name}`, clientRoot)
}
}
}

export function start(manifest: SSRManifest, options: Options) {
if (options.start === false) {
return;
Expand All @@ -40,7 +49,24 @@ export function start(manifest: SSRManifest, options: Options) {
// try to fetch a static file instead
const url = new URL(request.url);
const localPath = new URL('./' + app.removeBase(url.pathname), clientRoot);
const fileResp = await fetch(localPath.toString());

let fileResp = await serveFile(request, fromFileUrl(localPath));

// Attempt to serve `index.html` if 404
if (fileResp.status == 404) {
let fallback;
for await (const file of getPrerenderedFiles(clientRoot)) {
const pathname = file.pathname.replace(/\/(index)?\.html$/, '');
if (localPath.pathname.endsWith(pathname)) {
fallback = file;
break;
}
}
if (fallback) {
fileResp = await serveFile(request, fromFileUrl(fallback));
}
}


// If the static file can't be found
if (fileResp.status == 404) {
Expand Down
23 changes: 22 additions & 1 deletion packages/integrations/deno/test/basics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Deno.test({
resp = await fetch(new URL(href!, baseUrl));
assertEquals(resp.status, 200);
const ct = resp.headers.get('content-type');
assertEquals(ct, 'text/css');
assertEquals(ct, 'text/css; charset=UTF-8');
await resp.body!.cancel();
});
},
Expand Down Expand Up @@ -143,3 +143,24 @@ Deno.test({
sanitizeResources: false,
sanitizeOps: false,
});


Deno.test({
name: 'perendering',
permissions: defaultTestPermissions,
async fn() {
await startApp(async (baseUrl: URL) => {
const resp = await fetch(new URL('/prerender', baseUrl));
assertEquals(resp.status, 200);

const html = await resp.text();
assert(html);

const doc = new DOMParser().parseFromString(html, `text/html`);
const h1 = doc!.querySelector('h1');
assertEquals(h1!.innerText, 'test');
});
},
sanitizeResources: false,
sanitizeOps: false,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
export const prerender = true;
---

<html>
<body>
<h1>test</h1>
</body>
</html>

0 comments on commit 61113dd

Please sign in to comment.