authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2025-06-08 12:38:25-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2025-10-14 02:40:44-07:00
log6832e061d3ab21c081e593dc308f5087eacf377d
tree1d5a07d4463a1df48d1c827a82b9ccdbb992faa6
parent8f9a9d4e99fa9e1b759ae0f7a63151deae720585
signature Commit is signed but in an unrecognized format.

add content type library


12 files changed, 153 insertions(+), 40 deletions(-)

deno.jsonc+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1{1{
2 "lint": {2 "lint": {
3 "exclude": ["framework/meta"], // OLD3 "exclude": ["src"], // OLD
4 "rules": {4 "rules": {
5 "exclude": [5 "exclude": [
6 "no-explicit-any" // TODO6 "no-explicit-any" // TODO
framework/assets.ts+1
...@@ -97,3 +97,4 @@ import * as fs from "./fs.ts";...@@ -97,3 +97,4 @@ import * as fs from "./fs.ts";
97import type { Context, Next } from "hono";97import type { Context, Next } from "hono";
98import type { StatusCode } from "hono/utils/http-status";98import type { StatusCode } from "hono/utils/http-status";
99import type { BuiltAsset, BuiltAssetMap, View } from "./incremental.ts";99import type { BuiltAsset, BuiltAssetMap, View } from "./incremental.ts";
100import { Buffer } from "node:buffer";
framework/bundle.ts+2-2
...@@ -1,5 +1,4 @@...@@ -1,5 +1,4 @@
1// This file implements client-side bundling, mostly wrapping esbuild.1// This file implements client-side bundling, mostly wrapping esbuild.
2import process from "node:process";
3const plugins: esbuild.Plugin[] = [2const plugins: esbuild.Plugin[] = [
4 // There are currently no plugins needed by 'paperclover.net'3 // There are currently no plugins needed by 'paperclover.net'
5];4];
...@@ -80,6 +79,7 @@ export async function bundleClientJavaScript(...@@ -80,6 +79,7 @@ export async function bundleClientJavaScript(
80 }79 }
81}80}
8281
83import * as path from "node:path";
84import * as esbuild from "esbuild";82import * as esbuild from "esbuild";
83import * as path from "node:path";
84import process from "node:process";
85import { Incremental } from "./incremental.ts";85import { Incremental } from "./incremental.ts";
framework/engine/jsx-runtime.ts+1-1
...@@ -35,7 +35,7 @@ declare global {...@@ -35,7 +35,7 @@ declare global {
35 [name: string]: Record<string, unknown>; 35 [name: string]: Record<string, unknown>;
36 } 36 }
37 interface ElementChildrenAttribute { 37 interface ElementChildrenAttribute {
38 children: {}; 38 children: unknown;
39 } 39 }
40 type Element = engine.Node; 40 type Element = engine.Node;
41 type ElementType = keyof IntrinsicElements | engine.Component; 41 type ElementType = keyof IntrinsicElements | engine.Component;
framework/hot.ts+4-2
...@@ -157,11 +157,13 @@ function loadMarko(module: NodeJS.Module, filepath: string) {...@@ -157,11 +157,13 @@ function loadMarko(module: NodeJS.Module, filepath: string) {
157 // bare client import statements to it's own usage.157 // bare client import statements to it's own usage.
158 if (src.match(/^\s*client\s+import\s+["']/m)) {158 if (src.match(/^\s*client\s+import\s+["']/m)) {
159 src = src.replace(159 src = src.replace(
160 /^\s*client\s+import\s+("[^"]+|'[^']+)[^\n]+/m,160 /^\s*client\s+import\s+("[^"]+"|'[^']+')[^\n]+/m,
161 "<CloverScriptInclude src=$1 />",161 "<CloverScriptInclude src=$1 />",
162 ) + '\nimport { Script as CloverScriptInclude } from "#sitegen";';162 ) + '\nimport { Script as CloverScriptInclude } from "#sitegen";\n';
163 }163 }
164164
165 console.log(src);
166 console.log("---");
165 src = marko.compileSync(src, filepath).code;167 src = marko.compileSync(src, filepath).code;
166 src = src.replace("marko/debug/html", "#ssr/marko");168 src = src.replace("marko/debug/html", "#ssr/marko");
167 return loadEsbuildCode(module, filepath, src);169 return loadEsbuildCode(module, filepath, src);
framework/mime.ts+19-18
...@@ -1,29 +1,30 @@...@@ -1,29 +1,30 @@
1const db = new Map(1const entries = fs.readFileSync(
2 fs.readFileSync(path.join(import.meta.dirname, "mime.txt"), "utf8")2 path.join(import.meta.dirname, "mime.txt"),
3 .split("\n").filter(Boolean).map((line) =>3 "utf8",
4 line.split(/\s+/) as [string, string]4)
5 ),5 .split("\n")
6);6 .map((line) => line.trim())
7 .filter((line) => line && !line.startsWith("#"))
8 .map((line) => line.split(/\s+/, 2) as [string, string]);
9const extensions = new Map(entries.filter((x) => x[0].startsWith(".")));
10const fullNames = new Map(entries.filter((x) => !x[0].startsWith(".")));
711
8/**12/**
9 * Accepts:13 * Accepts:
10 * - Full file path14 * - Full file path or basename
11 * - Extension (with or without dot)15 * - Extension (with or without dot)
12 */16 */
13export function contentTypeFor(file: string) {17export function contentTypeFor(file: string) {
14 if (file.includes("/") || file.includes("\\")) {18 const slash = file.indexOf("/");
15 // Some file names are special cased.19 if (slash !== -1) file = file.slice(slash + 1);
16 switch (path.basename(file)) {
17 case "rss.xml":
18 return "application/rss+xml";
19 }
20
21 file = path.extname(file);
22 }
23 const dot = file.indexOf(".");20 const dot = file.indexOf(".");
24 if (dot === -1) file = "." + file;21 if (dot === -1) file = "." + file;
25 else if (dot > 0) file = file.slice(dot);22 else if (dot > 0) {
26 return db.get(file) ?? "application/octet-stream";23 let entry = fullNames.get(file);
24 if (entry) return entry;
25 file = file.slice(dot);
26 }
27 return extensions.get(file) ?? "application/octet-stream";
27}28}
2829
29import * as fs from "./fs.ts";30import * as fs from "./fs.ts";
framework/mime.txt created+99
...@@ -0,0 +1,99 @@
1# media types
2# https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types
3.aac audio/x-aac
4.aif audio/x-aiff
5.aifc audio/x-aiff
6.aiff audio/x-aiff
7.asm text/x-asm
8.avi video/x-msvideo
9.bat application/x-msdownload
10.c text/x-c
11.chat text/x-clover-chatlog
12.class application/java-vm
13.cmd application/x-msdownload
14.com application/x-msdownload
15.conf text/plain
16.cpp text/x-c
17.css text/css
18.csv text/csv
19.cxx text/x-c
20.def text/plain
21.diff text/plain
22.dll application/x-msdownload
23.dmg application/octet-stream
24.doc application/msword
25.docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
26.epub application/epub+zip
27.exe application/x-msdownload
28.flv video/x-flv
29.fbx application/fbx
30.gz application/x-gzip
31.h text/x-c
32.h264 video/h264
33.hh text/x-c
34.htm text/html;charset=utf-8
35.html text/html;charset=utf-8
36.ico image/x-icon
37.ics text/calendar
38.in text/plain
39.jar application/java-archive
40.java text/x-java-source
41.jpeg image/jpeg
42.jpg image/jpeg
43.jpgv video/jpeg
44.jxl image/jxl
45.js application/javascript
46.json application/json
47.latex application/x-latex
48.list text/plain
49.log text/plain
50.m4a audio/mp4
51.man text/troff
52.mid audio/midi
53.midi audio/midi
54.mov video/quicktime
55.mp3 audio/mpeg
56.mp4 video/mp4
57.msh model/mesh
58.msi application/x-msdownload
59.obj application/octet-stream
60.ogg audio/ogg
61.otf application/x-font-otf
62.pdf application/pdf
63.png image/png
64.ppt application/vnd.ms-powerpoint
65.pptx application/vnd.openxmlformats-officedocument.presentationml.presentation
66.psd image/vnd.adobe.photoshop
67.py text/x-python
68.rar application/x-rar-compressed
69.rss application/rss+xml
70.rtf application/rtf
71.rtx text/richtext
72.s text/x-asm
73.pem application/x-pem-file"
74.ser application/java-serialized-object
75.sh application/x-sh
76.sig application/pgp-signature
77.silo model/mesh
78.svg image/svg+xml
79.t text/troff
80.tar application/x-tar
81.text text/plain
82.tgz application/x-gzip
83.tif image/tiff
84.tiff image/tiff
85.torrent application/x-bittorrent
86.ttc application/x-font-ttf
87.ttf application/x-font-ttf
88.txt text/plain
89.urls text/uri-list
90.v text/x-v
91.wav audio/x-wav
92.wmv video/x-ms-wmv
93.xls application/vnd.ms-excel
94.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
95.xml application/xml
96.xps application/vnd.ms-xpsdocument
97
98# special cased based on file name
99rss.xml application/rss+xml
framework/queue.ts+5-5
...@@ -1,8 +1,3 @@...@@ -1,8 +1,3 @@
1import { Progress } from "@paperclover/console/Progress";
2import { Spinner } from "@paperclover/console/Spinner";
3import * as path from "node:path";
4import process from "node:process";
5
6interface QueueOptions<T, R> {1interface QueueOptions<T, R> {
7 name: string;2 name: string;
8 fn: (item: T, spin: Spinner) => Promise<R>;3 fn: (item: T, spin: Spinner) => Promise<R>;
...@@ -204,3 +199,8 @@ export class OnceMap<T> {...@@ -204,3 +199,8 @@ export class OnceMap<T> {
204 return result;199 return result;
205 }200 }
206}201}
202
203import { Progress } from "@paperclover/console/Progress";
204import { Spinner } from "@paperclover/console/Spinner";
205import * as path from "node:path";
206import process from "node:process";
package-lock.json+3-3
...@@ -1027,9 +1027,9 @@...@@ -1027,9 +1027,9 @@
1027 "license": "ISC"1027 "license": "ISC"
1028 },1028 },
1029 "node_modules/acorn": {1029 "node_modules/acorn": {
1030 "version": "8.14.1",1030 "version": "8.15.0",
1031 "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",1031 "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
1032 "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",1032 "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
1033 "license": "MIT",1033 "license": "MIT",
1034 "bin": {1034 "bin": {
1035 "acorn": "bin/acorn"1035 "acorn": "bin/acorn"
package.json+3-4
...@@ -1,5 +1,4 @@...@@ -1,5 +1,4 @@
1{1{
2 "private": true,
3 "type": "module",2 "type": "module",
4 "dependencies": {3 "dependencies": {
5 "@hono/node-server": "^1.14.3",4 "@hono/node-server": "^1.14.3",
...@@ -23,9 +22,9 @@...@@ -23,9 +22,9 @@
23 "#ssr/jsx-runtime": "./framework/engine/jsx-runtime.ts",22 "#ssr/jsx-runtime": "./framework/engine/jsx-runtime.ts",
24 "#ssr/marko": "./framework/engine/marko-runtime.ts",23 "#ssr/marko": "./framework/engine/marko-runtime.ts",
25 "#marko/html": {24 "#marko/html": {
26 "development": "marko/debug/html",25 "types": "marko/html",
27 "production": "marko/html",26 "production": "marko/production",
28 "types": "marko/html"27 "node": "marko/debug/html"
29 },28 },
30 "#hono/platform": {29 "#hono/platform": {
31 "bun": "hono/bun",30 "bun": "hono/bun",
repl.js+1-1
...@@ -18,7 +18,7 @@ hot.load("node:repl").start({...@@ -18,7 +18,7 @@ hot.load("node:repl").start({
18 .catch((err) => {18 .catch((err) => {
19 // TODO: improve @paperclover/console's ability to print AggregateError19 // TODO: improve @paperclover/console's ability to print AggregateError
20 // and errors with extra random properties20 // and errors with extra random properties
21 console.error(inspect(err));21 console.error(util.inspect(err));
22 })22 })
23 .then((result) => done(null, result));23 .then((result) => done(null, result));
24 },24 },
run.js+14-3
...@@ -1,6 +1,5 @@...@@ -1,6 +1,5 @@
1// This file allows using Node.js in combination with1// This file allows using Node.js in combination with
2// all available plugins. Usage: "node run <script>"2// all available plugins. Usage: "node run <script>"
3import * as path from "node:path";
4import * as util from "node:util";3import * as util from "node:util";
5import process from "node:process";4import process from "node:process";
65
...@@ -47,9 +46,21 @@ if (process.argv[1].startsWith(import.meta.filename.slice(0, -".js".length))) {...@@ -47,9 +46,21 @@ if (process.argv[1].startsWith(import.meta.filename.slice(0, -".js".length))) {
47 console.error("usage: node run <script> [...args]");46 console.error("usage: node run <script> [...args]");
48 process.exit(1);47 process.exit(1);
49 }48 }
50 const file = path.resolve(process.argv[2]);49 let found;
50 for (const dir of ["./", "./src/", "./framework/"]) {
51 try {
52 found = hot.resolveFrom(import.meta.filename, dir + process.argv[2]);
53 break;
54 } catch (e) {
55 continue;
56 }
57 }
58 if (!found) {
59 console.error("Cannot find script: " + process.argv[2]);
60 process.exit(1);
61 }
51 process.argv = [process.argv[0], ...process.argv.slice(2)];62 process.argv = [process.argv[0], ...process.argv.slice(2)];
52 hot.load(file).main?.();63 hot.load(found).main?.();
53}64}
5465
55export { hot };66export { hot };