Fetch the source of a val

This val was created before the introduction of https://esm.town

Usage

curl https://pomdtr-raw.web.val.run/<author>/<name>.<extension>[?v=<version>]

To see the code of this val, use https://pomdtr-raw.web.val.run/pomdtr/raw.ts

Examples

Fetching the val code

$  curl https://pomdtr-raw.web.val.run/pomdtr/add.tsx

You can also use js, jsx and ts extension (only the content-type change, there is no transpilation).

Fetching private val

Pass an api token as an username

$ curl "https://<token>@pomdtr-raw.web.val.run/pomdtr/privateVal.ts"

Fetching the val README

$ curl https://pomdtr-raw.web.val.run/pomdtr/add.md

Getting an image

$ curl https://pomdtr-raw.web.val.run/pomdtr/add.png

Fetching a specific version of a val

$ curl https://pomdtr-raw.web.val.run/pomdtr/raw.ts?v=66

You need to be authenticated to use this method.

Fetching the val metadata

$ curl https://pomdtr-raw.web.val.run/pomdtr/add.json

Running vals locally using Deno

Create a new val.ts file referencing the @pomdtr.add

import { add } from "https://pomdtr-raw.web.val.run/pomdtr/add.ts";

console.log(add(1, 2));

then use deno run

$ deno run ./val.ts
3

If you val accept a request and return a response, you can pass it to Deno.Serve to run it locally!

import {raw} from "https://pomdtr-raw.web.val.run/pomdtr/raw.ts";

Deno.serve(raw);

If your val is private, you can set the DENO_AUTH_TOKENS env.

DENO_AUTH_TOKENS=<val-town-token>@pomdtr-raw.web.val.run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo";
import { parseAuthorizationHeader } from "https://esm.town/v/pomdtr/parseAuthorizationHeader";
import { readmeToHtmlResponse } from "https://esm.town/v/pomdtr/readme";
async function createScreenshot(code: string) {
const apiUrl = "https://sourcecodeshots.com/api/image";
const resp = await fetch(apiUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ code }),
});
if (!resp.ok) {
throw new Error(await resp.text());
}
return resp.blob();
}
export default async function raw(req: Request) {
const url = new URL(req.url);
const [author, file] = url.pathname.slice(1).split("/");
if (!author || !file) {
const { author, name } = extractValInfo(import.meta.url);
return readmeToHtmlResponse(author, name);
}
const [name, extension] = file.split(".");
const headers = {};
if (req.headers.get("Authorization")) {
const auth = parseAuthorizationHeader(
req.headers.get("Authorization"),
);
if (auth.type == "basic") {
headers["Authorization"] = `Bearer ${auth.username}`;
}
else {
headers["Authorization"] = req.headers.get("Authorization");
}
}
const resp = await fetch(`https://api.val.town/v1/alias/${author}/${name}`, {
headers,
});
if (!resp.ok) {
return resp;
}
let val = await resp.json();
if (url.searchParams.has("v")) {
const version = url.searchParams.get("v");
const resp = await fetch(
`https://api.val.town/v1/vals/${val.id}/versions/${version}`,
{ headers },
);
if (!resp.ok) {
return resp;
}
val = await resp.json();
}
if (extension == "json") {
return new Response(JSON.stringify(val, null, 2), {
headers: { "Content-Type": "text/json" },
});
}
if (extension == "md") {
return new Response(val.readme, {
headers: { "content-type": "text/markdown" },
});
}
if (extension == "js") {
return new Response(val.code, {
headers: {
"Content-Type": "text/javascript",
},
});
}
if (extension == "ts") {
return new Response(val.code, {
headers: {
"Content-Type": "text/javascript",
},
});
}
if (extension == "jsx") {
return new Response(val.code, {
headers: {
"Content-Type": "text/jsx",
},
});
}
if (extension == "tsx") {
return new Response(val.code, {
headers: {
"Content-Type": "text/tsx",
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import { getMermaidDependencyGraph } from "https://esm.town/v/rlesser/getDependencyGraph?v=91";
import { getReverseDependencies } from "https://esm.town/v/rlesser/getReverseDependencies";
import { gfm } from "https://esm.town/v/rlesser/gfm";
import { Hono } from "npm:hono@3.9.2";
const app = new Hono();
const homePageMarkdown = `
# ValTown Dependency Graph Viewer
### What is this
This tool lets you view dependency graph for any public val on [valtown](www.val.town).
Add a valtown slug (\`author/name\`) to this url to see the dependency graph for that val.
Some big dependency graphs - let me know if you find any bigger ones!
* [stevekrouse/sqlite_admin](stevekrouse/sqlite_admin)
* [nbbaier/vtIdeaAggregator](nbbaier/vtIdeaAggregator)
* [rodrigotello/hnFollow](rodrigotello/hnFollow)
* [rlesser/dependency_graph](rlesser/dependency_graph) (a bit meta...)
### TODO
Some ideas for how to expand this site:
* **show the upstream dependencies** - no public API for this yet, but could probably make it work with the search api
* **search for vals** - let people search for vals right from here, using the search api and debouncing
* **toggle graph complexity** - let people choose to see all dependencies or only valtown dependencies (thru the browser)
* **cache graphs in DB** - we should be kind to valtown's API by caching graph data as exploring the
graph involves a lot of calls. maybe only update it when the val version number changes?
* **handle more module sources** - currently we only group npm, esm.sh, and valtown modules. we could add more or make
this a dynamic grouping mechanism?
* **author pages?** - a page to show all the public vals by a person. not sure if needed and dont want to just end up
wrapping every val.town page here.
* **val readme** - show the val readme at the top of each val page
`;
const valPageMarkdown = (author: string, name: string, mermaid: string, reverseDeps: string) => `
[Home](/)
# ${author}/${name}
### Dependency Graph
\`\`\`mermaid
${mermaid}
\`\`\`
### Reverse Dependencies
${reverseDeps || "*None found*"}
`;
const footerMarkdown = `
---
*Website created by [rlesser/dependency_graph](https://www.val.town/v/rlesser/dependency_graph)*
`;
async function makePage(markdown: string, title: string) {
return await gfm(markdown + footerMarkdown, { title, favicon: "🖇️" });
}
app.get("/", async (c) => {
return c.html(await makePage(homePageMarkdown, "ValTown Dependency Graph"));
});
app.get("/:author/:name", async (c) => {
const author = c.req.param("author");
const name = c.req.param("name");
try {
const mermaid = await getMermaidDependencyGraph(`${author}/${name}`);
const reverseDeps = (await getReverseDependencies(`${author}/${name}`))
.map(d => `* [${d.slug}](/${d.slug})`)
.join("\n");
return c.html(
await makePage(
valPageMarkdown(author, name, mermaid, reverseDeps),
`${author}/${name} | ValTown Dependency Graph`,
),
);
} catch (e) {
return c.html("Error!<br>" + e);
}
});
export default app.fetch;

Preview and edit blobs

Usage:

Create valimport blobEditor from "https://esm.town/v/pomdtr/blob_editor" export default blobEditor("article.md")

You can easily protect your val behind @pomdtr/password_auth or @pomdtr/email_auth

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/hono/jsx **/
import { useEffect, useRef } from "https://esm.sh/hono/jsx/dom";
export function BlobEditor(props: { blob: { language?: string; text: string } }) {
const ref = useRef();
const saveBlob = async () => {
const resp = await fetch("/edit", {
method: "POST",
body: JSON.stringify({
text: ref.current!.code,
}),
});
if (!resp.ok) {
alert(await resp.text());
return;
}
window.location.replace("/");
};
return (
<article style={{ height: "100vh", display: "flex", flexDirection: "column" }}>
<code-mirror
style={{
flexGrow: 1,
backgroundColor: "#F9FAFB",
color: "#373C44",
}}
class="overflow-auto"
ref={ref}
code={props.blob.text}
language={props.blob.language}
>
</code-mirror>
<footer>
<div class="grid">
<button onClick={saveBlob}>Save</button>
</div>
</footer>
</article>
);
}
export function blobEditor(key: string, options?: { title?: string }) {
return async (req: Request) => {
const { html } = await import("https://esm.town/v/pomdtr/gfm");
const { blob } = await import("https://esm.town/v/std/blob?v=11");
const { Hono } = await import("npm:hono");
const { Island } = await import("https://esm.town/v/pomdtr/hono_island");
const { HTTPException } = await import("npm:hono/http-exception");
const { jsxRenderer, useRequestContext } = await import("npm:hono/jsx-renderer");
const { extractValInfo } = await import("https://esm.town/v/pomdtr/extractValInfo");
async function readBlob(key: string) {
const resp = await blob.get(key);
if (resp.status == 200) {
return resp.text();
} else if (resp.status == 404) {
return "";
} else {
throw new Error(await resp.text());
}
}
const router = new Hono();
router.use(jsxRenderer(({ children }) => {
const c = useRequestContext();
const { pathname } = new URL(c.req.url);
return (
<html>
<head>
<link rel="icon" href="https://fav.farm/📃" />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
/>
<script
type="module"
src="https://esm.town/v/pomdtr/hydrate_hono_islands"
>
</script>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/code-mirror-web-component@0.0.18/dist/code-mirror.js"
>
</script>
</head>
<body
style={{
height: "100vh",
}}
>
{children}
</body>
</html>
);
}));

jsr2gh

Redirect to jsr package's GitHub repository page, like vladimyr-jsr2gh.web.val.run/@luca/flag

Usage

https://vladimyr-jsr2gh.web.val.run/<scope>/<name>

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// SPDX-License-Identifier: 0BSD
import { serveReadme } from "https://esm.town/v/vladimyr/serveReadme";
import { toHonoHandler } from "https://esm.town/v/vladimyr/toHonoHandler";
import { Hono } from "jsr:@kyiro/hono";
import ky from "npm:ky";
import * as v from "npm:valibot";
const PackageDataSchema = v.object({
githubRepository: v.nullable(v.object({
owner: v.string(),
name: v.string(),
})),
});
const app = new Hono();
app.get("/", toHonoHandler(serveReadme(import.meta.url)));
app.get("/:scope{@[^/]+}/:name", async (c) => {
const scope = c.req.param("scope").slice(1);
const name = c.req.param("name");
const { githubRepository: repo } = await fetchPackageData(scope, name);
if (repo) {
const githubURL = new URL(`https://github.com/${repo.owner}/${repo.name}`);
return c.redirect(githubURL);
}
const jsrURL = new URL(`https://jsr.io/@${scope}/${name}`);
return c.redirect(jsrURL);
});
export default app.fetch;
export async function fetchPackageData(scope: string, name: string) {
const prefixUrl = "https://api.jsr.io/";
const data = await ky.get(`scopes/${scope}/packages/${name}`, { prefixUrl }).json();
return v.parse(PackageDataSchema, data);
}

Use readme as website

Usage

Create valimport {article} from "https://esm.town/v/pomdtr/article" export default article()

See this val http endpoint as an example: https://pomdtr-article.web.val.run

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { api } from "https://esm.town/v/pomdtr/api";
import { gfm } from "https://esm.town/v/pomdtr/gfm";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";
export function article({
val,
title,
}: {
val?: { author: string; name: string };
title?: string;
} = {}) {
return async (req: Request) => {
if (!val) {
const { hostname } = new URL(req.url);
if (!hostname.endsWith(".web.val.run")) {
return new Response(null, {
status: 500,
});
}
const subdomain = hostname.split(".")[0];
const [author, name] = subdomain.split("-");
val = { author, name };
}
if (!title) {
title = `@${val.author}/${val.name}`;
}
const { code, readme } = await api(`/v1/alias/${val.author}/${val.name}`);
return html(await gfm(readme, { title, favicon: "📝" }));
};
}
export default article();

Cli Vals

Cli vals are a new type of val (same as http, email, cron and script vals). A cli val must use a function without args as it's default export.

The function body will run on the user device using deno. An error message will be shown if deno is not installed.

export default function() {
  if (Deno.args.length == 0) {
    console.error("<name> arg is required!");
    Deno.exit(1);
  }
  console.log(`Hey ${Deno.args[0]}!`);
}

Fork @pomdtr/example_cli to get started.

Of course, you can use a cli framework to parse arguments (ex: cliffy).

Running a cli val

Go to https://pomdtr-cli.web.val.run/v/<author>/<name>[?v=<version>] to get a runnable script for your val.

You can pipe the script to a shell to test it

curl  'https://pomdtr-cli.web.val.run/v/pomdtr/cli_example' | sh -s Steve
Hello Steve!

Or save it to your $PATH.

# save the script to the ~/.local/bin folder
curl  'https://pomdtr-cli.web.val.run/v/pomdtr/cli_example'  > ~/.local/bin/cli_example
# make the script executable
chmod +x ~/.local/bin/cli_example
# run the installed val
cli_example Steve

Allowing cli vals to access private resources

Cli vals run on your device, so by default they can only access public/unlisted vals.

You can set the DENO_AUTH_TOKENS env var in your shell config to allow deno to import private vals.

export DENO_AUTH_TOKENS=<your-token>@esm.town

Cli vals don't have access to val town tokens. Instead of trying to replicate your valtown secrets locally, you can configure your cli vals to call your http endpoints using fetch.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo";
import { gfm } from "https://esm.town/v/pomdtr/gfm";
import { readme } from "https://esm.town/v/pomdtr/readme";
import { Hono } from "npm:hono@3.11.11";
const script = (esmUrl) =>
`#!/bin/sh
# check if deno is installed
if ! command -v deno >/dev/null; then
echo "Please install deno first: https://deno.land/#installation"
exit 1
fi
exec deno eval --quiet --reload=https://esm.town/v/ '(await import("${esmUrl}")).default()' "$@"
`;
const app = new Hono();
app.get("/", async c => {
const html = await gfm(await readme());
return c.html(html);
});
app.get("/v/:author/:name", c => {
const { author, name } = c.req.param();
const { version } = c.req.query();
let esmUrl = `https://esm.town/v/${author}/${name}`;
if (version) {
esmUrl = `${esmUrl}?version=${version}`;
}
return new Response(script(esmUrl));
});
export default app.fetch;
// #cli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const html = `
<h3 style="text-align: center;">Welcome to my website</h3>
<script src="https://esm.town/v/pomdtr/embed" data-element-id="my-element" type="module"></script>
<!-- anywhere else on your page -->
<div id="my-element">
console.log("hello world!")
</div>
`;
export default function(req: Request) {
return new Response(
html,
{
headers: {
"Content-Type": "text/html",
},
},
);
}

My Val Town Wish List

Some things I'd love to see come to val town (some of these are already on the way according to the team)

  • Editing a val's readme through the API
  • A command bar (something like paco's cmdk would be dope)
  • The ability or programmatically respond to events (like a val being run) right within Val Town
  • User defined templates usable right in the val creation flow
  • Public folders / ability to add readmes to folders
  • Dynamic folders (ala Tana's search nodes)
  • Custom val metadata (such as tags)
  • A more fully featured markdown editor for readmes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// @title My Val Town Wish List
import { api } from "https://esm.town/v/pomdtr/api";
import { extractMetadata } from "https://esm.town/v/pomdtr/extractMetadata";
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo";
import { gfm } from "https://esm.town/v/pomdtr/gfm";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";
export async function examplePost(req: Request) {
const { author, name } = extractValInfo(import.meta.url);
const { code, readme } = await api(`/v1/alias/${author}/${name}`);
const title = extractMetadata("title", code);
return html(await gfm(readme, { title, favicon: "🎁" }));
}

Usage:

Create valimport blobEditor from "https://esm.town/v/pomdtr/blob_editor" export default blobEditor("article.md")

You can easily protect your val behind @pomdtr/passwordAuth or @pomdtr/emailAuth

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource npm:hono/jsx **/
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo";
import { html } from "https://esm.town/v/pomdtr/gfm";
import { blob } from "https://esm.town/v/std/blob?v=11";
import { Hono } from "npm:hono";
import { HTTPException } from "npm:hono/http-exception";
import { jsxRenderer, useRequestContext } from "npm:hono/jsx-renderer";
export function blobEditor(key: string, options?: { title?: string }) {
const router = new Hono();
router.use(jsxRenderer(({ children }) => {
const c = useRequestContext();
const { pathname } = new URL(c.req.url);
return (
<html>
<head>
<link rel="icon" href="https://fav.farm/📃" />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
/>
</head>
<body>
<header class="container">
<nav>
<ul>
<li>
<a href={"/"}>
<strong>{options?.title || "Blob Editor"}</strong>
</a>
</li>
</ul>
{pathname == "/"
? (
<ul>
<li>
<a href={"/edit"} role="button" class="outline">Edit Blob</a>
</li>
</ul>
)
: undefined}
</nav>
</header>
<main class="container">
{children}
</main>
</body>
</html>
);
}));
router.get("/", async (c) => {
if (key.endsWith(".md")) {
const markdown = await readBlob(key);
return c.render(
<article dangerouslySetInnerHTML={{ __html: await html(markdown) }}>
</article>,
);
}
const text = await readBlob(key);
return c.text(text);
});
router.get("/edit", async (c) => {
const text = readBlob(key);
return c.render(
<main class="container">
<form method="POST" action="/">
<article>
<textarea name="content" id="editor" style={{ height: "60vh" }}>{text}</textarea>
<footer>
<input type="submit" value="Save" />
</footer>
</article>
</form>
</main>,
);
});
router.post("/", async (c) => {
const body = await c.req.parseBody();
if (typeof body.content != "string") {
throw new HTTPException(400);
}
await blob.set(key, body.content);
return c.redirect("/");
});
return router.fetch;
}
async function readBlob(key: string) {
const resp = await blob.get(key);
if (resp.status == 200) {
return resp.text();
} else if (resp.status == 404) {
return "";

Render Gist

Usage

Go to a gist page, and replace gist.github.com by pomdtr-renderGist.web.val.run

Example: https://pomdtr-renderGist.web.val.run/probonopd/9feb7c20257af5dd915e3a9f2d1f2277

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { gfm } from "https://esm.town/v/pomdtr/gfm";
import { fetchText } from "https://esm.town/v/stevekrouse/fetchText?v=6";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";
export default async function(req: Request) {
const { pathname } = new URL(req.url);
const [_, owner, id] = pathname.split("/");
if (!owner || !id) {
return new Response("Invalid Request", {
status: 400,
});
}
const content = await fetchText(`https://gist.github.com/${owner}/${id}/raw`);
return html(await gfm(content));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { api } from "https://esm.town/v/pomdtr/api";
import { gfm } from "https://esm.town/v/pomdtr/gfm";
export async function readmeToHtmlResponse(author: string, name: string) {
return new Response(await readmeToHtml(author, name), {
headers: {
"Content-Type": "text/html",
},
});
}
export async function readmeToHtml(author: string, name: string) {
const markdown = await readme(author, name);
return gfm(markdown);
}
export async function readme(author: string, name: string) {
const { readme } = await api(`/v1/alias/${author}/${name}`);
return readme;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import { extractMetadata } from "https://esm.town/v/nbbaier/extractMetadata";
import getPosts from "https://esm.town/v/nbbaier/getPosts";
import { gfm } from "https://esm.town/v/nbbaier/gfm";
import { stripMetadata } from "https://esm.town/v/nbbaier/stripFrontmatter";
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo";
import { fetch } from "https://esm.town/v/std/fetch?v=4";
import { Hono } from "npm:hono";
const app = new Hono();
const homepage = extractValInfo(import.meta.url);
const repo = "nbbaier/tidal-triaxial";
const content = "posts";
const apiUrl = `https://api.github.com/repos/${repo}/contents/${content}`;
const contentUrl = `https://raw.githubusercontent.com/${repo}/main/${content}`;
const editUrl = `https://github.com/${repo}/edit/main/${content}`;
app.get("/", async c => {
const posts = await getPosts(
apiUrl,
Deno.env.get("GH_BLOG_TOKEN"),
);
const items = await Promise.all(posts.map(async (post) => {
const data = await fetch(post.url);
const title = await extractMetadata("title", await data.text());
const segments = new URL(post.url).pathname.split("/").filter(Boolean);
const slug = segments[segments.length - 1].replace(".md", "");
return `- [${title}](/posts/${slug})`;
}));
const md = `# ${homepage.author}'s blog
A proof of concept blog inspired by [pomdtr's](url) [Val Town Blog](https://www.val.town/u/pomdtr). The infrastructure for the blog is hosted on [val.town](https://www.val.town),
while the content is hosted on github in [this repo](https://github.com/nbbaier/tidal-triaxial).
## Posts
${items.join("\n")}
`;
return c.html(await gfm(md));
});
app.get("/posts/:title", async c => {
const title = c.req.param("title");
const res = await fetch(`${contentUrl}/${title}.md`);
const md = await stripMetadata(await res.text());
return c.html(await gfm(md));
});
app.get("/posts/:title/edit", async (c) => {
const title = c.req.param("title");
return c.redirect(`${editUrl}/${title}.md`);
});
export default app.fetch;
1
2
3
4
5
6
7
8
9
10
11
import { api } from "https://esm.town/v/pomdtr/api";
import { extractMetadata } from "https://esm.town/v/pomdtr/extractMetadata";
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo";
import { gfm } from "https://esm.town/v/pomdtr/gfm";
export async function ideaList(author, name) {
const { code, readme } = await api(`/v1/alias/${author}/${name}`);
const title = extractMetadata("title", code);
return gfm(readme, { title, favicon: "%F0%9F%92%A1" });
}

export const title = "mdx"

{title}

Usage

Create valimport { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo"; import { mdx } from "https://esm.town/v/pomdtr/mdx"; const { author, name } = extractValInfo(import.meta.url); export default mdx(author, name);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/** @jsxImportSource https://esm.sh/preact **/
import { modifyFetchHandler } from "https://esm.town/v/andreterron/codeOnValTown?v=50";
import { api } from "https://esm.town/v/pomdtr/api";
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo";
import { gfm } from "https://esm.town/v/pomdtr/gfm";
import { compile } from "npm:@mdx-js/mdx";
import { render } from "npm:preact-render-to-string";
const css = `
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 980px;
margin: 0 auto;
padding: 45px;
}
@media (max-width: 767px) {
.markdown-body {
padding: 15px;
}
}
`;
export function mdx(author: string, name: string) {
return async (req: Request) => {
const { readme } = await api(`/v1/alias/${author}/${name}`);
const vfile = await compile(readme, {
jsxImportSource: "https://esm.sh/preact",
});
const url = new URL(req.url);
if (url.pathname == "/mod.js") {
return new Response(vfile.toString(), {
headers: {
"Content-Type": "text/javascript",
},
});
}
const blob = new Blob([vfile.toString()], {
type: "text/tsx",
});
const importURL = URL.createObjectURL(blob);
const { default: MDXContent } = await import(importURL);
return new Response(
render(
<html>
<head>
<title>{`${author}/${name}`}</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="📝" rel="icon" />
<link
href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.4.0/github-markdown.min.css"
rel="stylesheet"
/>
<link href="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/themes/prism.css" rel="stylesheet" />
<style dangerouslySetInnerHTML={{ __html: css }}></style>
</head>
<body class="markdown-body">
<MDXContent />
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/components/prism-core.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/plugins/autoloader/prism-autoloader.min.js">
</script>
</body>
</html>,
),
{
headers: {
"Content-Type": "text/html",
},
},
);
};
}
const { author, name } = extractValInfo(import.meta.url);
export default mdx(author, name);

Val Shot

Usage

https://pomdtr-valshot.web.val.run/v/<author>/<val>

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo";
import { gfm } from "https://esm.town/v/pomdtr/gfm";
import { readme } from "https://esm.town/v/pomdtr/readme";
import { Hono } from "npm:hono";
import ky from "npm:ky";
const router = new Hono();
const { author, name } = extractValInfo(import.meta.url);
router.get("/", async c => {
return c.html(gfm(await readme(author, name)));
});
router.get("/v/:author/:name", async (c) => {
const params = c.req.param();
const query = c.req.query();
const code = await fetchValCode(`${params.author}/${params.name}`);
const imageURL = await createScreenshot(code, query.theme);
return Response.redirect(imageURL);
});
export default router.fetch;
export async function createScreenshot(code: string, theme: string = "dark-plus"): Promise<URL> {
const apiUrl = "https://sourcecodeshots.com/api/image/permalink";
const { url } = await ky.post(apiUrl, {
json: {
code,
settings: { language: "tsx", theme },
},
}).json();
return new URL(url);
}
async function fetchValCode(alias: string): Promise<string> {
const prefixUrl = "https://api.val.town/v1/alias";
const { code } = await ky.get(alias, { prefixUrl }).json();
return code;
}