Adding basic file browsing support
Some checks failed
continuous-integration/drone/push Build is failing
5
.vscode/settings.json
vendored
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"deno.enable": true,
|
"deno.enable": true,
|
||||||
"deno.unstable": true,
|
"deno.unstable": true,
|
||||||
"debug.javascript.usePreview": false
|
"debug.javascript.usePreview": false,
|
||||||
|
"deno.import_intellisense_origins": {
|
||||||
|
"https://deno.land": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BIN
registry/favicon.png
Normal file
After Width: | Height: | Size: 44 KiB |
11
registry/favicon.svg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
registry/public/android-icon-192x192.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
registry/public/apple-icon-114x114.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
registry/public/apple-icon-120x120.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
registry/public/apple-icon-144x144.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
registry/public/apple-icon-152x152.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
registry/public/apple-icon-180x180.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
registry/public/apple-icon-57x57.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
registry/public/apple-icon-60x60.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
registry/public/apple-icon-72x72.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
registry/public/apple-icon-76x76.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
2
registry/public/browserconfig.xml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<browserconfig><msapplication><tile><square70x70logo src="/public/ms-icon-70x70.png"/><square150x150logo src="/public/ms-icon-150x150.png"/><square310x310logo src="/public/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
|
BIN
registry/public/favicon-16x16.png
Normal file
After Width: | Height: | Size: 930 B |
BIN
registry/public/favicon-256x256.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
registry/public/favicon-32x32.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
registry/public/favicon-96x96.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
registry/public/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
1
registry/public/file.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg>
|
After Width: | Height: | Size: 337 B |
1
registry/public/folder.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-folder"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
After Width: | Height: | Size: 311 B |
41
registry/public/manifest.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "Deno package Registry",
|
||||||
|
"short_name": "DenReg",
|
||||||
|
"description": "A deno package registry, that can be self hosted easily",
|
||||||
|
"start_url": "/",
|
||||||
|
"scope": "/",
|
||||||
|
"theme_color": "#ffffff",
|
||||||
|
"display": "standalone",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/public/favicon-32x32.png",
|
||||||
|
"sizes": "32x32",
|
||||||
|
"type": "image/png",
|
||||||
|
"density": "0.75"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/public/apple-icon-72x72.png",
|
||||||
|
"sizes": "72x72",
|
||||||
|
"type": "image/png",
|
||||||
|
"density": "1.5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/public/favicon-96x96.png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
"type": "image/png",
|
||||||
|
"density": "2.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/public/apple-icon-144x144.png",
|
||||||
|
"sizes": "144x144",
|
||||||
|
"type": "image/png",
|
||||||
|
"density": "3.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/public/android-icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"density": "4.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
registry/public/ms-icon-144x144.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
registry/public/ms-icon-150x150.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
registry/public/ms-icon-310x310.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
registry/public/ms-icon-70x70.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
2
registry/public/paper.min.css
vendored
Normal file
6
registry/scripts/getPaperCSS.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
await fetch("https://unpkg.com/papercss/dist/paper.min.css")
|
||||||
|
.then((res) => res.arrayBuffer())
|
||||||
|
.then((data) =>
|
||||||
|
Deno.writeFile("./public/paper.min.css", new Uint8Array(data))
|
||||||
|
)
|
||||||
|
.catch(console.error);
|
@ -1,29 +1,33 @@
|
|||||||
// export { MongoClient, ObjectId } from "https://deno.land/x/mongo@v0.9.1/mod.ts";
|
export * as S3 from "https://deno.land/x/s3@0.2.0/mod.ts";
|
||||||
|
|
||||||
export * as S3 from "https://deno.land/x/s3/mod.ts";
|
export * as Ini from "https://deno.hibas123.de/raw/ini@0.0.1/mod.ts";
|
||||||
|
|
||||||
export * as Ini from "https://deno.land/x/ini/mod.ts";
|
|
||||||
|
|
||||||
export * as ABC from "https://deno.land/x/abc@v1/mod.ts";
|
export * as ABC from "https://deno.land/x/abc@v1/mod.ts";
|
||||||
export * as CorsMW from "https://deno.land/x/abc@v1/middleware/cors.ts";
|
export * as CorsMW from "https://deno.land/x/abc@v1/middleware/cors.ts";
|
||||||
export * as LoggerMW from "https://deno.land/x/abc@v1/middleware/logger.ts";
|
export * as LoggerMW from "https://deno.land/x/abc@v1/middleware/logger.ts";
|
||||||
|
|
||||||
export * as Path from "https://deno.land/std@0.63.0/path/mod.ts";
|
export * as Path from "https://deno.land/std@0.69.0/path/mod.ts";
|
||||||
export * as FS from "https://deno.land/std@0.63.0/fs/mod.ts";
|
export * as FS from "https://deno.land/std@0.69.0/fs/mod.ts";
|
||||||
export * as Base64 from "https://deno.land/std@0.63.0/encoding/base64.ts";
|
export * as Base64 from "https://deno.land/std@0.69.0/encoding/base64.ts";
|
||||||
export * as Hash from "https://deno.land/std@0.63.0/hash/mod.ts";
|
export * as Hash from "https://deno.land/std@0.69.0/hash/mod.ts";
|
||||||
|
export * as Colors from "https://deno.land/std@0.69.0/fmt/colors.ts";
|
||||||
|
|
||||||
export * as Compress from "https://git.stamm.me/Deno/DenReg/raw/branch/master/tar/mod.ts";
|
export * as Compress from "https://git.stamm.me/Deno/DenReg/raw/branch/master/tar/mod.ts";
|
||||||
|
|
||||||
export { Marked } from "https://deno.land/x/markdown/mod.ts";
|
export { default as Prism } from "https://cdn.skypack.dev/prismjs";
|
||||||
|
|
||||||
|
// export { Marked } from "https://deno.land/x/markdown/mod.ts";
|
||||||
|
|
||||||
|
export { Marked } from "../../markdown/mod.ts";
|
||||||
|
|
||||||
import DS from "https://raw.githubusercontent.com/hibas123/dndb/master/mod.ts";
|
import DS from "https://raw.githubusercontent.com/hibas123/dndb/master/mod.ts";
|
||||||
|
|
||||||
/// <reference path="./types/jsx.d.ts" />
|
/// <reference path="./types/jsx.d.ts" />
|
||||||
export {
|
export { React, jsx, Fragment } from "../../jsx-html/mod.ts";
|
||||||
React,
|
// export {
|
||||||
jsx,
|
// React,
|
||||||
Fragment,
|
// jsx,
|
||||||
} from "https://raw.githubusercontent.com/apiel/jsx-html/master/mod.ts";
|
// Fragment,
|
||||||
|
// } from "https://raw.githubusercontent.com/apiel/jsx-html/master/mod.ts";
|
||||||
|
|
||||||
export const Datastore = DS;
|
export const Datastore = DS;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/// <reference path="./types/jsx.d.ts" />
|
/// <reference path="./types/jsx.d.ts" />
|
||||||
import { ABC, CorsMW, LoggerMW } from "./deps.ts";
|
import { ABC, CorsMW, LoggerMW, Path } from "./deps.ts";
|
||||||
import config from "./config.ts";
|
import config from "./config.ts";
|
||||||
|
|
||||||
const port = config?.api?.port || 8000;
|
const port = config?.api?.port || 8000;
|
||||||
@ -8,6 +8,30 @@ const app = new ABC.Application();
|
|||||||
app.use(LoggerMW.logger({}));
|
app.use(LoggerMW.logger({}));
|
||||||
app.use(CorsMW.cors({}));
|
app.use(CorsMW.cors({}));
|
||||||
|
|
||||||
|
// app.static("/public", "./public");
|
||||||
|
|
||||||
|
const MIMEDB = {
|
||||||
|
".css": "text/css",
|
||||||
|
".json": "application/json",
|
||||||
|
".js": "application/javascript",
|
||||||
|
".gz": "applcation/gzip",
|
||||||
|
".ico": "image/x-icon",
|
||||||
|
".png": "image/png",
|
||||||
|
".svg": "image/svg+xml",
|
||||||
|
".xml": "application/xml",
|
||||||
|
};
|
||||||
|
|
||||||
|
app.get("/public/*path", async (ctx) => {
|
||||||
|
const filePath = Path.join("./public", ctx.params.path);
|
||||||
|
|
||||||
|
console.log(filePath);
|
||||||
|
|
||||||
|
ctx.blob(
|
||||||
|
await Deno.open(filePath, { read: true }),
|
||||||
|
(MIMEDB as any)[Path.extname(filePath)]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
import api from "./http/api.ts";
|
import api from "./http/api.ts";
|
||||||
api(app.group("/api"));
|
api(app.group("/api"));
|
||||||
|
|
||||||
@ -17,7 +41,15 @@ raw(app.group("/raw"));
|
|||||||
import view from "./http/views.ts";
|
import view from "./http/views.ts";
|
||||||
view(app);
|
view(app);
|
||||||
|
|
||||||
console.log(app.router.trees.GET);
|
// function logNode(router: Node, indent = 0) {
|
||||||
|
// trees.map((tree) => {
|
||||||
|
// console.log("Path:", tree.path);
|
||||||
|
// tree.children?.forEach((node) => logNode(node, indent + 3));
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const trees = Object.values(app.router) as Node[];
|
||||||
|
// trees.forEach(logNode);
|
||||||
|
|
||||||
import render from "./renderer.tsx";
|
import render from "./renderer.tsx";
|
||||||
app.renderer = {
|
app.renderer = {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ABC, Path, Compress, FS } from "../deps.ts";
|
import { ABC, Path, Compress, FS, Colors } from "../deps.ts";
|
||||||
|
|
||||||
import bucket from "../s3.ts";
|
import bucket from "../s3.ts";
|
||||||
import { isValidPackageName, basicauth, isValidFullVersion } from "../utils.ts";
|
import { isValidPackageName, basicauth, isValidFullVersion } from "../utils.ts";
|
||||||
@ -138,16 +138,14 @@ async function uploadPackage(ctx: ABC.Context) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
for await (const file of walker) {
|
for await (const file of walker) {
|
||||||
const relative = Path.relative(folder, file.path);
|
const relative = Path.relative(folder, file.path).replace("\\", "/");
|
||||||
|
|
||||||
|
console.log("Normalised path:", relative.replace("\\", "/"));
|
||||||
|
|
||||||
const bucketPath = (bucketBase + relative).replace(/@/g, "§");
|
const bucketPath = (bucketBase + relative).replace(/@/g, "§");
|
||||||
|
|
||||||
console.log("Uploading file:", file.path, bucketPath, bucketBase);
|
const body = await Deno.readAll(await Deno.open(file.path));
|
||||||
await bucket.putObject(
|
await bucket.putObject(bucketPath, body, {});
|
||||||
bucketPath,
|
|
||||||
await Deno.readAll(await Deno.open(file.path)),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
console.log("Setting new live version");
|
console.log("Setting new live version");
|
||||||
|
|
||||||
@ -168,7 +166,9 @@ async function uploadPackage(ctx: ABC.Context) {
|
|||||||
success: true,
|
success: true,
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error while processing newly uploaded package");
|
console.error(
|
||||||
|
Colors.red("Error while processing newly uploaded package")
|
||||||
|
);
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { ABC } from "../deps.ts";
|
import { Path } from "../deps.ts";
|
||||||
import { extractPackagePath, getFile } from "../utils.ts";
|
import type { ABC } from "../deps.ts";
|
||||||
|
import {
|
||||||
|
extractPackagePath,
|
||||||
|
getAbsolutePackageVersion,
|
||||||
|
getFile,
|
||||||
|
} from "../utils.ts";
|
||||||
|
import db from "../db.ts";
|
||||||
|
|
||||||
const MAX_FIXED_CACHE_AGE = 60 * 60 * 24 * 365;
|
const MAX_FIXED_CACHE_AGE = 60 * 60 * 24 * 365;
|
||||||
const MAX_FLOATING_CACHE_AGE = 60 * 30;
|
const MAX_FLOATING_CACHE_AGE = 60 * 30;
|
||||||
@ -11,16 +17,27 @@ export default function raw(g: ABC.Group) {
|
|||||||
ctx.params.package
|
ctx.params.package
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const pkg = await db.package.findOne({ name: packageName });
|
||||||
|
packageVersion = getAbsolutePackageVersion(pkg, packageVersion);
|
||||||
|
|
||||||
const E404 = () => {
|
const E404 = () => {
|
||||||
ctx.response.status = 404;
|
ctx.response.status = 404;
|
||||||
ctx.response.body = "// Not found!";
|
ctx.response.body = "// Not found!";
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await getFile(
|
const filepath = ctx.params.path;
|
||||||
packageName,
|
const result = await getFile(packageName, packageVersion, filepath);
|
||||||
packageVersion,
|
|
||||||
ctx.params.path
|
if (filepath.endsWith(".js")) {
|
||||||
);
|
const tsFile = filepath.substr(0, filepath.length - 3) + ".d.ts";
|
||||||
|
const tsResult = await getFile(packageName, packageVersion, tsFile);
|
||||||
|
if (tsResult) {
|
||||||
|
ctx.response.headers.set(
|
||||||
|
"X-TypeScript-Types",
|
||||||
|
"./" + Path.posix.basename(tsFile)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (packageVersion && result) {
|
if (packageVersion && result) {
|
||||||
ctx.response.headers.set(
|
ctx.response.headers.set(
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
import { ABC } from "../deps.ts";
|
import type { ABC } from "../deps.ts";
|
||||||
import { basicauth, extractPackagePath, sortVersions } from "../utils.ts";
|
import {
|
||||||
|
basicauth,
|
||||||
|
extractPackagePath,
|
||||||
|
getBucketFilePath,
|
||||||
|
getFile,
|
||||||
|
getAbsolutePackageVersion,
|
||||||
|
sortVersions,
|
||||||
|
} from "../utils.ts";
|
||||||
|
|
||||||
import { Hash } from "../deps.ts";
|
import { Hash, Path } from "../deps.ts";
|
||||||
import db, { IPackage } from "../db.ts";
|
import db, { IPackage } from "../db.ts";
|
||||||
|
import bucket from "../s3.ts";
|
||||||
|
|
||||||
const MAX_CACHE_AGE = 60 * 30; // 30 Minutes
|
const MAX_CACHE_AGE = 60 * 30; // 30 Minutes
|
||||||
|
|
||||||
@ -44,6 +52,7 @@ export default function views(g: ABC.Application) {
|
|||||||
ctx.response.headers.set("cache-control", CACHE_CONTROL);
|
ctx.response.headers.set("cache-control", CACHE_CONTROL);
|
||||||
ctx.response.headers.set("E-Tag", etag);
|
ctx.response.headers.set("E-Tag", etag);
|
||||||
});
|
});
|
||||||
|
|
||||||
g.get("/package/:package", async (ctx) => {
|
g.get("/package/:package", async (ctx) => {
|
||||||
let [packageName, packageVersion] = extractPackagePath(
|
let [packageName, packageVersion] = extractPackagePath(
|
||||||
ctx.params.package
|
ctx.params.package
|
||||||
@ -61,4 +70,84 @@ export default function views(g: ABC.Application) {
|
|||||||
ctx.response.headers.set("cache-control", CACHE_CONTROL);
|
ctx.response.headers.set("cache-control", CACHE_CONTROL);
|
||||||
ctx.response.headers.set("E-Tag", etag);
|
ctx.response.headers.set("E-Tag", etag);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function handleBrowse(ctx: ABC.Context) {
|
||||||
|
const E404 = () => {
|
||||||
|
ctx.response.status = 404;
|
||||||
|
ctx.response.body = "// Not found!";
|
||||||
|
};
|
||||||
|
|
||||||
|
let [packageName, packageVersion] = extractPackagePath(
|
||||||
|
ctx.params.package
|
||||||
|
);
|
||||||
|
|
||||||
|
const pkg = await db.package.findOne({ name: packageName });
|
||||||
|
packageVersion = getAbsolutePackageVersion(pkg, packageVersion);
|
||||||
|
|
||||||
|
if (!packageVersion) return E404();
|
||||||
|
|
||||||
|
const path = ctx.params.path || "";
|
||||||
|
|
||||||
|
const fileContent = await getFile(packageName, packageVersion, path);
|
||||||
|
if (fileContent) {
|
||||||
|
await ctx.render("browse_file", {
|
||||||
|
pkg,
|
||||||
|
version: packageVersion,
|
||||||
|
content: new TextDecoder().decode(fileContent.data),
|
||||||
|
ext: Path.extname(path),
|
||||||
|
path: `${packageName}@${packageVersion}/${path}`,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const bucketPath = await getBucketFilePath(
|
||||||
|
packageName,
|
||||||
|
packageVersion,
|
||||||
|
path
|
||||||
|
);
|
||||||
|
if (!bucketPath) return E404();
|
||||||
|
|
||||||
|
console.log(bucketPath);
|
||||||
|
|
||||||
|
const filesItr = bucket.listAllObjects({
|
||||||
|
batchSize: 100,
|
||||||
|
prefix: bucketPath,
|
||||||
|
// delimiter: "/",
|
||||||
|
});
|
||||||
|
|
||||||
|
let files: { name: string; size: number }[] = [];
|
||||||
|
let directories: Set<string> = new Set();
|
||||||
|
let readme: string | null = null;
|
||||||
|
for await (let file of filesItr) {
|
||||||
|
const relPath = Path.posix.relative(bucketPath, file.key || "");
|
||||||
|
if (relPath.indexOf("/") >= 0) {
|
||||||
|
directories.add(relPath.split("/")[0]);
|
||||||
|
} else {
|
||||||
|
files.push({ name: relPath, size: file.size || -1 });
|
||||||
|
if (relPath.toLowerCase() === "readme.md") {
|
||||||
|
let readmeCont = await getFile(
|
||||||
|
packageName,
|
||||||
|
packageVersion,
|
||||||
|
Path.posix.join(path, relPath)
|
||||||
|
);
|
||||||
|
if (readmeCont) {
|
||||||
|
readme = new TextDecoder().decode(readmeCont?.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.render("browse_folder", {
|
||||||
|
pkg,
|
||||||
|
version: packageVersion,
|
||||||
|
readme,
|
||||||
|
files,
|
||||||
|
directories: Array.from(directories.values()).map((e) => ({
|
||||||
|
name: e,
|
||||||
|
})),
|
||||||
|
path: `${packageName}@${packageVersion}/${path}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g.get("/browse/:package", handleBrowse);
|
||||||
|
g.get("/browse/:package/*path", handleBrowse);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,18 @@ if (!config.s3) {
|
|||||||
throw new Error("Config is missing [s3] section!");
|
throw new Error("Config is missing [s3] section!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!config.s3.endpoint) {
|
||||||
|
throw new Error("Config is missing s3.endpoint!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.s3.accessKey) {
|
||||||
|
throw new Error("Config is missing s3.accessKey!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.s3.secretKey) {
|
||||||
|
throw new Error("Config is missing s3.secretKey!");
|
||||||
|
}
|
||||||
|
|
||||||
const bucket = new S3.S3Bucket({
|
const bucket = new S3.S3Bucket({
|
||||||
bucket: config.s3.bucket || "deno-registry",
|
bucket: config.s3.bucket || "deno-registry",
|
||||||
endpointURL: config.s3.endpoint,
|
endpointURL: config.s3.endpoint,
|
||||||
|
3
registry/src/test.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import * as Prism from "./vendor/prism/prism.js";
|
||||||
|
|
||||||
|
console.log((window as any).Prism);
|
8
registry/src/types/jsx.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
declare namespace JSX {
|
declare namespace JSX {
|
||||||
interface IntrinsicElements {
|
interface IntrinsicElements {
|
||||||
[elemName: string]: any;
|
[elemName: string]: any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,30 +87,38 @@ export function extractPackagePath(path: string): [string, string | undefined] {
|
|||||||
return [packageName, packageVersion];
|
return [packageName, packageVersion];
|
||||||
}
|
}
|
||||||
|
|
||||||
import db from "./db.ts";
|
import type { IPackage } from "./db.ts";
|
||||||
|
|
||||||
import bucket from "./s3.ts";
|
import bucket from "./s3.ts";
|
||||||
|
|
||||||
export async function getFile(
|
export function getAbsolutePackageVersion(
|
||||||
pkgName: string,
|
pkg?: IPackage | null,
|
||||||
version: string | null | undefined,
|
version?: string
|
||||||
file: string
|
) {
|
||||||
): Promise<{ etag: string; data: Uint8Array } | null | undefined> {
|
if (!pkg || pkg.versions.length < 1) return undefined;
|
||||||
console.log("Searching for file: %s/%s@%s", pkgName, file, version);
|
|
||||||
const meta = await db.package.findOne({ name: pkgName });
|
|
||||||
|
|
||||||
if (!meta || meta.versions.length < 1) return null;
|
const versions = pkg.versions.sort(sortVersions).reverse();
|
||||||
|
|
||||||
const versions = meta.versions.sort(sortVersions).reverse();
|
|
||||||
|
|
||||||
if (!version) {
|
if (!version) {
|
||||||
version = versions[0];
|
version = versions[0];
|
||||||
} else {
|
} else {
|
||||||
const v = versions.filter((e) => e.startsWith(version as string));
|
const v = versions.filter((e) => e.startsWith(version as string));
|
||||||
if (v.length < 1) return null;
|
if (v.length < 1) return undefined;
|
||||||
version = v[0];
|
version = v[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getBucketFilePath(
|
||||||
|
pkgName: string,
|
||||||
|
version: string,
|
||||||
|
file: string
|
||||||
|
) {
|
||||||
|
if (file.startsWith("/")) {
|
||||||
|
file = file.substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
const bucketPath = (
|
const bucketPath = (
|
||||||
"packages/" +
|
"packages/" +
|
||||||
pkgName +
|
pkgName +
|
||||||
@ -120,11 +128,24 @@ export async function getFile(
|
|||||||
file
|
file
|
||||||
).replace(/@/g, "§");
|
).replace(/@/g, "§");
|
||||||
|
|
||||||
|
return bucketPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getFile(
|
||||||
|
pkgName: string,
|
||||||
|
version: string | undefined,
|
||||||
|
file: string
|
||||||
|
): Promise<{ etag: string; data: Uint8Array } | null | undefined> {
|
||||||
|
if (!version) return undefined;
|
||||||
|
const bucketPath = await getBucketFilePath(pkgName, version, file);
|
||||||
|
if (!bucketPath) return null;
|
||||||
|
|
||||||
console.log("Getting file from:", bucketPath);
|
console.log("Getting file from:", bucketPath);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await bucket.getObject(bucketPath);
|
const res = await bucket.getObject(bucketPath);
|
||||||
if (!res) return undefined;
|
if (!res || res.body.byteLength === 0) return undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
etag: res.etag,
|
etag: res.etag,
|
||||||
data: res.body,
|
data: res.body,
|
||||||
|
7
registry/src/vendor/prism.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import "./prism/prism.js";
|
||||||
|
|
||||||
|
const Prism = (window as any).Prism;
|
||||||
|
|
||||||
|
delete (window as any).Prism;
|
||||||
|
|
||||||
|
export default Prism;
|
218
registry/src/vendor/prism/prism.css
vendored
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
/* PrismJS 1.22.0
|
||||||
|
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+agda+al+antlr4+apacheconf+apl+applescript+aql+arduino+arff+asciidoc+aspnet+asm6502+autohotkey+autoit+bash+basic+batch+bbcode+birb+bison+bnf+brainfuck+brightscript+bro+bsl+c+csharp+cpp+cil+clojure+cmake+coffeescript+concurnas+csp+crystal+css-extras+cypher+d+dart+dax+dhall+diff+django+dns-zone-file+docker+ebnf+editorconfig+eiffel+ejs+elixir+elm+etlua+erb+erlang+excel-formula+fsharp+factor+firestore-security-rules+flow+fortran+ftl+gml+gcode+gdscript+gedcom+gherkin+git+glsl+go+graphql+groovy+haml+handlebars+haskell+haxe+hcl+hlsl+http+hpkp+hsts+ichigojam+icon+ignore+inform7+ini+io+j+java+javadoc+javadoclike+javastacktrace+jolie+jq+jsdoc+js-extras+json+json5+jsonp+jsstacktrace+js-templates+julia+keyman+kotlin+latex+latte+less+lilypond+liquid+lisp+livescript+llvm+lolcode+lua+makefile+markdown+markup-templating+matlab+mel+mizar+mongodb+monkey+moonscript+n1ql+n4js+nand2tetris-hdl+naniscript+nasm+neon+nginx+nim+nix+nsis+objectivec+ocaml+opencl+oz+parigp+parser+pascal+pascaligo+pcaxis+peoplecode+perl+php+phpdoc+php-extras+plsql+powerquery+powershell+processing+prolog+properties+protobuf+pug+puppet+pure+purebasic+purescript+python+q+qml+qore+r+racket+jsx+tsx+reason+regex+renpy+rest+rip+roboconf+robotframework+ruby+rust+sas+sass+scss+scala+scheme+shell-session+smali+smalltalk+smarty+sml+solidity+solution-file+soy+sparql+splunk-spl+sqf+sql+stan+iecst+stylus+swift+t4-templating+t4-cs+t4-vb+tap+tcl+tt2+textile+toml+turtle+twig+typescript+typoscript+unrealscript+vala+vbnet+velocity+verilog+vhdl+vim+visual-basic+warpscript+wasm+wiki+xeora+xml-doc+xojo+xquery+yaml+yang+zig&plugins=line-numbers+inline-color */
|
||||||
|
/**
|
||||||
|
* prism.js default theme for JavaScript, CSS and HTML
|
||||||
|
* Based on dabblet (http://dabblet.com)
|
||||||
|
* @author Lea Verou
|
||||||
|
*/
|
||||||
|
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
color: black;
|
||||||
|
background: none;
|
||||||
|
text-shadow: 0 1px white;
|
||||||
|
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||||
|
font-size: 1em;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
||||||
|
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
||||||
|
text-shadow: none;
|
||||||
|
background: #b3d4fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
||||||
|
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
||||||
|
text-shadow: none;
|
||||||
|
background: #b3d4fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code blocks */
|
||||||
|
pre[class*="language-"] {
|
||||||
|
padding: 1em;
|
||||||
|
margin: .5em 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(pre) > code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
background: #f5f2f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline code */
|
||||||
|
:not(pre) > code[class*="language-"] {
|
||||||
|
padding: .1em;
|
||||||
|
border-radius: .3em;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment,
|
||||||
|
.token.prolog,
|
||||||
|
.token.doctype,
|
||||||
|
.token.cdata {
|
||||||
|
color: slategray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.punctuation {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.namespace {
|
||||||
|
opacity: .7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.property,
|
||||||
|
.token.tag,
|
||||||
|
.token.boolean,
|
||||||
|
.token.number,
|
||||||
|
.token.constant,
|
||||||
|
.token.symbol,
|
||||||
|
.token.deleted {
|
||||||
|
color: #905;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.selector,
|
||||||
|
.token.attr-name,
|
||||||
|
.token.string,
|
||||||
|
.token.char,
|
||||||
|
.token.builtin,
|
||||||
|
.token.inserted {
|
||||||
|
color: #690;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.operator,
|
||||||
|
.token.entity,
|
||||||
|
.token.url,
|
||||||
|
.language-css .token.string,
|
||||||
|
.style .token.string {
|
||||||
|
color: #9a6e3a;
|
||||||
|
/* This background color was intended by the author of this theme. */
|
||||||
|
background: hsla(0, 0%, 100%, .5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.atrule,
|
||||||
|
.token.attr-value,
|
||||||
|
.token.keyword {
|
||||||
|
color: #07a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.function,
|
||||||
|
.token.class-name {
|
||||||
|
color: #DD4A68;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.regex,
|
||||||
|
.token.important,
|
||||||
|
.token.variable {
|
||||||
|
color: #e90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important,
|
||||||
|
.token.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.entity {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"].line-numbers {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 3.8em;
|
||||||
|
counter-reset: linenumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"].line-numbers > code {
|
||||||
|
position: relative;
|
||||||
|
white-space: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-numbers .line-numbers-rows {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
top: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
left: -3.8em;
|
||||||
|
width: 3em; /* works for line-numbers below 1000 lines */
|
||||||
|
letter-spacing: -1px;
|
||||||
|
border-right: 1px solid #999;
|
||||||
|
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-numbers-rows > span {
|
||||||
|
display: block;
|
||||||
|
counter-increment: linenumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-numbers-rows > span:before {
|
||||||
|
content: counter(linenumber);
|
||||||
|
color: #999;
|
||||||
|
display: block;
|
||||||
|
padding-right: 0.8em;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.inline-color-wrapper {
|
||||||
|
/*
|
||||||
|
* The background image is the following SVG inline in base 64:
|
||||||
|
*
|
||||||
|
* <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2">
|
||||||
|
* <path fill="gray" d="M0 0h2v2H0z"/>
|
||||||
|
* <path fill="white" d="M0 0h1v1H0zM1 1h1v1H1z"/>
|
||||||
|
* </svg>
|
||||||
|
*
|
||||||
|
* SVG-inlining explained:
|
||||||
|
* https://stackoverflow.com/a/21626701/7595472
|
||||||
|
*/
|
||||||
|
background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyIDIiPjxwYXRoIGZpbGw9ImdyYXkiIGQ9Ik0wIDBoMnYySDB6Ii8+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0wIDBoMXYxSDB6TTEgMWgxdjFIMXoiLz48L3N2Zz4=");
|
||||||
|
/* This is to prevent visual glitches where one pixel from the repeating pattern could be seen. */
|
||||||
|
background-position: center;
|
||||||
|
background-size: 110%;
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
height: 1.333ch;
|
||||||
|
width: 1.333ch;
|
||||||
|
margin: 0 .333ch;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid white;
|
||||||
|
outline: 1px solid rgba(0,0,0,.5);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.inline-color {
|
||||||
|
display: block;
|
||||||
|
/* To prevent visual glitches again */
|
||||||
|
height: 120%;
|
||||||
|
width: 120%;
|
||||||
|
}
|
||||||
|
|
239
registry/src/vendor/prism/prism.js
vendored
Normal file
@ -5,6 +5,7 @@ const styles = new TextDecoder().decode(
|
|||||||
Deno.readFileSync("src/views/styles.css")
|
Deno.readFileSync("src/views/styles.css")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// href="https://unpkg.com/papercss@1.6.1/dist/paper.min.css"
|
||||||
export default function Base(p: any, children: any[]) {
|
export default function Base(p: any, children: any[]) {
|
||||||
const title = p.title || "DenReg";
|
const title = p.title || "DenReg";
|
||||||
return (
|
return (
|
||||||
@ -14,9 +15,86 @@ export default function Base(p: any, children: any[]) {
|
|||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://deno.hibas123.de/raw/@hibas123-theme@2.0.2/out/base.css"
|
href="https://deno.hibas123.de/raw/@hibas123-theme@2.0.2/out/base.css"
|
||||||
/> */}
|
/> */}
|
||||||
|
<link rel="stylesheet" href="/public/paper.min.css" />
|
||||||
<link
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="57x57"
|
||||||
|
href="/public/apple-icon-57x57.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="60x60"
|
||||||
|
href="/public/apple-icon-60x60.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="72x72"
|
||||||
|
href="/public/apple-icon-72x72.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="76x76"
|
||||||
|
href="/public/apple-icon-76x76.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="114x114"
|
||||||
|
href="/public/apple-icon-114x114.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="120x120"
|
||||||
|
href="/public/apple-icon-120x120.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="144x144"
|
||||||
|
href="/public/apple-icon-144x144.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="152x152"
|
||||||
|
href="/public/apple-icon-152x152.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="/public/apple-icon-180x180.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="192x192"
|
||||||
|
href="/public/android-icon-192x192.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="/public/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="96x96"
|
||||||
|
href="/public/favicon-96x96.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="/public/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="/public/manifest.json" />
|
||||||
|
<meta name="msapplication-TileColor" content="#ffffff" />
|
||||||
|
<meta
|
||||||
|
name="msapplication-TileImage"
|
||||||
|
content="/public/ms-icon-144x144.png"
|
||||||
|
/>
|
||||||
|
<meta name="theme-color" content="#ffffff"></meta>
|
||||||
|
<link
|
||||||
|
href="https://unpkg.com/prismjs/themes/prism.css"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://unpkg.com/papercss@1.6.1/dist/paper.min.css"
|
|
||||||
/>
|
/>
|
||||||
<style innerHTML={styles}></style>
|
<style innerHTML={styles}></style>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
|
113
registry/src/views/_browse.tsx
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/// <reference path="../types/jsx.d.ts" />
|
||||||
|
import { React, Marked } from "../deps.ts";
|
||||||
|
import type { IPackage } from "../db.ts";
|
||||||
|
import { sortVersions } from "../utils.ts";
|
||||||
|
|
||||||
|
import Prism from "../vendor/prism.ts";
|
||||||
|
|
||||||
|
interface IEntryParams {
|
||||||
|
name: string;
|
||||||
|
size?: number;
|
||||||
|
directory?: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Entry({ name, size, directory }: IEntryParams) {
|
||||||
|
return (
|
||||||
|
<a class="browse-list-item" href={"./" + name + (directory ? "/" : "")}>
|
||||||
|
<img src={directory ? "/public/folder.svg" : "/public/file.svg"} />
|
||||||
|
<span>{name}</span>
|
||||||
|
{size && <span class="browse-list-item-size">{size}</span>}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IEntryListParams {
|
||||||
|
files: { name: string; size: number }[];
|
||||||
|
directories: { name: string }[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EntryList({ directories, files }: IEntryListParams) {
|
||||||
|
return (
|
||||||
|
<div class="card" style="padding: 1rem;">
|
||||||
|
<div>
|
||||||
|
{directories.map((e) => (
|
||||||
|
<Entry name={e.name} directory />
|
||||||
|
))}
|
||||||
|
{files.map((e) => (
|
||||||
|
<Entry name={e.name} size={e.size} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRenderFileInterface {
|
||||||
|
content: string;
|
||||||
|
ext: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const languages: { [key: string]: string } = {
|
||||||
|
js: "javascript",
|
||||||
|
cjs: "javascript",
|
||||||
|
mjs: "javascript",
|
||||||
|
ts: "typescript",
|
||||||
|
c: "clike",
|
||||||
|
svelte: "html",
|
||||||
|
cs: "csharp",
|
||||||
|
hb: "handlebars",
|
||||||
|
ps: "powershell",
|
||||||
|
sh: "bash",
|
||||||
|
bat: "batch",
|
||||||
|
yml: "yaml",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function RenderFile({ content, ext }: IRenderFileInterface) {
|
||||||
|
if (ext === ".md") {
|
||||||
|
content = Marked.parse(content).content;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class="card browse-code-block"
|
||||||
|
style="margin-top: 1rem; padding: 1rem;"
|
||||||
|
innerHTML={content}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let lang = languages[ext.replace(".", "")] || ext.replace(".", "");
|
||||||
|
|
||||||
|
if (Prism.languages[lang]) {
|
||||||
|
content = Prism.highlight(content, Prism.languages[lang], lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <pre innerHTML={content} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IBrowseHeaderParams {
|
||||||
|
pkg: IPackage;
|
||||||
|
version?: string;
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BrowseHeader({ pkg, version, path }: IBrowseHeaderParams) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style="display: flex;">
|
||||||
|
<div>
|
||||||
|
<h2 style="margin-bottom: 0">Browse: {pkg.name}</h2>
|
||||||
|
<h4 class="text-muted" style="margin-top: 0; margin-left: .5rem">
|
||||||
|
By {pkg.owner}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Version:
|
||||||
|
<select>
|
||||||
|
{pkg.versions.sort(sortVersions).map((v) => (
|
||||||
|
<option selected={version === v}>{v}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="browse-path">{path}</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
20
registry/src/views/_pkgheader.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/// <reference path="../types/jsx.d.ts" />
|
||||||
|
import { React, Fragment, Marked } from "../deps.ts";
|
||||||
|
import type { IPackage } from "../db.ts";
|
||||||
|
|
||||||
|
export default async function index({
|
||||||
|
pkg,
|
||||||
|
version,
|
||||||
|
}: {
|
||||||
|
pkg: IPackage;
|
||||||
|
version?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h2 style="margin-bottom: 0">Package: {pkg.name}</h2>
|
||||||
|
<h4 class="text-muted" style="margin-top: 0; margin-left: .5rem">
|
||||||
|
By {pkg.owner}
|
||||||
|
</h4>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
38
registry/src/views/browse_file.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/// <reference path="../types/jsx.d.ts" />
|
||||||
|
import { React } from "../deps.ts";
|
||||||
|
import Base from "./_base.tsx";
|
||||||
|
import type { IPackage } from "../db.ts";
|
||||||
|
|
||||||
|
import { Main, Menu } from "./_default.tsx";
|
||||||
|
import { RenderFile, EntryList, BrowseHeader } from "./_browse.tsx";
|
||||||
|
|
||||||
|
export default async function index({
|
||||||
|
pkg,
|
||||||
|
version,
|
||||||
|
content,
|
||||||
|
ext,
|
||||||
|
path,
|
||||||
|
}: {
|
||||||
|
pkg: IPackage;
|
||||||
|
version?: string;
|
||||||
|
content: string;
|
||||||
|
ext: string;
|
||||||
|
path: string;
|
||||||
|
}) {
|
||||||
|
if (!pkg)
|
||||||
|
return (
|
||||||
|
<Base>
|
||||||
|
<h1>Not found</h1>
|
||||||
|
</Base>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Base title={"DenReg - " + pkg.name}>
|
||||||
|
<Main>
|
||||||
|
<BrowseHeader pkg={pkg} version={version} path={path} />
|
||||||
|
<RenderFile content={content} ext={ext} />
|
||||||
|
</Main>
|
||||||
|
<Menu></Menu>
|
||||||
|
</Base>
|
||||||
|
);
|
||||||
|
}
|
43
registry/src/views/browse_folder.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/// <reference path="../types/jsx.d.ts" />
|
||||||
|
import { React } from "../deps.ts";
|
||||||
|
import Base from "./_base.tsx";
|
||||||
|
import type { IPackage } from "../db.ts";
|
||||||
|
|
||||||
|
import { Main, Menu } from "./_default.tsx";
|
||||||
|
import { RenderFile, EntryList, BrowseHeader } from "./_browse.tsx";
|
||||||
|
|
||||||
|
export default async function index({
|
||||||
|
pkg,
|
||||||
|
version,
|
||||||
|
files,
|
||||||
|
directories,
|
||||||
|
readme,
|
||||||
|
path,
|
||||||
|
}: {
|
||||||
|
pkg: IPackage;
|
||||||
|
version?: string;
|
||||||
|
files: { name: string; size: number }[];
|
||||||
|
directories: { name: string }[];
|
||||||
|
readme?: string;
|
||||||
|
path: string;
|
||||||
|
}) {
|
||||||
|
if (!pkg)
|
||||||
|
return (
|
||||||
|
<Base>
|
||||||
|
<h1>Not found</h1>
|
||||||
|
</Base>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Base title={"DenReg - " + pkg.name}>
|
||||||
|
<Main>
|
||||||
|
<BrowseHeader pkg={pkg} version={version} path={path} />
|
||||||
|
|
||||||
|
<EntryList directories={directories} files={files} />
|
||||||
|
|
||||||
|
{readme && <RenderFile content={readme} ext={".md"} />}
|
||||||
|
</Main>
|
||||||
|
<Menu></Menu>
|
||||||
|
</Base>
|
||||||
|
);
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/// <reference path="../types/jsx.d.ts" />
|
/// <reference path="../types/jsx.d.ts" />
|
||||||
import { React, Fragment } from "../deps.ts";
|
import { React, Fragment } from "../deps.ts";
|
||||||
import Base from "./_base.tsx";
|
import Base from "./_base.tsx";
|
||||||
import DB, { IPackage } from "../db.ts";
|
import type { IPackage } from "../db.ts";
|
||||||
import { sortVersions } from "../utils.ts";
|
import { sortVersions } from "../utils.ts";
|
||||||
|
|
||||||
function Package({ pkg }: { pkg: IPackage }) {
|
function Package({ pkg }: { pkg: IPackage }) {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/// <reference path="../types/jsx.d.ts" />
|
/// <reference path="../types/jsx.d.ts" />
|
||||||
import { React, Fragment, Marked } from "../deps.ts";
|
import { React, Fragment, Marked } from "../deps.ts";
|
||||||
import Base from "./_base.tsx";
|
import Base from "./_base.tsx";
|
||||||
import DB, { IPackage } from "../db.ts";
|
import type { IPackage } from "../db.ts";
|
||||||
import { sortVersions, getFile } from "../utils.ts";
|
import { sortVersions, getFile, getAbsolutePackageVersion } from "../utils.ts";
|
||||||
|
import PkgHeader from "./_pkgheader.tsx";
|
||||||
// function Package({ pkg }: { pkg: IPackage }) {
|
// function Package({ pkg }: { pkg: IPackage }) {
|
||||||
// const { name, versions, author } = pkg;
|
// const { name, versions, author } = pkg;
|
||||||
|
|
||||||
@ -44,6 +44,7 @@ export default async function index({
|
|||||||
</Base>
|
</Base>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
version = getAbsolutePackageVersion(pkg, version);
|
||||||
const readmeContent = await getFile(pkg.name, version, "README.md").then(
|
const readmeContent = await getFile(pkg.name, version, "README.md").then(
|
||||||
(res) => {
|
(res) => {
|
||||||
if (res)
|
if (res)
|
||||||
@ -56,23 +57,13 @@ export default async function index({
|
|||||||
return (
|
return (
|
||||||
<Base title={"DenReg - " + pkg.name}>
|
<Base title={"DenReg - " + pkg.name}>
|
||||||
<Main>
|
<Main>
|
||||||
<h2 style="margin-bottom: 0">Package: {pkg.name}</h2>
|
<PkgHeader pkg={pkg} version={version} />
|
||||||
<h4 class="text-muted" style="margin-top: 0; margin-left: .5rem">
|
|
||||||
By {pkg.owner}
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<input id="tab1" type="radio" name="tabs" checked />
|
<input id="tab1" type="radio" name="tabs" checked />
|
||||||
<label for="tab1">Readme</label>
|
<label for="tab1">Readme</label>
|
||||||
|
|
||||||
<input id="tab2" type="radio" name="tabs" />
|
<input id="tab2" type="radio" name="tabs" />
|
||||||
<label for="tab2">Versions</label>
|
<label for="tab2">Versions</label>
|
||||||
{/*
|
|
||||||
<input id="tab3" type="radio" name="tabs" />
|
|
||||||
<label for="tab3">Tab 3</label>
|
|
||||||
|
|
||||||
<input id="tab4" type="radio" name="tabs" />
|
|
||||||
<label for="tab4">Tab 4</label> */}
|
|
||||||
<div class="content" id="content1">
|
<div class="content" id="content1">
|
||||||
{readmeContent !== undefined ? (
|
{readmeContent !== undefined ? (
|
||||||
<div
|
<div
|
||||||
@ -95,7 +86,11 @@ export default async function index({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Main>
|
</Main>
|
||||||
<Menu></Menu>
|
<Menu>
|
||||||
|
<a href={`/browse/${pkg.name}${version ? "@" + version : ""}/`}>
|
||||||
|
Browse Files
|
||||||
|
</a>
|
||||||
|
</Menu>
|
||||||
</Base>
|
</Base>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -27,3 +27,34 @@ code {
|
|||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.browse-list-item {
|
||||||
|
display: flex;
|
||||||
|
margin: 0.2rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: unset;
|
||||||
|
background-image: unset;
|
||||||
|
|
||||||
|
/* flex */
|
||||||
|
}
|
||||||
|
|
||||||
|
.browse-list-item:hover {
|
||||||
|
background-color: var(--muted-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.browse-list-item > img {
|
||||||
|
height: 1rem;
|
||||||
|
width: auto;
|
||||||
|
border: none !important;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browse-list-item-size {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browse-path {
|
||||||
|
margin: 1rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|