-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(@remix-run/deno): add server runtime package for deno
- Loading branch information
Showing
19 changed files
with
431 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"// Resolve NPM imports for `packages/remix-deno`.": "", | ||
|
||
"// This import map is used solely for the denoland.vscode-deno extension.": "", | ||
"// Remix does not support import maps.": "", | ||
"// Dependency management is done through `npm` and `node_modules/` instead.": "", | ||
"// Deno-only dependencies may be imported via URL imports (without using import maps).": "", | ||
|
||
"imports": { | ||
"mime": "https://esm.sh/mime@3.0.0", | ||
"@remix-run/server-runtime": "https://esm.sh/@remix-run/server-runtime@1.4.3" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
{ | ||
"typescript.tsdk": "node_modules/typescript/lib" | ||
"typescript.tsdk": "node_modules/typescript/lib", | ||
"deno.enablePaths": [ | ||
"./packages/remix-deno/", | ||
], | ||
"deno.importMap": "./.vscode/deno_resolve_npm_imports.json" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/* | ||
Intentionally left empty as a dummy input for Rollup. | ||
This package should not be bundled by Rollup as its source | ||
code is a Deno module, not an NPM package. | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# This file is needed for the Rollup copy plugin to ignore local node_modules/ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# @remix-run/deno |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import type { SignFunction, UnsignFunction } from "@remix-run/server-runtime"; | ||
|
||
const encoder = new TextEncoder(); | ||
|
||
export const sign: SignFunction = async (value, secret) => { | ||
const data = encoder.encode(value); | ||
const key = await createKey(secret, ["sign"]); | ||
const signature = await crypto.subtle.sign("HMAC", key, data); | ||
const hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace( | ||
/=+$/, | ||
"", | ||
); | ||
|
||
return value + "." + hash; | ||
}; | ||
|
||
export const unsign: UnsignFunction = async (cookie, secret) => { | ||
const value = cookie.slice(0, cookie.lastIndexOf(".")); | ||
const hash = cookie.slice(cookie.lastIndexOf(".") + 1); | ||
|
||
const data = encoder.encode(value); | ||
const key = await createKey(secret, ["verify"]); | ||
const signature = byteStringToUint8Array(atob(hash)); | ||
const valid = await crypto.subtle.verify("HMAC", key, signature, data); | ||
|
||
return valid ? value : false; | ||
}; | ||
|
||
async function createKey( | ||
secret: string, | ||
usages: CryptoKey["usages"], | ||
): Promise<CryptoKey> { | ||
const key = await crypto.subtle.importKey( | ||
"raw", | ||
encoder.encode(secret), | ||
{ name: "HMAC", hash: "SHA-256" }, | ||
false, | ||
usages, | ||
); | ||
|
||
return key; | ||
} | ||
|
||
function byteStringToUint8Array(byteString: string): Uint8Array { | ||
const array = new Uint8Array(byteString.length); | ||
|
||
for (let i = 0; i < byteString.length; i++) { | ||
array[i] = byteString.charCodeAt(i); | ||
} | ||
|
||
return array; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* | ||
Remix provides `process.env.NODE_ENV` at compile time. | ||
Declare types for `process` here so that they are available in Deno. | ||
*/ | ||
|
||
interface ProcessEnv { | ||
NODE_ENV: "development" | "production" | "test"; | ||
} | ||
interface Process { | ||
env: ProcessEnv; | ||
} | ||
var process: Process; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { | ||
createCookieFactory, | ||
createCookieSessionStorageFactory, | ||
createMemorySessionStorageFactory, | ||
createSessionStorageFactory, | ||
} from "@remix-run/server-runtime"; | ||
|
||
import { sign, unsign } from "./crypto.ts"; | ||
|
||
export const createCookie = createCookieFactory({ sign, unsign }); | ||
export const createCookieSessionStorage = createCookieSessionStorageFactory( | ||
createCookie, | ||
); | ||
export const createSessionStorage = createSessionStorageFactory(createCookie); | ||
export const createMemorySessionStorage = createMemorySessionStorageFactory( | ||
createSessionStorage, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import "./globals.ts"; | ||
export { createFileSessionStorage } from "./sessions/fileStorage.ts"; | ||
export { | ||
createRequestHandler, | ||
createRequestHandlerWithStaticFiles, | ||
serveStaticFiles, | ||
} from "./server.ts"; | ||
|
||
export { | ||
createCookie, | ||
createCookieSessionStorage, | ||
createMemorySessionStorage, | ||
createSessionStorage, | ||
} from "./implementations.ts"; | ||
|
||
export { | ||
createSession, | ||
isCookie, | ||
isSession, | ||
json, | ||
redirect, | ||
} from "@remix-run/server-runtime"; | ||
|
||
export type { | ||
ActionFunction, | ||
AppData, | ||
AppLoadContext, | ||
Cookie, | ||
CookieOptions, | ||
CookieParseOptions, | ||
CookieSerializeOptions, | ||
CookieSignatureOptions, | ||
CreateRequestHandlerFunction, | ||
DataFunctionArgs, | ||
EntryContext, | ||
ErrorBoundaryComponent, | ||
HandleDataRequestFunction, | ||
HandleDocumentRequestFunction, | ||
HeadersFunction, | ||
HtmlLinkDescriptor, | ||
HtmlMetaDescriptor, | ||
LinkDescriptor, | ||
LinksFunction, | ||
LoaderFunction, | ||
MetaDescriptor, | ||
MetaFunction, | ||
PageLinkDescriptor, | ||
RequestHandler, | ||
RouteComponent, | ||
RouteHandle, | ||
ServerBuild, | ||
ServerEntryModule, | ||
Session, | ||
SessionData, | ||
SessionIdStorageStrategy, | ||
SessionStorage, | ||
} from "@remix-run/server-runtime"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"name": "@remix-run/deno", | ||
"version": "1.4.3", | ||
"description": "Deno platform abstractions for Remix", | ||
"homepage": "https://remix.run", | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/remix-run/remix", | ||
"directory": "packages/remix-deno" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/remix-run/remix/issues" | ||
}, | ||
"sideEffects": false, | ||
"dependencies": { | ||
"@remix-run/server-runtime": "*", | ||
"mime": "^3.0.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import * as path from "https://deno.land/std@0.128.0/path/mod.ts"; | ||
import mime from "mime"; | ||
import { createRequestHandler as createRemixRequestHandler } from "@remix-run/server-runtime"; | ||
import type { ServerBuild } from "@remix-run/server-runtime"; | ||
|
||
function defaultCacheControl(url: URL, assetsPublicPath = "/build/") { | ||
if (url.pathname.startsWith(assetsPublicPath)) { | ||
return "public, max-age=31536000, immutable"; | ||
} else { | ||
return "public, max-age=600"; | ||
} | ||
} | ||
|
||
export function createRequestHandler<Context = unknown>({ | ||
build, | ||
mode, | ||
getLoadContext, | ||
}: { | ||
build: ServerBuild; | ||
mode?: string; | ||
getLoadContext?: (request: Request) => Promise<Context> | Context; | ||
}) { | ||
const remixHandler = createRemixRequestHandler(build, mode); | ||
return async (request: Request) => { | ||
try { | ||
const loadContext = getLoadContext | ||
? await getLoadContext(request) | ||
: undefined; | ||
|
||
return await remixHandler(request, loadContext); | ||
} catch (e) { | ||
console.error(e); | ||
|
||
return new Response("Internal Error", { status: 500 }); | ||
} | ||
}; | ||
} | ||
|
||
export async function serveStaticFiles( | ||
request: Request, | ||
{ | ||
cacheControl, | ||
publicDir = "./public", | ||
assetsPublicPath = "/build/", | ||
}: { | ||
cacheControl?: string | ((url: URL) => string); | ||
publicDir?: string; | ||
assetsPublicPath?: string; | ||
}, | ||
) { | ||
const url = new URL(request.url); | ||
|
||
const headers = new Headers(); | ||
const contentType = mime.getType(url.pathname); | ||
if (contentType) { | ||
headers.set("Content-Type", contentType); | ||
} | ||
|
||
if (typeof cacheControl === "function") { | ||
headers.set("Cache-Control", cacheControl(url)); | ||
} else if (cacheControl) { | ||
headers.set("Cache-Control", cacheControl); | ||
} else { | ||
headers.set("Cache-Control", defaultCacheControl(url, assetsPublicPath)); | ||
} | ||
|
||
const file = await Deno.readFile(path.join(publicDir, url.pathname)); | ||
|
||
return new Response(file, { headers }); | ||
} | ||
|
||
export function createRequestHandlerWithStaticFiles<Context = unknown>({ | ||
build, | ||
mode, | ||
getLoadContext, | ||
staticFiles = { | ||
publicDir: "./public", | ||
assetsPublicPath: "/build/", | ||
}, | ||
}: { | ||
build: ServerBuild; | ||
mode?: string; | ||
getLoadContext?: (request: Request) => Promise<Context> | Context; | ||
staticFiles?: { | ||
cacheControl?: string | ((url: URL) => string); | ||
publicDir?: string; | ||
assetsPublicPath?: string; | ||
}; | ||
}) { | ||
const remixHandler = createRequestHandler({ build, mode, getLoadContext }); | ||
|
||
return async (request: Request) => { | ||
try { | ||
return await serveStaticFiles(request, staticFiles); | ||
} catch (error) { | ||
if (error.code !== "EISDIR" && error.code !== "ENOENT") { | ||
throw error; | ||
} | ||
} | ||
|
||
return remixHandler(request); | ||
}; | ||
} |
Oops, something went wrong.