Skip to content

Commit

Permalink
Merge pull request #46 from exhibitionist-digital/reload
Browse files Browse the repository at this point in the history
reload websocket
  • Loading branch information
mashaal authored Mar 5, 2022
2 parents 1a208b6 + 9ca549e commit a5417d1
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 1 deletion.
1 change: 1 addition & 0 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default } from "./src/server.ts";
export { default as ultraHandler } from "./src/oak/handler.ts";
86 changes: 86 additions & 0 deletions src/oak/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { LRU, readableStreamFromReader } from "../deps.ts";
import assets from "../assets.ts";
import transform from "../transform.ts";
import render from "../render.ts";
import { jsxify, tsxify } from "../resolver.ts";
import { isDev } from "../env.ts";

import { OakOptions } from "../types.ts";

const memory = new LRU(500);

const server = (
{
importmap,
dir = "src",
root = "http://localhost:8000",
lang = "en",
env,
context,
}: OakOptions,
) => {
const serverStart = Math.ceil(+new Date() / 100);

const handler = async (request: Request) => {
const requestStart = Math.ceil(+new Date() / 100);
const cacheBuster = isDev ? requestStart : serverStart;
const { raw, transpile } = await assets(dir);
const url = new URL(request.url);

// static assets
if (raw.has(`${dir}${url.pathname}`)) {
const file = await Deno.open(`./${dir}${url.pathname}`);
const body = readableStreamFromReader(file);
context.response.body = body;
return;
}

const transpilation = async (file: string) => {
let js = memory.get(url.pathname);

if (!js) {
const source = await Deno.readTextFile(`./${file}`);
const t0 = performance.now();
js = await transform({
source,
importmap,
root,
cacheBuster,
env,
});
const t1 = performance.now();
console.log(`Transpile ${file.replace(dir, "")} in ${t1 - t0}ms`);
if (!isDev) memory.set(url.pathname, js);
}
context.response.type = "text/javascript";
// @ts-ignore add js type
context.response.body = js;
return;
};

// jsx
const jsx = `${dir}${jsxify(url.pathname)}`;
if (transpile.has(jsx)) {
return await transpilation(jsx);
}

// tsx
const tsx = `${dir}${tsxify(url.pathname)}`;
if (transpile.has(tsx)) {
return await transpilation(tsx);
}

context.response.type = "text/html";
context.response.body = await render({
url,
root,
importmap,
lang,
cacheBuster,
});
};

return handler(context.request);
};

export default server;
16 changes: 15 additions & 1 deletion src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ const render = async (
Object.keys(helmet)
.map((i) => helmet[i].toString())
.join("")
}<script type="module" defer>import { createElement } from "${
}<script type="module" defer>${
isDev && socket(root)
}import { createElement } from "${
importmap.imports["react"]
}";import { hydrateRoot } from "${
importmap.imports["react-dom"]
Expand Down Expand Up @@ -234,3 +236,15 @@ const staticLocationHook = (
hook.history = [path];
return hook;
};

const socket = (root: string) => {
const url = new URL(root);
return `
const _ultra_socket = new WebSocket("ws://${url.host}/_ultra_socket");
_ultra_socket.addEventListener("message", (e) => {
if (e.data === "reload") {
location.reload();
}
});
`;
};
26 changes: 26 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,26 @@ const server = async (
const fileRootUri = `file://${Deno.cwd()}/${dir}`;
const link = await ultraloader({ importmap, cache });
const serverStart = Math.ceil(+new Date() / 100);
const listeners = new Set<WebSocket>();

const handler = async (request: Request) => {
const requestStart = Math.ceil(+new Date() / 100);
const cacheBuster = isDev ? requestStart : serverStart;
const { raw, transpile } = await assets(dir);
const url = new URL(request.url);

// web socket listener
if (isDev) {
if (url.pathname == "/_ultra_socket") {
const { socket, response } = Deno.upgradeWebSocket(request);
listeners.add(socket);
socket.onclose = () => {
listeners.delete(socket);
};
return response;
}
}

// static assets
if (raw.has(`${dir}${url.pathname}`)) {
const contentType = raw.get(`${dir}${url.pathname}`);
Expand Down Expand Up @@ -140,6 +153,19 @@ const server = async (
);
};

// async file watcher to send socket messages
if (isDev) {
(async () => {
for await (const { kind } of Deno.watchFs(dir, { recursive: true })) {
if (kind === "modify") {
for (const socket of listeners) {
socket.send("reload");
}
}
}
})();
}

console.log(`Ultra running ${root}`);
//@ts-ignore any
return serve(handler, { port: +port });
Expand Down
17 changes: 17 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,23 @@ export type StartOptions = {
env?: Record<string, unknown>;
};

type Context = {
request: Request;
response: {
body: string | ReadableStream<Uint8Array>;
type: string;
};
};

export type OakOptions = {
importmap: Importmap;
lang?: string;
root?: string;
dir?: string;
env?: Record<string, unknown>;
context: Context;
};

export type TransformOptions = {
source: string;
importmap: Importmap;
Expand Down

0 comments on commit a5417d1

Please sign in to comment.