diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index d59927b..6380188 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,14 +1,20 @@ -# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster -ARG VARIANT=16-bullseye -FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT} +FROM debian:bullseye as tjs -# [Optional] Uncomment this section to install additional OS packages. -# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ -# && apt-get -y install --no-install-recommends +RUN apt-get update -y && apt install cmake build-essential curl libcurl4-openssl-dev git -y -# [Optional] Uncomment if you want to install an additional version of node using nvm -# ARG EXTRA_NODE_VERSION=10 -# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" +WORKDIR / -# [Optional] Uncomment if you want to install more global node packages -# RUN su node -c "npm install -g " +RUN git clone --recursive https://github.com/saghul/txiki.js --shallow-submodules && cd txiki.js && \ + make + +FROM denoland/deno:bin AS deno + +FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:16-bullseye + +COPY --from=deno /deno /usr/local/bin/deno +COPY --from=tjs /txiki.js/build/tjs /usr/local/bin/tjs + +RUN su node -c "npm install -g esbuild webpack jest ts-node" + +ARG EXTRA_NODE_VERSION=14 +RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" diff --git a/.devcontainer/base.Dockerfile b/.devcontainer/base.Dockerfile index 35b6654..9792657 100644 --- a/.devcontainer/base.Dockerfile +++ b/.devcontainer/base.Dockerfile @@ -1,6 +1,6 @@ # [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster ARG VARIANT=16-bullseye -FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT} +FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:${VARIANT} # Install tslint, typescript. eslint is installed by javascript image ARG NODE_MODULES="tslint-to-eslint-config typescript" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0d46cbe..ab1fcd0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,17 +1,11 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: -// https://github.com/microsoft/vscode-dev-containers/tree/v0.238.1/containers/typescript-node { - "name": "Node.js & TypeScript", + "name": "Handlers.js Devlopment Container", "build": { "dockerfile": "Dockerfile", - // Update 'VARIANT' to pick a Node version: 18, 16, 14. - // Append -bullseye or -buster to pin to an OS version. - // Use -bullseye variants on local on arm64/Apple Silicon. - "args": { + "args": { "VARIANT": "16-bullseye" } }, - // Configure tool-specific properties. "customizations": { // Configure properties specific to VS Code. @@ -21,19 +15,28 @@ "dbaeumer.vscode-eslint", "ms-vscode.vscode-typescript-next", "oderwat.indent-rainbow", + "GitHub.copilot", + "redhat.vscode-yaml", + "ms-azuretools.vscode-docker" ] } }, - // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], - // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "yarn install", - // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "node", "features": { - "git": "latest" + "git": "latest", + "docker-in-docker": { + "version": "latest", + "moby": true, + "dockerDashComposeVersion": "v1" + }, + "rust": { + "version": "latest", + "profile": "minimal" + } } -} +} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3cd84c5..8414318 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: push jobs: - build: + node: runs-on: ubuntu-latest strategy: @@ -12,11 +12,35 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + - name: Install dependencies run: yarn install + - name: Test handlers.js - run: yarn test + run: yarn test:node + + deno: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Use Node.js Latest + uses: actions/setup-node@v3 + with: + node-version: vx.x.x + + - name: Install dependencies + run: yarn install + + - uses: denoland/setup-deno@v1 + with: + deno-version: vx.x.x + + - name: Test handlers.js + run: yarn test:deno diff --git a/package.json b/package.json index 8e683ad..c296389 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,8 @@ "clean": "rm -rf ./dist", "demo": "env NODE_ENV=development yarn build:node && node ./dist/main.node.js", "tsc": "tsc", - "test": "jest", + "test:node": "jest ./test/node.test.ts", + "test:deno": "BUILD_TARGET=deno:test webpack && jest ./test/deno.test.ts", "coverage": "jest --collectCoverage --" }, "engines": { diff --git a/src/platform/serviceworker.ts b/src/platform/serviceworker.ts index 61a1294..9138ce3 100644 --- a/src/platform/serviceworker.ts +++ b/src/platform/serviceworker.ts @@ -35,6 +35,7 @@ export class SWPlatformAdapter implements platformAdapater { } async handleResponse(response: response): Promise { + if (response.status === 204) { response.body = null; } const nativResponse = new Response(response.body, { status: response.status, headers: response.headers.headers, diff --git a/src/route.ts b/src/route.ts index 85115c7..ebe8bfa 100644 --- a/src/route.ts +++ b/src/route.ts @@ -10,26 +10,33 @@ interface matchedStatus { }[]; } +interface regExpKey { + name: string; + prefix: string; + suffix: string; + pattern: string; + modifier: string; +}; + export class route { - public paths: path[]; + private paths: path[]; public handlers: handler[]; + private regExps: { regExp: RegExp, keys: regExpKey[] }[] = []; constructor(paths: path[], handlers: handler[]) { this.paths = paths; this.handlers = handlers; + + this.paths.forEach(path => { + const keys: regExpKey[] = []; + this.regExps.push({ regExp: pathToRegexp(path, keys), keys }); + }) } async exec(path: string): Promise { let Answer = await Promise.all>( - this.paths.map(async (it) => { - const keys: { - name: string; - prefix: string; - suffix: string; - pattern: string; - modifier: string; - }[] = []; - const regExp = pathToRegexp(it, keys); - const answer = regExp.exec(path); + this.regExps.map(async (it) => { + + const answer = it.regExp.exec(path); if (answer === null) return { matched: false, @@ -38,7 +45,7 @@ export class route { let attributes: matchedStatus["attributes"] = []; - keys.forEach((key, index) => { + it.keys.forEach((key, index) => { attributes.push({ name: key.name, value: answer[index + 1], diff --git a/test/deno.test.ts b/test/deno.test.ts new file mode 100644 index 0000000..2be1ed3 --- /dev/null +++ b/test/deno.test.ts @@ -0,0 +1,64 @@ +import { spawn } from "child_process"; + +import Axios from "axios"; + +const Instance = Axios.create({ + baseURL: "http://localhost:3000" +}) + +spawn(`deno`, [ + "run", "--allow-net", `${__dirname}/../dist/test.deno.js` +]); + +const randomString = () => Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); + +describe("Test server", () => { + test("normal 200 response", async () => { + expect.assertions(2); + + await new Promise((r) => setTimeout(r, 200)); + + const { data, status } = await Instance.get("/"); + expect(status).toEqual(200); + expect(data).toEqual("200 OK"); + }) + + test("post response", async () => { + expect.assertions(2); + const string = randomString(); + + const { data, status } = await Instance.post("/post", string); + + expect(status).toEqual(200); + expect(data).toEqual(string); + }) + + test("change header and status code", async () => { + expect.assertions(3); + + const { data, status, headers } = await Instance.get("/header"); + + expect(status).toEqual(204); + expect(headers["itis"]).toEqual("work"); + expect(data).toEqual(""); + }) + + test("get param", async () => { + expect.assertions(2); + + const string = randomString(); + const { data, status } = await Instance.get(`/info/${string}`); + + expect(status).toEqual(200); + expect(data).toEqual(string); + }) + + test("chain interrupted", async () => { + expect.assertions(2); + + const { data, status } = await Instance.get(`/info/foo`); + + expect(status).toEqual(200); + expect(data).toEqual("hit"); + }) +}) \ No newline at end of file diff --git a/test/test-server.deno.ts b/test/test-server.deno.ts new file mode 100644 index 0000000..5a89e66 --- /dev/null +++ b/test/test-server.deno.ts @@ -0,0 +1,3 @@ +import App from "./test-server"; + +App.listen(3000); \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index e506420..d1acd6c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -81,6 +81,10 @@ export default () => { 'Promise': 'bluebird' }) ) + case "deno:test": + config.target = "webworker"; + config.output.filename = "test.deno.js"; + config.entry = "./test/test-server.deno.ts"; default: config.target = "es6"; }