Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/app/app/assets/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,7 @@ details {
summary {
@apply cursor-pointer;
}

code > pre {
@apply rounded-lg overflow-x-scroll bg-transparent! text-sm;
}
54 changes: 45 additions & 9 deletions packages/app/app/components/Commits.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<script lang="ts" setup>
import type { RendererObject } from "marked";
import bash from "@shikijs/langs/bash";
import githubDark from "@shikijs/themes/github-dark";
import githubLight from "@shikijs/themes/github-light";
import { marked } from "marked";
import { createHighlighterCoreSync, type HighlighterCore } from "shiki/core";
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";

const props = defineProps<{
owner: string;
Expand Down Expand Up @@ -42,16 +47,47 @@ const selectedCommit = shallowRef<
// Markdown

// Add target to links
const renderer: RendererObject = {
link(originalLink) {
const link = marked.Renderer.prototype.link.call(this, originalLink);
return link.replace("<a", "<a target='_blank' rel='noreferrer' ");
},
};
marked.use({ renderer });

// Pagination
const colorMode = useColorMode();
let shiki: HighlighterCore;

onBeforeMount(async () => {
// if (typeof window === 'undefined') {
// const { loadWasm } = await import('shiki')
// // @ts-expect-error ignore error
// await loadWasm(import(/* @vite-ignore */ 'shiki/onig.wasm'))
// }

shiki = createHighlighterCoreSync({
themes: [githubDark, githubLight],
langs: [bash],
engine: createJavaScriptRegexEngine(),
});

const renderer: RendererObject = {
link(originalLink) {
const link = marked.Renderer.prototype.link.call(this, originalLink);
return link.replace(
"<a",
"<a target='_blank' rel='noreferrer' class='text-primary underline'",
);
},
code({ text }) {
return `<code class="language-bash">${shiki.codeToHtml(text, {
theme: colorMode.preference === "dark" ? "github-dark" : "github-light",
lang: "bash",
})}</code>`;
},
};

marked.use({ renderer });
});

onBeforeUnmount(() => {
shiki?.dispose();
});

// Pagination
const fetching = ref(false);
const fetchMoreForceDisabled = ref(!commitsWithRelease.value.length);

Expand Down Expand Up @@ -218,7 +254,7 @@ async function fetchMore() {
</div>

<div
class="max-w-full p-4 border border-gray-100 dark:border-gray-800 rounded-lg prose dark:prose-invert"
class="max-w-full p-4 overflow-x-scroll border border-gray-100 dark:border-gray-800 rounded-lg prose dark:prose-invert flex flex-col gap-2"
v-html="marked(selectedCommit.release.text)"
/>
</div>
Expand Down
11 changes: 8 additions & 3 deletions packages/app/app/components/RepoSearch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,20 @@ const examples = [
name: "vite",
avatar: "https://avatars.githubusercontent.com/u/65625612?v=4",
},
{
owner: "rolldown",
name: "rolldown",
avatar: "https://avatars.githubusercontent.com/u/94954945?s=200&v=4",
},
{
owner: "vuejs",
name: "core",
avatar: "https://avatars.githubusercontent.com/u/6128107?v=4",
},
{
owner: "QwikDev",
name: "qwik",
avatar: "https://avatars.githubusercontent.com/u/138123704?v=4",
owner: "sveltejs",
name: "svelte",
avatar: "https://avatars.githubusercontent.com/u/23617963?s=200&v=4",
},
{
owner: "Tresjs",
Expand Down
14 changes: 2 additions & 12 deletions packages/app/app/layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,12 @@ const route = useRoute();
<ClientOnly>
<UButton
aria-label="Toggle theme"
:icon="
colorMode.preference === 'dark'
? 'ph-moon'
: colorMode.preference === 'light'
? 'ph-sun'
: 'ph-moon-stars'
"
:icon="colorMode.preference === 'light' ? 'ph-sun' : 'ph-moon-stars'"
color="neutral"
variant="link"
@click="
colorMode.preference =
colorMode.preference === 'dark'
? 'system'
: colorMode.preference === 'system'
? 'light'
: 'dark'
colorMode.preference === 'dark' ? 'light' : 'dark'
"
/>
<template #fallback>
Expand Down
2 changes: 1 addition & 1 deletion packages/app/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ beforeAll(async () => {
server = await app.listen(3300);

await ezSpawn.async(
"pnpm cross-env TEST=true pnpm --filter=app run build",
"pnpm cross-env TEST=true NITRO_GH_BASE_URL=http://localhost:3300 pnpm --filter=app run build",
[],
{
stdio: "inherit",
Expand Down
26 changes: 13 additions & 13 deletions packages/app/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import process from "node:process";
import ncb from "nitro-cloudflare-dev";
// import ncb from "nitro-cloudflare-dev";
import { resolve } from "pathe";

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
sourcemap: true,
compatibilityDate: "2024-07-30",

// https://nuxt.com/docs/getting-started/upgrade#testing-nuxt-4
future: { compatibilityVersion: 4 },

// https://nuxt.com/modules
modules: ["@nuxt/eslint", "@nuxt/ui", "@vueuse/nuxt"],
modules: ["@nuxt/eslint", "@nuxt/ui", "@vueuse/nuxt", "nitro-cloudflare-dev"],

css: ["~/assets/css/main.css"],

Expand All @@ -28,7 +28,6 @@ export default defineNuxtConfig({
preset: "cloudflare-pages",
sourceMap: "inline",
compatibilityDate: "2024-09-19",
modules: [ncb],
externals: {
inline: [
"@octokit",
Expand All @@ -47,14 +46,15 @@ export default defineNuxtConfig({
},

runtimeConfig: {
appId: process.env.NITRO_APP_ID || "",
webhookSecret: process.env.NITRO_WEBHOOK_SECRET || "",
privateKey: process.env.NITRO_PRIVATE_KEY || "",
rmStaleKey: process.env.NITRO_RM_STALE_KEY || "",
githubToken:
process.env.GITHUB_TOKEN || process.env.NITRO_GITHUB_TOKEN || "",
ghBaseUrl: process.env.NITRO_GH_BASE_URL || "https://api.github.com",
test: process.env.NITRO_TEST || "",
nitro: {
envPrefix: "NITRO_",
},
appId: "",
webhookSecret: "",
privateKey: "",
rmStaleKey: "",
ghBaseUrl: "https://api.github.com",
test: "",
},

hooks: {
Expand All @@ -68,7 +68,7 @@ export default defineNuxtConfig({
handler: clientRenderer,
});
nitro.options.handlers.unshift({
route: "/view/**",
route: "/~/**",
handler: clientRenderer,
});
},
Expand Down
3 changes: 3 additions & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"dependencies": {
"@iconify-json/ph": "^1.1.14",
"@nuxt/ui": "^3.0.0-alpha.10",
"@nuxtjs/mdc": "0.17.0",
"@octokit/app": "^15.1.1",
"@octokit/graphql": "^8.1.1",
"@octokit/plugin-paginate-rest": "^11.3.6",
Expand All @@ -26,8 +27,10 @@
"@vueuse/nuxt": "^12.2.0",
"marked": "^15.0.4",
"nuxt": "^3.15.0",
"nuxt-shiki": "0.3.0",
"octokit": "^4.0.2",
"query-registry": "^3.0.1",
"unstorage": "^1.16.0",
"vue": "^3.5.13",
"vue-router": "^4.4.3",
"zod": "^3.23.8"
Expand Down
22 changes: 14 additions & 8 deletions packages/app/server/api/repo/commits.get.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { z } from "zod";
import { useGithubREST } from "../../../server/utils/octokit";

const querySchema = z.object({
owner: z.string(),
Expand All @@ -14,12 +13,19 @@
const query = await getValidatedQuery(event, (data) =>
querySchema.parse(data),
);
const octokit = useGithubREST(event);
const installation = await useOctokitInstallation(
event,
query.owner,
query.repo,
);

const { data: repo } = await octokit.request("GET /repos/{owner}/{repo}", {
owner: query.owner,
repo: query.repo,
});
const { data: repo } = await installation.request(
"GET /repos/{owner}/{repo}",
{
owner: query.owner,
repo: query.repo,
},
);

const defaultBranch = repo.default_branch;

Expand All @@ -30,7 +36,7 @@
: 1;
const per_page = Number.parseInt(query.per_page);

const { data: commits } = await octokit.request(
const { data: commits } = await installation.request(
"GET /repos/{owner}/{repo}/commits",
{
owner: query.owner,
Expand All @@ -44,7 +50,7 @@
const commitsWithStatuses = await Promise.all(
commits.map(async (commit) => {
try {
const { data: checkRuns } = await octokit.request(
const { data: checkRuns } = await installation.request(
"GET /repos/{owner}/{repo}/commits/{ref}/check-runs",
{
owner: query.owner,
Expand Down Expand Up @@ -89,7 +95,7 @@
: null,
};
} catch (error) {
console.warn(

Check warning on line 98 in packages/app/server/api/repo/commits.get.ts

View workflow job for this annotation

GitHub Actions / Run Linting

Unexpected console statement
`Could not fetch check runs for commit ${commit.sha}:`,
error,
);
Expand Down Expand Up @@ -126,7 +132,7 @@
},
};
} catch (error) {
console.error("Error fetching repository commits:", error);

Check warning on line 135 in packages/app/server/api/repo/commits.get.ts

View workflow job for this annotation

GitHub Actions / Run Linting

Unexpected console statement

return {
id: "error",
Expand Down
7 changes: 3 additions & 4 deletions packages/app/server/api/repo/index.get.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import type { H3Event } from "h3";
import { z } from "zod";
import { useGithubREST } from "../../../server/utils/octokit";

const querySchema = z.object({
owner: z.string(),
repo: z.string(),
});

const getRepoInfo = defineCachedFunction(
async (owner: string, repo: string, event?: H3Event) => {
async (owner: string, repo: string, event: H3Event) => {
try {
const octokit = useGithubREST(event);
const installation = await useOctokitInstallation(event, owner, repo);

const { data } = await octokit.request("GET /repos/{owner}/{repo}", {
const { data } = await installation.request("GET /repos/{owner}/{repo}", {
owner,
repo,
});
Expand All @@ -30,7 +29,7 @@
description: data.description || "",
};
} catch (error) {
console.error(

Check warning on line 32 in packages/app/server/api/repo/index.get.ts

View workflow job for this annotation

GitHub Actions / Run Linting

Unexpected console statement
`Error fetching repository info for ${owner}/${repo}:`,
error,
);
Expand All @@ -38,7 +37,7 @@
}
},
{
getKey: (owner: string, repo: string, _event?: H3Event) =>

Check warning on line 40 in packages/app/server/api/repo/index.get.ts

View workflow job for this annotation

GitHub Actions / Run Linting

'_event' is defined but never used
`${owner}/${repo}`,
maxAge: 60 * 30, // 30 minutes
swr: true,
Expand All @@ -52,7 +51,7 @@
);
return getRepoInfo(query.owner, query.repo, event);
} catch (error) {
console.error("Error in repo info endpoint:", error);

Check warning on line 54 in packages/app/server/api/repo/index.get.ts

View workflow job for this annotation

GitHub Actions / Run Linting

Unexpected console statement
return {
error: true,
message: (error as Error).message,
Expand Down
79 changes: 57 additions & 22 deletions packages/app/server/api/repo/search.get.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import type { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
import { z } from "zod";
import { useGithubREST } from "../../../server/utils/octokit";

const querySchema = z.object({
text: z.string(),
});

export default defineEventHandler(async (event) => {
const r2Binding = useBinding(event);
const request = toWebRequest(event);
const signal = request.signal;

try {
const query = await getValidatedQuery(event, (data) =>
querySchema.parse(data),
Expand All @@ -16,32 +18,57 @@
return { nodes: [] };
}

const octokit = useGithubREST(event);
const searchText = query.text.toLowerCase();

// Internal pagination: iterate until uniqueNodes is filled or no more objects
let cursor: string | undefined;
const seen = new Set<string>();
const uniqueNodes = [];
const maxNodes = 10;
let keepGoing = true;

while (uniqueNodes.length < maxNodes && keepGoing && !signal.aborted) {
const listResult = await r2Binding.list({
prefix: usePackagesBucket.base,
limit: 1000,
cursor,
});
const { objects, truncated } = listResult;
cursor = truncated ? listResult.cursor : undefined;

for (const obj of objects) {
const parts = parseKey(obj.key);
const orgRepo = `${parts.org}/${parts.repo}`.toLowerCase();
const applies =
parts.org.toLowerCase().includes(searchText) ||
parts.repo.toLowerCase().includes(searchText) ||
orgRepo.includes(searchText);
if (!applies) continue;

const { data } = await octokit.request("GET /search/repositories", {
q: query.text,
per_page: 10,
});
const key = `${parts.org}/${parts.repo}`;
if (!seen.has(key)) {
seen.add(key);
uniqueNodes.push({
name: parts.repo,
owner: {
login: parts.org,
avatarUrl: `https://github.com/${parts.org}.png`,
},
});
if (uniqueNodes.length >= maxNodes) break;
}
}

if (!truncated || uniqueNodes.length >= maxNodes) {
keepGoing = false;
}
}

return {
nodes: data.items.map(
(
repo: RestEndpointMethodTypes["search"]["repos"]["response"]["data"]["items"][0],
) => ({
id: repo.id.toString(),
name: repo.name,
owner: repo.owner
? {
id: repo.owner.id.toString(),
login: repo.owner.login,
avatarUrl: repo.owner.avatar_url,
}
: null,
}),
),
nodes: uniqueNodes,
};
} catch (error) {
console.error("Error in repository search:", error);

Check warning on line 71 in packages/app/server/api/repo/search.get.ts

View workflow job for this annotation

GitHub Actions / Run Linting

Unexpected console statement
return {
nodes: [],
error: true,
Expand All @@ -49,3 +76,11 @@
};
}
});

function parseKey(key: string) {
const parts = key.split(":");
return {
org: parts[2],
repo: parts[3],
};
}
Loading
Loading