Skip to content

Commit

Permalink
feat: support deno deploy
Browse files Browse the repository at this point in the history
  • Loading branch information
justinawrey committed Feb 20, 2023
1 parent 0079b86 commit a9bf2bc
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 7 deletions.
58 changes: 53 additions & 5 deletions core/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ import {
import { notFound } from "./response.ts";
import { normalizeRootDir, Route } from "./route.ts";
import { setupLogger } from "./log.ts";
import { generateManifest } from "./manifest.ts";

// Re-export
export * from "./types.ts";

// Given a map of routes to their respective handlers, returns a single
// handler that correctly forwards requests to the right handler.
// If a route is hit that doesn't exist, the returned handler will 404.
function handleRoutes(routes: Route[], options: RouterOptions): http.Handler {
export function handleRoutes(
routes: Route[],
options: RouterOptions,
): http.Handler {
// Split routes into ones that are exact (don't have slugs) and ones that aren't
const exactRoutes = routes.filter((route) => !route.hasSlugs);
const slugRoutes = Route.sort(routes.filter((route) => route.hasSlugs));
Expand Down Expand Up @@ -64,7 +68,10 @@ function handleRoutes(routes: Route[], options: RouterOptions): http.Handler {
};
}

async function discoverRoutes(rootDir: string): Promise<Route[]> {
async function discoverRoutes(
rootDir: string,
options: RouterOptions,
): Promise<Route[]> {
const walkOpts: fs.WalkOptions = {
// Exclude directories when walking the filesystem. We only care
// about files which have declared handlers in them.
Expand All @@ -90,9 +97,21 @@ async function discoverRoutes(rootDir: string): Promise<Route[]> {
}

log.debug("fs.walk found", files.length, "entries:", files);
return Promise.all(
const routes = await Promise.all(
files.map((file) => Route.create(file.path, rootDir)),
);

if (options.generateManifest) {
await generateManifest(routes, rootDir, options);
} else {
log.debug(
`Not generating manifest file because ${
colors.bold("RouterOptions.generateManifest")
} is false`,
);
}

return routes;
}

/**
Expand Down Expand Up @@ -122,12 +141,28 @@ export interface RouterOptions {
* Defaults to true.
*/
convertToNumber: boolean;

/**
* Whether or not a manifest file should be generated containing static imports for all routes.
* This manifest file is needed for Deno environments that do not support
* dynamic imports, e.g. Deno Deploy.
* The location of the manifest file is as a sibling to the supplied root directory.
*
* Set this option to false if you don't need this manifest, e.g.
* you're deploying to a Deno environment that supports dynamic imports.
*
* Defaults to true.
*/
generateManifest: boolean;
_routes: Route[];
}

const defaultOptions: RouterOptions = {
export const defaultOptions: RouterOptions = {
bootMessage: true,
debug: false,
convertToNumber: true,
generateManifest: true,
_routes: [],
};

/**
Expand Down Expand Up @@ -193,7 +228,20 @@ export async function fsRouter(
log.debug("fsRouter initialized with options:", mergedOptions);

rootDir = normalizeRootDir(rootDir);
const routes = await discoverRoutes(rootDir);

let routes = mergedOptions._routes;
if (routes.length) {
log.debug(
"Routes supplied as option. Not discovering routes with dynamic imports...",
);
log.debug("Supplied routes:", routes);
} else {
log.debug(
"No routes supplied as option. Discovering routes with dynamic import...",
);
routes = await discoverRoutes(rootDir, mergedOptions);
}

if (routes.length === 0) {
errorRootDirEmpty(rootDir);
Deno.exit(0);
Expand Down
79 changes: 79 additions & 0 deletions core/manifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { RouterOptions } from "./handler.ts";
import { type Route } from "./route.ts";
import { colors, log, path } from "../deps.ts";

export function generateManifest(
routes: Route[],
rootDir: string,
options: RouterOptions,
) {
const manifestPath = path.join(rootDir, "..", "deploy-generated.ts");
log.debug(
`Generated manifest ${
colors.bold("deploy-generated.ts")
} at ${manifestPath}`,
);

return Deno.writeTextFile(
manifestPath,
generateManifestText(routes, rootDir, options),
);
}

function generateManifestText(
routes: Route[],
rootDir: string,
options: RouterOptions,
) {
const relativeRootDir = `./${path.basename(rootDir)}`;

function getRelativePath(file: string) {
return `./${path.join(relativeRootDir, path.relative(rootDir, file))}`;
}

return `// WARNING: This is an auto-generated file.
// This file SHOULD NOT be manually modified and SHOULD be checked in to source control.
// This file should be used as the entry point file for Deno Deploy.
// If you're deploying to a Deno environment that supports dynamic imports (e.g. something other than Deno Deploy)
// and you want to opt out of generating this file, you can
// 1) Set RouterOptions.generateManifest to false
// 2) Delete this file
import { serve } from "https://deno.land/std@0.177.0/http/server.ts";
import { fsRouter } from "https://deno.land/x/fsrouter/core/handler.ts";
import { Route } from "https://deno.land/x/fsrouter/core/route.ts";
const rootDir = "${relativeRootDir}";
${
routes
.map((route, index) =>
`import $${index} from "${getRelativePath(route.file)}";`
)
.join("\n")
}
const _routes = await Promise.all([
${
routes.map((route, index) =>
`Route.create(
"${getRelativePath(route.file)}",
rootDir,
$${index},
),`
).join("\n ")
}
]);
const devModeOptions = ${JSON.stringify(options, null, 2)}
const options = {
...devModeOptions,
generateManifest: false,
_routes,
};
serve(await fsRouter(import.meta.resolve(rootDir), options));`;
}
3 changes: 2 additions & 1 deletion core/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export class Route {
static async create(
filePath: string,
rootDir: string,
handler?: FsHandler,
): Promise<Route> {
// Derive the correct route from raw file paths,
// e.g. /example/blog/post.ts -> /blog/post (where example is the root directory)
Expand All @@ -72,7 +73,7 @@ export class Route {
filePath,
absPath,
absRootDir,
(await import(absPath)).default as FsHandler,
handler ? handler : (await import(absPath)).default as FsHandler,
);
}

Expand Down
7 changes: 6 additions & 1 deletion example/mod.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { serve } from "https://deno.land/std@0.177.0/http/server.ts";
import { fsRouter } from "../mod.ts";

serve(await fsRouter(import.meta.resolve("./pages"), { debug: true }));
serve(
await fsRouter(import.meta.resolve("./pages"), {
debug: true,
generateManifest: false,
}),
);

0 comments on commit a9bf2bc

Please sign in to comment.