From 3b314135c11d20cdead0ad83ba79ffc47c2108b2 Mon Sep 17 00:00:00 2001
From: ayame113 <40050810+ayame113@users.noreply.github.com>
Date: Wed, 12 Apr 2023 12:07:53 +0900
Subject: [PATCH 1/2] v3
---
README.md | 9 +-
example/serve.ts | 49 ++++-
example/serve_old.ts | 3 +
middlewear/README.md | 6 +
middlewear/_webp_encode.ts | 57 ++++++
middlewear/_webp_lib.ts | 59 ++++++
middlewear/gfm.ts | 55 ++++++
middlewear/ts-serve.ts | 35 ++++
middlewear/webp.ts | 32 +++
mod.ts | 26 +--
src/app/mod.ts | 187 ++++++++++++++++++
src/file_server.ts | 2 +-
src/oak.ts | 2 +-
oak_test.ts => src/oak_test.ts | 3 +-
src/utils/fource_instantiate_wasm.ts | 22 +++
.../utils/fource_instantiate_wasm_test.ts | 3 +-
{utils => src/utils}/transpile.ts | 0
{utils => src/utils}/transpile_bench.ts | 2 +-
{utils => src/utils}/transpile_response.ts | 0
.../utils}/transpile_response_test.ts | 0
{utils => src/utils}/transpile_test.ts | 0
21 files changed, 521 insertions(+), 31 deletions(-)
create mode 100644 example/serve_old.ts
create mode 100644 middlewear/README.md
create mode 100644 middlewear/_webp_encode.ts
create mode 100644 middlewear/_webp_lib.ts
create mode 100644 middlewear/gfm.ts
create mode 100644 middlewear/ts-serve.ts
create mode 100644 middlewear/webp.ts
create mode 100644 src/app/mod.ts
rename oak_test.ts => src/oak_test.ts (97%)
create mode 100644 src/utils/fource_instantiate_wasm.ts
rename mod_test.ts => src/utils/fource_instantiate_wasm_test.ts (88%)
rename {utils => src/utils}/transpile.ts (100%)
rename {utils => src/utils}/transpile_bench.ts (81%)
rename {utils => src/utils}/transpile_response.ts (100%)
rename {utils => src/utils}/transpile_response_test.ts (100%)
rename {utils => src/utils}/transpile_test.ts (100%)
diff --git a/README.md b/README.md
index ac725e5..d97340a 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,15 @@
-# ts-serve
+# jit-server
[![Test](https://github.com/ayame113/ts-serve/actions/workflows/test.yml/badge.svg)](https://github.com/ayame113/ts-serve/actions/workflows/test.yml)
[![codecov](https://codecov.io/gh/ayame113/ts-serve/branch/main/graph/badge.svg?token=mz0SfmUYRL)](https://codecov.io/gh/ayame113/ts-serve)
+**jit-server** is a file server with just-in-time compilation.
+
+> **Note** Up until version 2, this library was a specialized server for
+> transpiling TypeScript to JavaScript, called ts-serve.
+
+# ts-serve
+
TypeScript + ES Modules
Transpile TypeScript on the fly and serve it from your server as ES Modules.
diff --git a/example/serve.ts b/example/serve.ts
index 3efeaf0..eddaf98 100644
--- a/example/serve.ts
+++ b/example/serve.ts
@@ -1,3 +1,48 @@
import { serve } from "https://deno.land/std@0.178.0/http/mod.ts";
-import { serveDirWithTs } from "../mod.ts";
-serve((req) => serveDirWithTs(req, { fsRoot: "example" }));
+import { App } from "../mod.ts";
+import { tsServe } from "../middlewear/ts-serve.ts";
+import { markdown } from "../middlewear/gfm.ts";
+// import { webpConverter } from "../middlewear/webp.ts";
+// import basicAuth from "https://deno.land/x/lume@v1.15.3/middlewares/basic_auth.ts";
+
+const app = new App();
+
+// middleware
+app
+ // .use(basicAuth({
+ // users: {
+ // "user": "pass",
+ // },
+ // }))
+ .use(tsServe())
+ // .use(webpConverter())
+ .use(markdown({
+ renderOptions: { disableHtmlSanitization: true },
+ frontMatter: true,
+ format(body, { CSS }, frontMatter) {
+ console.log(frontMatter);
+ return `
+
+
+
+
+
+
+
+
+
+ ${body}
+
+
+
+ `;
+ },
+ }));
+
+serve(app.handler);
diff --git a/example/serve_old.ts b/example/serve_old.ts
new file mode 100644
index 0000000..3efeaf0
--- /dev/null
+++ b/example/serve_old.ts
@@ -0,0 +1,3 @@
+import { serve } from "https://deno.land/std@0.178.0/http/mod.ts";
+import { serveDirWithTs } from "../mod.ts";
+serve((req) => serveDirWithTs(req, { fsRoot: "example" }));
diff --git a/middlewear/README.md b/middlewear/README.md
new file mode 100644
index 0000000..d76dd4b
--- /dev/null
+++ b/middlewear/README.md
@@ -0,0 +1,6 @@
+## middlewares
+
+This directory contains middleware that can be used in conjunction with the
+`App` class exported from `/mod.ts`.
+
+A detailed usage example is in [../example/serve.ts](../example/serve.ts).
diff --git a/middlewear/_webp_encode.ts b/middlewear/_webp_encode.ts
new file mode 100644
index 0000000..fc6f348
--- /dev/null
+++ b/middlewear/_webp_encode.ts
@@ -0,0 +1,57 @@
+// https://github.com/jamsinclair/jSquash/blob/1edfc086e22b6aa01910cff5fd20826cf9e3dfa2/packages/webp/encode.ts
+// avoid top-level-await for deno deploy
+
+/**
+ * Copyright 2020 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Notice: I (Jamie Sinclair) have modified this file.
+ * Updated to support a partial subset of WebP encoding options to be provided.
+ * The WebP options are defaulted to defaults from the meta.ts file.
+ * Also manually allow instantiation of the Wasm Module.
+ */
+import type { WebPModule } from "https://esm.sh/@jsquash/webp@1.1.3/codec/enc/webp_enc";
+import type { EncodeOptions } from "https://esm.sh/@jsquash/webp@1.1.3/meta";
+
+import { defaultOptions } from "https://esm.sh/@jsquash/webp@1.1.3/meta";
+import { initEmscriptenModule } from "https://esm.sh/@jsquash/webp@1.1.3/utils";
+import { simd } from "https://esm.sh/wasm-feature-detect@1.5.0";
+
+import webpEncoder from "https://esm.sh/@jsquash/webp@1.1.3/codec/enc/webp_enc";
+import webpEncoderSimd from "https://esm.sh/@jsquash/webp@1.1.3/codec/enc/webp_enc_simd";
+
+let emscriptenModule: Promise;
+
+export async function init(module?: WebAssembly.Module): Promise {
+ if (await simd()) {
+ emscriptenModule = initEmscriptenModule(webpEncoderSimd, module);
+ return emscriptenModule;
+ }
+ emscriptenModule = initEmscriptenModule(webpEncoder, module);
+ return emscriptenModule;
+}
+
+export default async function encode(
+ data: ImageData,
+ options: Partial = {},
+): Promise {
+ if (!emscriptenModule) emscriptenModule = init();
+
+ const _options: EncodeOptions = { ...defaultOptions, ...options };
+ const module = await emscriptenModule;
+ const result = module.encode(data.data, data.width, data.height, _options);
+
+ if (!result) throw new Error("Encoding error.");
+
+ return result.buffer;
+}
diff --git a/middlewear/_webp_lib.ts b/middlewear/_webp_lib.ts
new file mode 100644
index 0000000..830ef67
--- /dev/null
+++ b/middlewear/_webp_lib.ts
@@ -0,0 +1,59 @@
+import decodeJpeg, {
+ init as initJpegWasm,
+} from "https://esm.sh/@jsquash/jpeg@1.1.5/decode";
+import decodePng, {
+ init as initPngWasm,
+} from "https://esm.sh/@jsquash/png@2.0.0/decode";
+// import encodeWebp, {
+// init as initWebpWasm,
+// } from "https://esm.sh/@jsquash/webp@1.1.3/encode";
+import encodeWebp, { init as initWebpWasm } from "./_webp_encode.ts";
+
+const jpegWasm =
+ "https://esm.sh/@jsquash/jpeg@1.1.5/codec/dec/mozjpeg_dec.wasm";
+const pngWasm = "https://esm.sh/@jsquash/png@2.0.0/codec/squoosh_png_bg.wasm";
+const webpWasm =
+ "https://esm.sh/@jsquash/webp@1.1.3/codec/enc/webp_enc_simd.wasm";
+
+async function loadWasmModule(url: string) {
+ return await WebAssembly.compileStreaming(fetch(url));
+}
+
+export const jpegWasmInit = loadWasmModule(jpegWasm).then(initJpegWasm);
+export const pngWasmInit = loadWasmModule(pngWasm).then(initPngWasm);
+export const webpWasmInit = loadWasmModule(webpWasm).then(initWebpWasm);
+
+globalThis.ImageData ??= class ImageData {
+ colorSpace = "srgb" as const;
+ data: Uint8ClampedArray;
+ width: number;
+ height: number;
+ constructor(data: Uint8ClampedArray, width: number, height?: number);
+ constructor(data: number, width: number);
+ constructor(
+ data: Uint8ClampedArray | number,
+ width: number,
+ height?: number,
+ ) {
+ if (!(data instanceof Uint8ClampedArray) || typeof height !== "number") {
+ throw new Error("unimplemented");
+ }
+ this.data = data;
+ this.width = width;
+ this.height = height;
+ }
+};
+
+export async function jpegToWebp(buf: ArrayBuffer) {
+ await jpegWasmInit;
+ const imageData = await decodeJpeg(buf);
+ await webpWasmInit;
+ return await encodeWebp(imageData);
+}
+
+export async function pngToWebp(buf: ArrayBuffer) {
+ await pngWasmInit;
+ const imageData = await decodePng(buf);
+ await webpWasmInit;
+ return await encodeWebp(imageData);
+}
diff --git a/middlewear/gfm.ts b/middlewear/gfm.ts
new file mode 100644
index 0000000..0ed0555
--- /dev/null
+++ b/middlewear/gfm.ts
@@ -0,0 +1,55 @@
+import { createTranspiler } from "../mod.ts";
+import {
+ CSS,
+ KATEX_CSS,
+ render,
+ type RenderOptions,
+} from "https://deno.land/x/gfm@0.2.0/mod.ts";
+import {
+ extract,
+ test,
+} from "https://deno.land/std@0.176.0/encoding/front_matter/any.ts";
+import type { JSONValue } from "https://deno.land/std@0.176.0/encoding/jsonc.ts";
+
+export interface MarkdownOptions {
+ /** Transpile only if the file name matches this value (format follows URLPattern). */
+ targetDir?: string;
+ /** If the file name matches this value, it will not be transpiled (format follows URLPattern). */
+ excludeDir?: string;
+ /** whether to parse frontMatter. If set to true, the parsed frontMatter is given to the argument of the format function. */
+ frontMatter?: boolean;
+ /** A function that creates full HTML from parsed markdown body. */
+ format(
+ body: string,
+ css: { CSS: string; KATEX_CSS: string },
+ frontMatter: JSONValue,
+ ): string | Promise;
+ /** Options passed to [deno.land/x/gfm](https://deno.land/x/gfm)'s render function */
+ renderOptions?: RenderOptions;
+}
+
+/** Middleware for converting markdown to HTML using [deno.land/x/gfm](https://deno.land/x/gfm) . */
+export function markdown(options: MarkdownOptions) {
+ const {
+ targetDir,
+ excludeDir,
+ frontMatter,
+ format,
+ renderOptions,
+ } = options;
+
+ return createTranspiler({
+ from: ".md",
+ to: ".html",
+ targetDir,
+ excludeDir,
+ fn(content) {
+ let attrs: JSONValue = {};
+ if (frontMatter && test(content)) {
+ ({ body: content, attrs } = extract(content));
+ }
+ const body = render(content, renderOptions);
+ return format(body, { CSS, KATEX_CSS }, attrs);
+ },
+ });
+}
diff --git a/middlewear/ts-serve.ts b/middlewear/ts-serve.ts
new file mode 100644
index 0000000..c2da103
--- /dev/null
+++ b/middlewear/ts-serve.ts
@@ -0,0 +1,35 @@
+import { createTranspiler } from "../src/app/mod.ts";
+import { fourceInstantiateWasm } from "../src/utils/fource_instantiate_wasm.ts";
+import { MediaType, transpile } from "../src/utils/transpile.ts";
+
+export interface TsServeOptions {
+ /** Transpile only if the file name matches this value (format follows URLPattern). */
+ targetDir?: string;
+ /** If the file name matches this value, it will not be transpiled (format follows URLPattern). */
+ excludeDir?: string;
+}
+
+/**
+ * A Promise that resolves when the wasm files used internally by this library are initialized.
+ * Normally you wouldn't use this variable, but if a test gives an error that it's leaking an asynchronous resource, awaiting this promise before running this test might solve the problem.
+ */
+export const denoEmitWasmInitPromise = fourceInstantiateWasm();
+
+/** Middleware that transpiles TypeScript to JavaScript. */
+export function tsServe({ targetDir, excludeDir }: TsServeOptions = {}) {
+ return createTranspiler({
+ from: [".jsx", ".tsx", ".ts"],
+ to: ".js",
+ targetDir,
+ excludeDir,
+ fn(content, { ctx, request }) {
+ const url = new URL(request.url);
+ const mediaType = {
+ ".ts": MediaType.TypeScript,
+ ".jsx": MediaType.Jsx,
+ ".tsx": MediaType.Tsx,
+ }[ctx.type];
+ return transpile(content, url, mediaType);
+ },
+ });
+}
diff --git a/middlewear/webp.ts b/middlewear/webp.ts
new file mode 100644
index 0000000..34e5325
--- /dev/null
+++ b/middlewear/webp.ts
@@ -0,0 +1,32 @@
+import { createTranspiler } from "../mod.ts";
+import { jpegToWebp, pngToWebp } from "./_webp_lib.ts";
+
+export interface WebpConverterOptions {
+ /** Transpile only if the file name matches this value (format follows URLPattern). */
+ targetDir?: string;
+ /** If the file name matches this value, it will not be transpiled (format follows URLPattern). */
+ excludeDir?: string;
+}
+
+/** Middleware to transpile images to webp. */
+export function webpConverter(
+ { targetDir, excludeDir }: WebpConverterOptions = {},
+) {
+ return createTranspiler({
+ from: [".jpg", ".jpeg", ".png"],
+ to: ".webp",
+ type: "arrayBuffer",
+ targetDir,
+ excludeDir,
+ async fn(content, { ctx, request }) {
+ if (!request.headers.get("accept")?.includes("image/webp")) {
+ throw new Error("Accept header does not contain image/webp.");
+ }
+ if (ctx.type === ".png") {
+ return new Uint8Array(await pngToWebp(content));
+ } else {
+ return new Uint8Array(await jpegToWebp(content));
+ }
+ },
+ });
+}
diff --git a/mod.ts b/mod.ts
index a33defb..0fba913 100644
--- a/mod.ts
+++ b/mod.ts
@@ -1,25 +1,5 @@
export * from "./src/oak.ts";
export * from "./src/file_server.ts";
-export * from "./utils/transpile.ts";
-import { MediaType, transpile } from "./utils/transpile.ts";
-
-/**
- * Calling this function will load the wasm file used in the deno_emit of the dependency.
- * Even if you don't call this function, if you call the transpile function, the wasm file will be read automatically at that timing.
- * However, performance can be an issue on the server as loading the wasm file takes time.
- * In that case, calling this function in advance can speed up later calls to the transpile function.
- *
- * ```ts
- * import { serve } from "https://deno.land/std@0.178.0/http/mod.ts";
- * import { serveDirWithTs, fourceInstantiateWasm } from "https://deno.land/x/ts_serve@$MODULE_VERSION/mod.ts";
- *
- * // load the wasm file in the background when the server starts.
- * fourceInstantiateWasm();
- * serve((request) => serveDirWithTs(request));
- * ```
- */
-export async function fourceInstantiateWasm() {
- try {
- await transpile("", new URL("file:///src"), MediaType.TypeScript);
- } catch (_) { /* ignore error*/ }
-}
+export * from "./src/utils/transpile.ts";
+export * from "./src/utils/fource_instantiate_wasm.ts";
+export * from "./src/app/mod.ts";
diff --git a/src/app/mod.ts b/src/app/mod.ts
new file mode 100644
index 0000000..50af709
--- /dev/null
+++ b/src/app/mod.ts
@@ -0,0 +1,187 @@
+import type { ConnInfo } from "https://deno.land/std@0.178.0/http/mod.ts";
+import { serveDir } from "https://deno.land/std@0.178.0/http/file_server.ts";
+import { contentType } from "https://deno.land/std@0.178.0/media_types/mod.ts";
+import { extname } from "https://deno.land/std@0.178.0/path/mod.ts";
+
+/** Middleware type used in `App#use`. */
+export type Middleware = (
+ req: Request,
+ next: (req: Request) => Promise,
+ conn: ConnInfo,
+ ctx: Context,
+) => Promise;
+
+/** The type of context passed to the middleware. */
+export interface Context {
+ /** Represents the current file type. An extension is usually used. (e.g. `.ts`, `.md`) */
+ type: T;
+}
+
+/**
+ * Create a file server. You can apply middleware to each request using the `app.use` method.
+ *
+ * Middleware runs similar to express, oak, and hono, but it is specialized for file servers and can be used in conjunction with other frameworks.
+ *
+ * ```ts
+ * import { serve } from "https://deno.land/std@0.178.0/http/mod.ts";
+ * import { App } from "https://deno.land/x/ts_serve@$MODULE_VERSION/mod.ts";
+ * import { tsServe } from "https://deno.land/x/ts_serve@$MODULE_VERSION/middlewear/ts-serve.ts";
+ *
+ * const app = new App();
+ *
+ * // use middleware
+ * app.use(tsServe());
+ *
+ * // serve handler
+ * serve(app.handler);
+ * ```
+ */
+export class App {
+ #handler;
+ #middleware: Middleware[] = [];
+ constructor({ handler = serveDir }: {
+ handler?: (req: Request) => Response | Promise;
+ } = {}) {
+ this.#handler = handler;
+ }
+ /** Add middleware to the server. */
+ use(...middleware: Middleware[]) {
+ this.#middleware.push(...middleware);
+ return this;
+ }
+ /** Server handler function. It takes a Request object and returns a Response object. */
+ handler = async (req: Request, conn: ConnInfo): Promise => {
+ // not supported range request
+ if (req.headers.has("Range")) {
+ req = new Request(req);
+ req.headers.delete("Range");
+ }
+ const { pathname } = new URL(req.url);
+ const ctx: Context = {
+ type: extname(pathname) as `.${string}` || ".html", // todo handle index.html or other
+ };
+ return await this.#dispatchMiddleware(0, req, conn, ctx);
+ };
+ async #dispatchMiddleware(
+ i: number,
+ req: Request,
+ conn: ConnInfo,
+ ctx: Context,
+ ): Promise {
+ if (i === this.#middleware.length) {
+ return await this.#handler(req);
+ }
+ return await this.#middleware[i](
+ req,
+ (req) => this.#dispatchMiddleware(i + 1, req, conn, ctx),
+ conn,
+ ctx,
+ );
+ }
+}
+
+export interface TranspilerOptions<
+ F extends `.${string}`,
+ T extends "string" | "arrayBuffer" | undefined,
+> {
+ /** Extension to transpile. */
+ from: F | readonly F[];
+ /** Extension after transpilation. */
+ to: `.${string}`;
+ /** Type of argument passed to optional function `fn`. */
+ type?: T;
+ /** Transpile only if the file name matches this value (format follows URLPattern). */
+ targetDir?: string;
+ /** If the file name matches this value, it will not be transpiled (format follows URLPattern). */
+ excludeDir?: string;
+ /** Function called when transpiling. Takes the pre-transpiled content as an argument and returns the post-transpiled value. */
+ fn(originalContent: T extends "arrayBuffer" ? ArrayBuffer : string, options: {
+ ctx: Context;
+ conn: ConnInfo;
+ request: Request;
+ response: Response;
+ }):
+ | Promise>
+ | string
+ | Uint8Array
+ | ReadableStream;
+}
+
+/** Utilities for creating middleware. You can create middleware that transpiles only certain extensions. */
+export function createTranspiler<
+ F extends `.${string}`,
+ T extends "string" | "arrayBuffer" | undefined = "string",
+>(
+ options: TranspilerOptions,
+): Middleware {
+ const { from, to, type = "string", targetDir, excludeDir, fn } = options;
+ const preExtensions = new Set(Array.isArray(from) ? from : [from]);
+ const postMediaType = contentType(to);
+ const includePattern = targetDir
+ ? new URLPattern({ pathname: targetDir })
+ : undefined;
+ const excludePattern = excludeDir
+ ? new URLPattern({ pathname: excludeDir })
+ : undefined;
+
+ return async function (req, next, conn, ctx) {
+ const res = await next(req);
+ if (includePattern && !includePattern.test(req.url)) {
+ return res;
+ }
+ if (excludePattern && excludePattern.test(req.url)) {
+ return res;
+ }
+ if (res.status !== 200) {
+ return res;
+ }
+ if (!preExtensions.has(ctx.type)) {
+ return res;
+ }
+
+ let content;
+ switch (type) {
+ case "string":
+ content = await res.text();
+ break;
+ case "arrayBuffer":
+ content = await res.arrayBuffer();
+ break;
+ default: {
+ const _: never = type;
+ throw new Error("unreachable");
+ }
+ }
+ const transpiled = await fn(
+ content as T extends "arrayBuffer" ? ArrayBuffer : string,
+ {
+ ctx: ctx as Context,
+ conn,
+ request: req,
+ response: res,
+ },
+ );
+ if (transpiled == null) {
+ console.warn("Transpile skipped because transpiler returned null.");
+ return new Response(content, {
+ headers: res.headers,
+ status: res.status,
+ statusText: res.statusText,
+ });
+ }
+
+ if (postMediaType) {
+ res.headers.set("Content-Type", postMediaType);
+ } else {
+ res.headers.delete("Content-Type");
+ }
+ res.headers.delete("Content-Length");
+ ctx.type = to;
+
+ return new Response(transpiled, {
+ headers: res.headers,
+ status: res.status,
+ statusText: res.statusText,
+ });
+ };
+}
diff --git a/src/file_server.ts b/src/file_server.ts
index 697c502..8a30631 100644
--- a/src/file_server.ts
+++ b/src/file_server.ts
@@ -5,7 +5,7 @@ import {
type ServeFileOptions,
} from "https://deno.land/std@0.178.0/http/file_server.ts";
-import { transpileResponse } from "../utils/transpile_response.ts";
+import { transpileResponse } from "./utils/transpile_response.ts";
/**
* This can be used in the same way as the [serveFile](https://doc.deno.land/https://deno.land/std@0.178.0/http/file_server.ts/~/serveFile) function of the standard library, but if the file is TypeScript, it will be rewritten to JavaScript.
diff --git a/src/oak.ts b/src/oak.ts
index 96b8aff..48c5b01 100644
--- a/src/oak.ts
+++ b/src/oak.ts
@@ -1,7 +1,7 @@
import type { Context } from "https://deno.land/x/oak@v12.0.1/mod.ts";
import { convertBodyToBodyInit } from "https://deno.land/x/oak@v12.0.1/response.ts";
-import { MediaType, transpile } from "../utils/transpile.ts";
+import { MediaType, transpile } from "./utils/transpile.ts";
const decoder = new TextDecoder();
const tsType = new Set(
diff --git a/oak_test.ts b/src/oak_test.ts
similarity index 97%
rename from oak_test.ts
rename to src/oak_test.ts
index 0c92e88..c0a50e3 100644
--- a/oak_test.ts
+++ b/src/oak_test.ts
@@ -4,7 +4,8 @@ import {
} from "https://deno.land/std@0.178.0/testing/asserts.ts";
import { deferred } from "https://deno.land/std@0.178.0/async/mod.ts";
import { Application } from "https://deno.land/x/oak@v12.0.1/mod.ts";
-import { MediaType, transpile, tsMiddleware } from "./mod.ts";
+import { tsMiddleware } from "./oak.ts";
+import { MediaType, transpile } from "./utils/transpile.ts";
const port = 8888;
const jsContentType = "application/javascript; charset=UTF-8";
diff --git a/src/utils/fource_instantiate_wasm.ts b/src/utils/fource_instantiate_wasm.ts
new file mode 100644
index 0000000..18e16c5
--- /dev/null
+++ b/src/utils/fource_instantiate_wasm.ts
@@ -0,0 +1,22 @@
+import { MediaType, transpile } from "./transpile.ts";
+
+/**
+ * Calling this function will load the wasm file used in the deno_emit of the dependency.
+ * Even if you don't call this function, if you call the transpile function, the wasm file will be read automatically at that timing.
+ * However, performance can be an issue on the server as loading the wasm file takes time.
+ * In that case, calling this function in advance can speed up later calls to the transpile function.
+ *
+ * ```ts
+ * import { serve } from "https://deno.land/std@0.178.0/http/mod.ts";
+ * import { serveDirWithTs, fourceInstantiateWasm } from "https://deno.land/x/ts_serve@$MODULE_VERSION/mod.ts";
+ *
+ * // load the wasm file in the background when the server starts.
+ * fourceInstantiateWasm();
+ * serve((request) => serveDirWithTs(request));
+ * ```
+ */
+export async function fourceInstantiateWasm() {
+ try {
+ await transpile("", new URL("file:///src"), MediaType.TypeScript);
+ } catch (_) { /* ignore error*/ }
+}
diff --git a/mod_test.ts b/src/utils/fource_instantiate_wasm_test.ts
similarity index 88%
rename from mod_test.ts
rename to src/utils/fource_instantiate_wasm_test.ts
index f2ea3ce..bf47d2c 100644
--- a/mod_test.ts
+++ b/src/utils/fource_instantiate_wasm_test.ts
@@ -4,7 +4,8 @@ import {
stub,
} from "https://deno.land/std@0.178.0/testing/mock.ts";
-import { fourceInstantiateWasm, MediaType, transpile } from "./mod.ts";
+import { MediaType, transpile } from "./transpile.ts";
+import { fourceInstantiateWasm } from "./fource_instantiate_wasm.ts";
Deno.test({
name: "fourceInstantiateWasm",
diff --git a/utils/transpile.ts b/src/utils/transpile.ts
similarity index 100%
rename from utils/transpile.ts
rename to src/utils/transpile.ts
diff --git a/utils/transpile_bench.ts b/src/utils/transpile_bench.ts
similarity index 81%
rename from utils/transpile_bench.ts
rename to src/utils/transpile_bench.ts
index fc3c9cd..ef8d14e 100644
--- a/utils/transpile_bench.ts
+++ b/src/utils/transpile_bench.ts
@@ -1,4 +1,4 @@
-import { MediaType, transpile } from "../mod.ts";
+import { MediaType, transpile } from "../../mod.ts";
const codes = ["function name(params:type) {}"];
const url = new URL("file:///src.ts");
diff --git a/utils/transpile_response.ts b/src/utils/transpile_response.ts
similarity index 100%
rename from utils/transpile_response.ts
rename to src/utils/transpile_response.ts
diff --git a/utils/transpile_response_test.ts b/src/utils/transpile_response_test.ts
similarity index 100%
rename from utils/transpile_response_test.ts
rename to src/utils/transpile_response_test.ts
diff --git a/utils/transpile_test.ts b/src/utils/transpile_test.ts
similarity index 100%
rename from utils/transpile_test.ts
rename to src/utils/transpile_test.ts
From 038baf67d0338572bc33b0121d6e6df18ce280e8 Mon Sep 17 00:00:00 2001
From: ayame113 <40050810+ayame113@users.noreply.github.com>
Date: Mon, 17 Apr 2023 13:06:37 +0900
Subject: [PATCH 2/2] fix import path
---
src/utils/transpile_response.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/utils/transpile_response.ts b/src/utils/transpile_response.ts
index 0f06d55..9559fd5 100644
--- a/src/utils/transpile_response.ts
+++ b/src/utils/transpile_response.ts
@@ -10,7 +10,7 @@ const jsContentType = contentType(".js");
* import { serve } from "https://deno.land/std@0.178.0/http/mod.ts";
* import { serveFile } from "https://deno.land/std@0.178.0/http/file_server.ts";
*
- * import { transpileResponse } from "https://deno.land/x/ts_serve@$MODULE_VERSION/utils/transpile_response.ts"
+ * import { transpileResponse } from "https://deno.land/x/ts_serve@$MODULE_VERSION/src/utils/transpile_response.ts"
*
* serve(async (request) => {
* const filePath = "./mod.ts";