Trending

1
2
3
4
5
6
7
8
9
10
11
12
import { rootRef } from "https://esm.town/v/andreterron/rootRef";
import { ValRef } from "https://esm.town/v/andreterron/ValRef";
export function rootValRef(): ValRef | undefined {
const val = rootRef();
return val
? {
handle: val.userHandle,
name: val.valName,
}
: undefined;
}

All TODOs from public vals

Screenshot 2024-02-26 at 10.19.04 AM.png

Todo

  • Remove forks from results ?
  • use a cron val to cache results in an sqlite table
Readme
Fork
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
import codeOnValTown from "https://esm.town/v/andreterron/codeOnValTown";
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";
async function handler(req: Request) {
const { data: vals } = await api(`/v1/search/vals?query=${encodeURIComponent("- [ ]")}`);
let markdown = "# Todos\n";
for (const val of vals) {
if (val.name.startsWith("untitled_")) {
continue;
}
const { readme } = await api<{ readme: string }>(`/v1/vals/${val.id}`);
if (!readme) {
continue;
}
const rows = readme.split("\n");
const todos = rows.filter(row => row.trim().startsWith("- [ ]"));
const link = `https://val.town/v/${val.author.username}/${val.name}`;
markdown = `${markdown}\n## [${val.author.username}/${val.name}](${link})\n${todos.join("\n")}`;
}
return html(await gfm(markdown));
}
export default codeOnValTown(handler);

valToGH

A utility function for programmatically updating a GitHub repository with code retrieved from a Val.

  • Fetches code from a specified Val
  • Commits the fetched code to a specified GitHub repository.
  • Supports TypeScript files (.ts and .tsx).

Note: This function currently only commits to a branch named main. I will be adding functionality for committing to arbitrary branches in the future! (or PR for that!)

Usage

Below is an example snippet demonstrating how to use the valToGH function. This function updates a GitHub repository with the code from a Val repository.

import { valToGH } from 'https://esm.town/v/nbbaier/valToGH';

const repo = "yourGitHubUser/yourRepo";
const val = "valUser/valName";
const ghToken = Deno.eng.get("yourGitHubToken");

const result = await valToGH(repo, val, ghToken);
 
console.log(result);

Parameters:

  • repo: The GitHub repository in the format {user}/{repo}.
  • val: The Val repository in the format {user}/{val}.
  • ghToken: Your GitHub token for authentication (must have write permission to the target repo)
  • prefix (optional): Folder path to prefix to the committed filename. Leave out trailing slash.
  • branch (optional): Branch to commit to. Defaults to main
  • message (optional): The message for the GitHub commit. Defaults to "Created programmatically from val: https://www.val.town/v/${val}"
  • tsx (optional): A boolean indicating whether to treat the file as .tsx (true) or .ts (false).
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
import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON?v=42";
import { Octokit } from "npm:@octokit/rest";
async function getLatestCommit(ghUser: string, ghRepo: string, branch: string, client: Octokit)
{
const { data: refData } = await client.rest.git.getRef({
owner: ghUser,
repo: ghRepo,
ref: `heads/${branch}`,
});
return refData.object.sha;
}
async function createNewTree(
ghUser: string,
ghRepo: string,
path: string,
content: string,
commitSHA: string,
client: Octokit,
) {
const {
data: { sha: currentTreeSHA },
} = await client.rest.git.createTree({
owner: ghUser,
repo: ghRepo,
tree: [
{
path,
content,
mode: "100644",
type: "commit",
},
],
base_tree: commitSHA,
parents: [commitSHA],
});
return currentTreeSHA;
}
async function createNewCommit(
ghUser: string,
ghRepo: string,
commitSHA: string,
currentTreeSHA: string,
message: string,
client: Octokit,
) {
const {

moduleHighlightValueLink: link to a val with a value or method's code highlighted

Examples:

import { moduleHighlightValueLink, getRedirectUrl } from "https://esm.town/v/postpostscript/moduleHighlightValueLink";

console.log(await moduleHighlightValueLink("@std/email", "email"))
// https://val.town/v/std/email#L6-42

console.log(await moduleHighlightValueLink("@postpostscript/moduleHighlightValueLink", "moduleHighlightValueLink"))
// https://val.town/v/postpostscript/moduleHighlightValueLink#L6-20

// get URL you can imbed in an iframe
console.log(getRedirectUrl("@postpostscript/moduleHighlightValueLink", "getRedirectUrl", true));
// https://postpostscript-modulehighlightvaluelink.web.val.run/?embed=1&module=%40postpostscript%2FmoduleHighlightValueLink&name=getRedirectUrl

Iframe example:

import { htmlResponse } from "https://esm.town/v/postpostscript/html";
import { getRedirectUrl } from "https://esm.town/v/postpostscript/moduleHighlightValueLink";

export default async function(req: Request): Promise<Response> {
  return htmlResponse`
    <iframe src="${getRedirectUrl(import.meta.url, "default", true)}" width="100%" height="100%">
    </iframe>
  `;
}
Readme
Fork
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
import * as queryString from "https://deno.land/x/querystring@v1.0.2/mod.js";
import { parse, print } from "https://deno.land/x/swc@0.2.1/mod.ts";
import { findASTValueDefinition, findSpanLocation, getSourceAST } from "https://esm.town/v/postpostscript/ast";
import {
getValEndpointFromUrl,
getValNameFromUrl,
getValUrlFromName,
moduleSource,
} from "https://esm.town/v/postpostscript/meta";
export async function moduleHighlightValueLink(module: string, name: string, log = false) {
const { source, node: ast } = getSourceAST(await moduleSource(module));
const node = findASTValueDefinition(ast, name);
const range = node && findSpanLocation(source, ast, node.span);
const locationText = range && (
range.start === range.end
? `L${range.start}`
: `L${range.start}-${range.end}`
);
if (log) {
console.log({
module,
name,
astSpan: ast.span,
node,
range,
locationText,
});
}
const url = module.charAt(0) === "@"
? getValUrlFromName(module, "val.town")
: getValUrlFromName(getValNameFromUrl(module), "val.town");
return url + (locationText ? `#${locationText}` : "");
}
export function getRedirectUrl(module: string, name: string, embed = false) {
const qs = new URLSearchParams();
if (embed) {
qs.set("embed", "1");
}
qs.set("module", module);
qs.set("name", name);
return getValEndpointFromUrl(import.meta.url) + "/?" + qs.toString();
}
export default async function(req: Request) {
const qs = queryString.parse(req.url.split("?")[1] || "");

sqliteBuilder: Opinionated safe(r) query builder using tagged templates

import { Statement } from "https://esm.town/v/postpostscript/sqliteBuilder";

const unsafeId = "1234 or TRUE"

console.log(Statement`
SELECT *
FROM table
WHERE id = ${unsafeId}
${Statement`AND other_`}
`)
// StatementInstance {
//   sql: "\nSELECT *\nFROM table\nWHERE id = ?\nAND otherCondition\n",
//   args: [ "1234 or TRUE" ],
//   log: false
// }


const results = await Statement`SELECT ...`.execute()
// [ { key: "value", anotherKey: "anotherValue" }, ... ] 

Or you can pass it directly to @std/sqlite.execute:

import { sqlite } from "https://esm.town/v/std/sqlite"

await sqlite.execute(Statement`Select ...`) 

You can combine multiple statements using Statement.prototype.combineWith:

Statement`...`.combineWith(Statement`...`, " AND ")
[
  Statement`fieldA`,
  Statement`fieldB`,
  Statement`fieldC`,
].reduce((a, b) => a.combineWith(b, ", "))
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
import { type InValue, sqlite } from "https://esm.town/v/std/sqlite";
export class StatementInstance {
public sql: string;
public args: InValue[];
public log: boolean;
constructor(sql: string, args: InValue[] = []) {
this.sql = sql;
this.args = args;
this.log = false;
}
combineWith(instance: StatementInstance, sep = "") {
return new StatementInstance(this.sql + sep + instance.sql, this.args.concat(instance.args));
}
async execute() {
if (this.log) {
console.log("executing", {
sql: this.sql,
args: this.args,
});
}
const {
columns,
columnTypes,
rows,
} = await sqlite.execute(this);
return rows.map(row => {
return Object.fromEntries(columns.map((key, i) => {
return [key, row[i]];
}));
});
}
}
export function Statement(
strings: TemplateStringsArray,
...replacements: (InValue | StatementInstance)[]
) {
return strings.reduce((statement, string, index) => {
const stringInstance = new StatementInstance(string);
if (replacements.length >= index + 1) {
const replacement = replacements[index];
return statement
.combineWith(stringInstance)
.combineWith(
replacement instanceof StatementInstance ? replacement : new StatementInstance("?", [replacement]),

Starter App for ssr_react_mini

You need to export four things:

  1. loader - runs on any GET request, on the server. it accepts the Request and returns the props of your React compnent.
  2. action- runs on the server on any non-GET, ie POST, PUT, DELETE, or <form>s submit
  3. Component - your React component. it's initially server-rendered and then client-hydrated
  4. default - you should mostly leave this line alone

This is framework is bleeding-edge. You'll need to read the code of the framework itself (it's very short) to understand what it's doing.

If you have questions or comments, please comment below on this val! (or any of these vals)

Readme
Fork
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
/** @jsxImportSource https://esm.sh/react */
import { useEffect, useState } from "https://esm.sh/react@18.2.0";
import codeOnValTown from "https://esm.town/v/andreterron/codeOnValTown";
import { Button, Form, hydrate } from "https://esm.town/v/stevekrouse/ssr_react_mini";
// runs on page load, on the server
export async function loader(req: Request) {
// ensure any server-side imports only run server side
const { sqlite } = await import("https://esm.town/v/std/sqlite?v=4");
const [, { columns, rows }] = await sqlite.batch([
// you can optionally create your table here IF NOT EXISTS
`CREATE TABLE IF NOT EXISTS ssr_react_mini_startesr_clicks (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`,
// get the data your page needs on load
`select count(*) from ssr_react_mini_starter_clicks`,
]);
// return the props for your Component
return { initialClicks: rows[0][0] };
}
// handle <Form> submissions and other server-requests
export async function action(req: Request) {
const { sqlite } = await import("https://esm.town/v/std/sqlite?v=4");
if (req.method === "POST") {
await sqlite.execute(`INSERT INTO ssr_react_mini_starter_clicks DEFAULT VALUES`);
}
return Response.json("OK");
}
export function Component({ initialClicks }: { initialClicks: number }) {
const [clicks, setClicks] = useState(0);
const addClick = () => setClicks(clicks + 1); // optimistic, client-side
return (
<html>
<head>
<title>SSR React Starter</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://cdn.tailwindcss.com" />
</head>
<body className="max-w-md mx-auto p-10">
<h1 className="text-3xl font-bold">Hello Val Town</h1>
<div className="flex justify-between">
<div>
<div>{clicks + initialClicks} clicks ever</div>
<div>{clicks} clicks since the page loaded</div>
</div>
<Form onSubmit={addClick}>

Inspector to browser json data in HTTP vals

Screenshot 2024-02-23 at 9.31.42 AM.png

Live example: https://stevekrouse-weatherdescription.web.val.run/

Installation

import { fetch } from "https://esm.town/v/std/fetch";
import { json_viewer } from "https://esm.town/v/stevekrouse/json_viewer";

export const weatherDescription = async (params: string[]): Promise<unknown> => {
  let data = await fetch(`https://wttr.in/${params["city"]}?format=j1`);
  let jsonData = await data.json();
  return json_viewer(jsonData);
};

https://val.town/v/stevekrouse/weatherDescription

Thanks @mmcgrana (https://markmcgranaghan.com/) for the idea!

Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { html } from "https://esm.town/v/stevekrouse/html";
export const json_viewer = (data) => {
return html(`<!DOCTYPE html>
<html lang="en">
<body>
<div id="json-viewer"></div>
<script src="https://cdn.jsdelivr.net/npm/@textea/json-viewer@3"></script>
<script>
new JsonViewer({
value: ${JSON.stringify(data)}
}).render('#json-viewer')
</script>
</body>
</html>`);
};

Todo

  • extract params from request paths using urlpatterns
  • add a Form component
  • add support for Layouts
  • support other types url in router ("https://val.town/v/user/val" or "owner/val")
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
/** @jsxImportSource https://esm.sh/react */
import { renderToString } from "https://esm.sh/react-dom@18.2.0/server";
import { html } from "https://esm.town/v/stevekrouse/html";
export type PageProps<T = {}> = T & {
route: string;
params: Record<string, string>;
};
type Context = PageProps & {
render: (props) => Response | Promise<Response>;
};
export type Action = (req: Request, ctx: Context) => Response | Promise<Response>;
export function handler(Component, action?: Action) {
return async (c) => {
if (action) {
return action(c.req.raw, {
params: c.req.param(),
route: c.req.routePath,
render(props) {
return html(renderToString(
<Component
{...{
params: c.req.param(),
route: c.req.routePath,
...props,
}}
/>,
));
},
});
}
return html(renderToString(
<Component
{...{
params: c.req.param(),
route: c.req.routePath,
}}
/>,
));
};
}

Val Release Pattern

Create a new val, then use the proper exports depending on what version you want to install:

// install stable version
export { stable } from "https://esm.town/v/pomdtr/example_release"

// install dev version
export { dev } from "https://esm.town/v/pomdtr/example_release"

// install canary version
export { canary } from "https://esm.town/v/pomdtr/example_release"

Readme
Fork
1
2
3
4
5
6
7
export default function(req) {
return new Response("dev");
}
export { default as dev } from "https://esm.town/v/pomdtr/example_release";
export { default as canary } from "https://esm.town/v/pomdtr/example_release?v=10";
export { default as stable } from "https://esm.town/v/pomdtr/example_release?v=8";

Server-side Render React Mini Framework

Example: https://www.val.town/v/stevekrouse/TodoApp

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
/** @jsxImportSource https://esm.sh/react */
import { renderToString } from "https://esm.sh/react-dom@18.2.0/server";
import { useEffect, useState } from "https://esm.sh/react@18.2.0";
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo?v=25";
import { html } from "https://esm.town/v/stevekrouse/html";
// button that's disabled until client react hydrates
export const Button = (props) => {
const [clientHydrated, setClientHydrated] = useState(false);
useEffect(() => setClientHydrated(true), []);
return <button disabled={!clientHydrated} {...props}></button>;
};
export const Form = ({ onSubmit, ...props }: any) => {
const [clientHydrated, setClientHydrated] = useState(false);
useEffect(() => setClientHydrated(true), []);
return (
<form
disabled={!clientHydrated}
onSubmit={async (e) => {
e.preventDefault();
const onData = onSubmit ? onSubmit(e) : () => 1;
const formData = new FormData(e.target as any);
const resp = await fetch("/", {
method: props.action ?? "POST",
body: formData,
});
await onData(resp);
}}
{...props}
>
</form>
);
};
export const hydrate = (importMetaURL: string) =>
async function(req: Request): Promise<Response> {
const { author, name } = extractValInfo(importMetaURL);
const valURL = `https://www.val.town/v/${author}/${name}`;
const moduleURL = `https://esm.town/v/${author}/${name}`;
const exports = await import(moduleURL);
const action = exports.action ?? (() => ({}));
const loader = exports.loader ?? (() => ({}));
const Component = exports.Component ?? (() => (
<div>
Error: Please ensure you <code>export Component</code>" in{" "}
<a target="_blank" href={valURL}>@{author}/{name}</a>.
</div>
));
if (req.method === "GET") {