Skip to content

Commit

Permalink
feat(hono): Make workable on any runtime (#64)
Browse files Browse the repository at this point in the history
Use the env adapter to avoid Deno-dedicated API.
  • Loading branch information
5ouma authored Sep 27, 2024
1 parent 87188e5 commit 539fae1
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 104 deletions.
4 changes: 2 additions & 2 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
},
"fmt": { "exclude": ["LICENSE", "README.md", ".github/**/*.md"] },
"tasks": {
"run": "deno run --env='.env' --allow-env='REPOSITORY_OWNER,REPOSITORY_NAME,REPOSITORY_PATH,GITHUB_TOKEN' --allow-net='0.0.0.0,api.github.com'",
"run": "deno run --env='.env' --allow-env --allow-net='0.0.0.0,api.github.com'",
"start": "deno task run src/server.ts",
"dev": "deno task run --watch src/server.ts",
"test": "deno test --allow-env='REPOSITORY_OWNER,REPOSITORY_NAME,REPOSITORY_PATH,GITHUB_TOKEN' --allow-net='0.0.0.0,api.github.com' --parallel --shuffle",
"test": "deno test --allow-env --allow-net='0.0.0.0,api.github.com' --parallel --shuffle",
"cov": "deno task test --coverage && deno coverage --lcov > coverage.lcov"
},
"imports": {
Expand Down
11 changes: 4 additions & 7 deletions src/libs/content.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,25 @@ import { assertEquals, assertExists, assertStringIncludes } from "@std/assert";
import { STATUS_CODE } from "@std/http/status";

import { getContent } from "./content.ts";
import { exportRepo, testRef, testRepo, unknownRepo } from "./test_utils.ts";
import { testRef, testRepo, unknownRepo } from "./test_utils.ts";

Deno.test("Get Content", async (t: Deno.TestContext) => {
await t.step("normal", async () => {
exportRepo(testRepo);
const [data, status] = await getContent();
const [data, status] = await getContent(testRepo);

assertExists(data);
assertEquals(status, STATUS_CODE.OK);
});

await t.step("with ref", async () => {
exportRepo(testRepo);
const [data, status] = await getContent(testRef);
const [data, status] = await getContent(testRepo, testRef);

assertExists(data);
assertEquals(status, STATUS_CODE.OK);
});

await t.step("not found", async () => {
exportRepo(unknownRepo);
const [data, status] = await getContent();
const [data, status] = await getContent(unknownRepo);

assertStringIncludes(data, `⚠️ ${STATUS_CODE.NotFound}:`);
assertEquals(status, STATUS_CODE.NotFound);
Expand Down
36 changes: 29 additions & 7 deletions src/libs/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Octokit } from "@octokit/rest";
import type { StatusCode } from "@std/http";
export type { StatusCode };

import { getRepository, githubToken } from "./env.ts";
import type { Repository } from "./types.ts";

/**
* Get the content of the repository.
Expand All @@ -12,29 +12,51 @@ import { getRepository, githubToken } from "./env.ts";
*
* @example Use the default branch
* ```ts
* const [content, status] = await getContent();
* const repository: Repository = {
* owner: "denoland",
* name: "deno",
* path: "README.md",
* };
* const [content, status] = await getContent(repository);
* ```
* @example Use a specific branch
* ```ts
* const repository: Repository = {
* owner: "denoland",
* name: "deno",
* path: "README.md",
* };
* const branch = "main";
* const [content, status] = await getContent(branch);
* const [content, status] = await getContent(repository, branch);
* ```
* @example Use a specific tag
* ```ts
* const repository: Repository = {
* owner: "denoland",
* name: "deno",
* path: "README.md",
* };
* const tag = "v1.0.0";
* const [content, status] = await getContent(tag);
* const [content, status] = await getContent(repository, tag);
* ```
* @example Use a specific commit
* ```ts
* const repository: Repository = {
* owner: "denoland",
* name: "deno",
* path: "README.md",
* };
* const commit = "a1b2c3d4e5f6";
* const [content, status] = await getContent(commit);
* const [content, status] = await getContent(repository, commit);
* ```
*/

export async function getContent(
repository: Repository,
ref: string | undefined = undefined,
token: string | undefined = undefined,
): Promise<[string, StatusCode]> {
const octokit = new Octokit({ auth: githubToken });
const repository = getRepository();
const octokit = new Octokit({ auth: token });

try {
const { status, data } = await octokit.rest.repos.getContent({
Expand Down
24 changes: 0 additions & 24 deletions src/libs/env.test.ts

This file was deleted.

37 changes: 0 additions & 37 deletions src/libs/env.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/libs/mod.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./content.ts";
export * from "./redirect.ts";
export type { Repository } from "./types.ts";
8 changes: 3 additions & 5 deletions src/libs/redirect.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import { assertEquals } from "@std/assert";

import { checkRedirect } from "./redirect.ts";
import { exportRepo, testRef, testRepo, testUserAgent } from "./test_utils.ts";
import { testRef, testRepo, testUserAgent } from "./test_utils.ts";
import { getGitHubUrl } from "./utils.ts";

Deno.test("Redirect Detection", async (t: Deno.TestContext) => {
await t.step("normal", () => {
exportRepo(testRepo);
const url: URL | null = checkRedirect(testUserAgent);
const url: URL | null = checkRedirect(testUserAgent, testRepo);

assertEquals(url, getGitHubUrl(testRepo));
});

await t.step("with ref", () => {
exportRepo(testRepo);
const url: URL | null = checkRedirect(testUserAgent, testRef);
const url: URL | null = checkRedirect(testUserAgent, testRepo, testRef);

assertEquals(url, getGitHubUrl(testRepo, testRef));
});
Expand Down
33 changes: 27 additions & 6 deletions src/libs/redirect.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { UserAgent } from "@std/http/user-agent";
export type { UserAgent };

import { getRepository } from "./env.ts";
import type { Repository } from "./types.ts";
import { getGitHubUrl } from "./utils.ts";

/**
Expand All @@ -14,32 +14,53 @@ import { getGitHubUrl } from "./utils.ts";
* @example Use the default branch
* ```ts
* const userAgent = new UserAgent("Chrome/1.2.3");
* const url: URL | null = checkRedirect(userAgent);
* const repository: Repository = {
* owner: "denoland",
* name: "deno",
* path: "README.md",
* };
* const url: URL | null = checkRedirect(userAgent, repository);
* ```
* @example Use a specific branch
* ```ts
* const userAgent = new UserAgent("Chrome/1.2.3");
* const repository: Repository = {
* owner: "denoland",
* name: "deno",
* path: "README.md",
* };
* const branch = "main";
* const url: URL | null = checkRedirect(userAgent, branch);
* const url: URL | null = checkRedirect(userAgent, repository, branch);
* ```
* @example Use a specific tag
* ```ts
* const userAgent = new UserAgent("Chrome/1.2.3");
* const repository: Repository = {
* owner: "denoland",
* name: "deno",
* path: "README.md",
* };
* const tag = "v1.0.0";
* const url: URL | null = checkRedirect(userAgent, tag);
* const url: URL | null = checkRedirect(userAgent, repository, tag);
* ```
* @example Use a specific commit
* ```ts
* const userAgent = new UserAgent("Chrome/1.2.3");
* const repository: Repository = {
* owner: "denoland",
* name: "deno",
* path: "README.md",
* };
* const commit = "a1b2c3d4e5f6";
* const url: URL | null = checkRedirect(userAgent, commit);
* const url: URL | null = checkRedirect(userAgent, repository, commit);
* ```
*/
export function checkRedirect(
userAgent: UserAgent,
repository: Repository,
ref: string = "master",
): URL | null {
const url = getGitHubUrl(getRepository(), ref);
const url = getGitHubUrl(repository, ref);

return userAgent?.browser.name ? url : null;
}
14 changes: 0 additions & 14 deletions src/libs/test_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,3 @@ export function exportRepo(repository: Repository) {
Deno.env.set("REPOSITORY_NAME", repository.name);
Deno.env.set("REPOSITORY_PATH", repository.path);
}

/**
* Clear the repository details from the environment variables.
*
* @example
* ```ts
* clearRepo();
* ```
*/
export function clearRepo() {
Deno.env.delete("REPOSITORY_OWNER");
Deno.env.delete("REPOSITORY_NAME");
Deno.env.delete("REPOSITORY_PATH");
}
21 changes: 19 additions & 2 deletions src/server.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { type Context, Hono } from "@hono/hono";
export type { Hono };
import { env } from "@hono/hono/adapter";
import { logger } from "@hono/hono/logger";
import { STATUS_CODE } from "@std/http/status";
import { UserAgent } from "@std/http/user-agent";

import { checkRedirect, getContent } from "./libs/mod.ts";
import { checkRedirect, getContent, type Repository } from "./libs/mod.ts";

/**
* The Hono application for this project.
Expand All @@ -24,15 +25,31 @@ export const app: Hono = new Hono();
app.use(logger());
app
.get("/:ref?", async (ctx: Context) => {
const { REPOSITORY_OWNER, REPOSITORY_NAME, REPOSITORY_PATH, GITHUB_TOKEN } =
env<
{
REPOSITORY_OWNER: string;
REPOSITORY_NAME: string;
REPOSITORY_PATH: string;
GITHUB_TOKEN: string;
}
>(ctx);
const repository: Repository = {
owner: REPOSITORY_OWNER,
name: REPOSITORY_NAME,
path: REPOSITORY_PATH,
};
const ref: string = ctx.req.param("ref");

const url: URL | null = checkRedirect(
new UserAgent(ctx.req.header("User-Agent") ?? ""),
repository,
ref,
);

return url
? ctx.redirect(url.toString(), STATUS_CODE.PermanentRedirect)
: ctx.text(...await getContent(ref));
: ctx.text(...await getContent(repository, ref, GITHUB_TOKEN));
})
.get("*", (ctx: Context) => {
return ctx.redirect("/", STATUS_CODE.SeeOther);
Expand Down

0 comments on commit 539fae1

Please sign in to comment.