diff --git a/framework/fs.ts b/framework/fs.ts index 251e7cc..b6d2d61 100644 --- a/framework/fs.ts +++ b/framework/fs.ts @@ -1,10 +1,25 @@ -// default -function isScope(node, parent) { - if ((0, _index.isBlockStatement)(node) && ((0, _index.isFunction)(parent) || (0, _index.isCatchClause)(parent))) { - return false; - } - if ((0, _index.isPattern)(node) && ((0, _index.isFunction)(parent) || (0, _index.isCatchClause)(parent))) { - return true; - } - return (0, _index.isScopable)(node); +// File System APIs +import { readFileSync, writeFileSync, readdirSync, statSync, existsSync, mkdirSync as nodeMkdirSync, rmSync } from 'node:fs'; +import { readFile, writeFile, readdir, stat, mkdir as nodeMkdir, rm } from 'node:fs/promises'; + +// Re-export a mix of built-in Node.js sync+promise fs methods. +export { + readFileSync, writeFileSync, readdirSync, statSync, existsSync, rmSync , + readFile, writeFile, readdir, stat, mkdir, rm } + +export function mkdir(dir: string) { + return nodeMkdir(dir, { recursive: true }); +} + +export function mkdirSync(dir: string) { + return nodeMkdirSync(dir, { recursive: true }); +} + +export async function writeMkdir(file: string, contents: Buffer | string) { + await mkdir(path.dirname(file)); + return writeFile(file, contents); +} + +import * as path from 'node:path'; + diff --git a/framework/mime.txt b/framework/mime.txt deleted file mode 100644 index cb6b705..0000000 --- a/framework/mime.txt +++ /dev/null @@ -1,8 +0,0 @@ -.css text/css -.html text/html; charset=utf8 -.jpeg image/jpeg -.jpg image/jpeg -.js text/javascript -.json application/json -.png image/png -.txt text/plain diff --git a/framework/sitegen.tsx b/framework/sitegen.tsx index 930fbf5..3df1caf 100644 --- a/framework/sitegen.tsx +++ b/framework/sitegen.tsx @@ -1,11 +1,222 @@ -// default -class Hub { - getCode() {} - getScope() {} - addHelper() { - throw new Error("Helpers are not supported by the default hub."); - } - buildError(node, msg, Error = TypeError) { - return new Error(msg); - } +// Sitegen! Clover's static site generator, built with love. + +function main() { + return withSpinner({ + text: "Recovering State", + successText: ({ elapsed }) => + "sitegen! update in " + elapsed.toFixed(1) + "s", + failureText: () => "sitegen FAIL", + }, sitegen); } + +async function sitegen(status) { + const startTime = performance.now(); + + let root = path.resolve(import_meta.dirname, "../src"); + const join = (...sub) => path.join(root, ...sub); + const incr = new Incremental(); + const sections: Section[] = + require(path.join(root, "sections.ts")).siteSections; + const views = []; + const staticFiles = []; + let pages = []; + let scripts = []; + const backendFiles = []; + + status.text = "Scanning Project"; + for (const section of sections) { + const { root: sectionRoot } = section; + const sectionPath = (...sub) => path.join(sectionRoot, ...sub); + const rootPrefix = root === sectionRoot + ? "" + : path.relative(root, sectionRoot) + "/"; + const kinds = [ + { + dir: sectionPath("pages"), + list: pages, + prefix: "/", + exclude: [".css", ".client.ts", ".client.tsx"], + }, + { dir: sectionPath("static"), list: staticFiles, prefix: "/", ext: true }, + { dir: sectionPath("scripts"), list: scripts, prefix: rootPrefix }, + { dir: sectionPath("views"), list: views, prefix: rootPrefix }, + ]; + for (const { dir, list, prefix, exclude = [], ext = false } of kinds) { + const pages2 = fs.readDirRecOptional(dir); + page: for (const page of pages2) { + if (page.isDirectory()) continue; + for (const ext2 of exclude) { + if (page.name.endsWith(ext2)) continue page; + } + const file = path.relative(dir, page.parentPath + "/" + page.name); + const trim = ext + ? file + : file.slice(0, -path.extname(file).length).replaceAll(".", "/"); + let id = prefix + trim.replaceAll("\\", "/"); + if (prefix === "/" && id.endsWith("/index")) { + id = id.slice(0, -"/index".length) || "/"; + } + list.push({ id, file: path.join(page.parentPath, page.name) }); + } + } + let backendFile = [ + sectionPath("backend.ts"), + sectionPath("backend.tsx"), + ].find((file) => fs.existsSync(file)); + if (backendFile) backendFiles.push(backendFile); + } + scripts = scripts.filter(({ file }) => !file.match(/\.client\.[tj]sx?/)); + const globalCssPath = join("global.css"); + status.text = "Building"; + const cssOnce = new OnceMap(); + const cssQueue = new Queue({ + name: "Bundle", + fn: ([, files, theme]) => css.bundleCssFiles(files, theme), + passive: true, + getItemText: ([id]) => id, + maxJobs: 2, + }); + const ssrResults = []; + function loadSsrModule(page) { + require(page.file); + } + async function doSsrPage(page) { + const module2 = require(page.file); + const Page = module2.default; + if (!Page) { + throw new Error("Page is missing a 'default' export."); + } + const metadata = module2.meta; + if (!metadata) { + throw new Error("Page is missing 'meta' attribute with a title."); + } + const theme = { + bg: "#fff", + fg: "#050505", + primary: "#2e7dab", + ...module2.theme, + }; + const renderedMetaPromise = Promise.resolve( + typeof metadata === "function" ? metadata({ ssr: true }) : metadata, + ).then((m) => meta.resolveAndRenderMetadata(m)); + const cssImports = [globalCssPath, ...hot.getCssImports(page.file)]; + const cssPromise = cssOnce.get( + cssImports.join(":") + JSON.stringify(theme), + () => cssQueue.add([page.id, cssImports, theme]), + ); + const sitegenApi = sg.initRender(); + const body = await (0, import_ssr.ssrAsync)( + /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Page, {}), + { + sitegen: sitegenApi, + }, + ); + const inlineCss = await cssPromise; + const renderedMeta = await renderedMetaPromise; + if (!renderedMeta.includes("
- this place is sacred, but dangerous. i have to keep visitors to an - absolute minimum; you'll get dust on all the artifacts. -
-- by entering our museum, you agree not to use your camera. flash off - isn't enough; the bits and bytes are alergic even to a camera's - sensor -
-- (in english: please do not store downloads after you're done viewing - them) -
-- welcome to the archive. if this is your first time here, i recommend - starting in '2017' and going - chronologically from there. however, there is truly no wrong way to - explore. -
-- note that there is a blanket trigger warning for everything in this - archive: while there is nothing visually offensive, some portions of - the text and emotions conveyed through this may hit extremely hard. - you are warned. -
-- all file dates are real. at least as real as i could figure out. - when i moved data across drives over my years, i accidentally had a - few points where i stamped over all the dates with the day that - moved the files. even fucked it up a final time in february 2025, - while in the process of unfucking things. -
-- thankfully, my past self knew i'd want to assemble this kind of - site, and because of that they were crazy about storing the dates of - things inside of html, json/yaml files, and even in fucking - databases. i'm glad it was all stored though, but jeez what a nerd. -
-- a few files were touched up for privacy, or otherwise re-encoded. - some of them i added extra metadata. -
-- from the bottom of my heart: i hope you enjoy. it has been a - nightmare putting this all together. technically and emotionally - speaking. i'm glad we can put this all behind us, mark it as - completed, and get started with the good shit. -
-
- love,
clo
-
- start here -> 2017 -
-today is my 21st birthday. april 30th, 2025.
-it's been nearly six months starting hormones.
-sometimes i feel great,
-sometimes i get dysphoria.
-with the walls around me gone
-that shit hits way harder than it did before.
-ugh..
-i'm glad the pain i felt is now explained,
-but now rendered in high definition.
-the smallest strands of hair on my face and belly act
-as sharpened nails to pierce my soul.
- -it's all a pathway to better days; the sun had risen.
-one little step at a time for both of us.
-today i quit my job. free falling, it feels so weird.
-like sky diving.
-the only thing i feel is cold wind.
-the only thing i see is everything,
-and it's beautiful.
-i have a month of falling before the parachute activates,
-gonna spend as much time of it on art as i can.
-that was, after all, my life plan:
-i wanted to make art, all the time,
-for everyone.
- -then you see what happened
-to the world and the internet.
-i never really got to live through that golden age,
-it probably sucked back then too.
-but now the big sites definitely stopped being fun.
-they slide their cold hands up my body
-and feel me around. it's unwelcoming, and
-inconsiderate to how sensitive my skin is.
-i'm so fucking glad i broke up with YouTube
-and their devilish friends.
-my NAS is at 5 / 24 TB
-and probably wont fill for the next decade.
- -it took 2 months for me to notice my body changed.
-that day was really nice, but it hurt a lot.
-a sharp, satisfying pain in my chest gave me life.
-learned new instincts for my arms
-so they'd stop poking my new shape.
-when i look at my face
-it's like a different person.
-she was the same as before, but completely new.
-something changed
-or i'm now used to seeing what makes me smile.
-regardless, whatever i see in the mirror, i smile.
-and, i don't hear that old name much anymore
-aside from nightmares. and you'll never repeat it, ok?
-okay.
- -been playing 'new canaan' by 'bill wurtz' on loop
-in the background.
-it kinda just feels right.
-especially when that verse near the end comes on.
- -more people have been allowed to visit me.
-my apartment used to be just for me,
-but the more i felt like a person
-the more i felt like having others over.
-still have to decorate and clean it a little,
-but it isn't a job to do alone.
-we dragged a giant a rug across the city one day,
-and it felt was like anything was possible.
-sometimes i have ten people visit in a day,
-or sometimes i focus my little eyes on just one.
-i never really know what i want to do
-until the time actually comes.
- -{/* FILIP */} -i think about the times i was by the water with you.
-the sun setting warmly, icy air fell on our shoulders.
-{/* NATALIE */} -and how we walked up to the top of that hill,
-you picked up and disposed a nail on the ground,
-walking the city thru places i've never been.
-{/* BEN */} -or hiking through the park talking about compilers,
-tiring me out until i'd fall asleep in your arms.
-{/* ELENA */} -and the way you held on to my hand as i woke up,
-noticing how i was trying to hide nightmare's tears.
- -{/* HIGH SCHOOL */} -i remember we were yelling lyrics loudly,
-out of key yet cheered on because it was fun.
-{/* ADVAITH/NATALIE */} -and when we all toured the big corporate office,
-{/* AYU/HARRIS */} -then snuck in to some startup's office after hours;
-i don't remember what movie we watched.
-{/* COLLEGE, DAY 1 IN EV's ROOM */} -i remember laying on the bunk bed,
-while the rest played a card game.
-{/* MEGHAN/MORE */} -with us all laying on the rug, staring at the TV
-as the ending twist to that show was revealed.
- -all the moments i cherish,
-i love because it was always me.
-i didn't have to pretend,
-even if i didn't know who i was at the time.
-you all were there. for me.
- -i don't want to pretend any more
-i want to be myself. for everyone.
- -oh, the song ended. i thought it was on loop?
-it's late... can hear the crickets...
-and i can almost see the moon... mmmm...
-...nah, too much light pollution.
- -one day. one day.
- -before i go, i want to show the uncensored version of "journal about a girl", because i can trust you at least. keep in mind, i think you're one of the first people to ever see this.
---journal - 2024-09-14
-been at HackMIT today on behalf of the company. it's fun. me and zack were running around looking for people that might be good hires. he had this magic arbitrary criteria to tell "oh this person is probably cracked let's talk to them" and we go to the first one. they were a nerd, perfect. they seemed to be extremely talented with some extreme software projects.
-
-okay.. oof... its still clouding my mind
-i cant shake that feeling awayhold on...
-at some point they open one of their profiles to navigate to some code, and it displays for a couple of seconds: "pronouns: she/they". i don't actually know anything about this person, but it was my perception that she is trans. their appearance, physique, and age felt similar to me, which tends makes people think you are male.
-but... she was having fun being herself. being a legend of identity and of her skill in computer science. winning the physics major. making cool shit at the hackathon, and probably in life. my perception of her was the exact essence of who i myself wanted to be. i was jealous of her life.
-i tried hard to avoid a breakdown. success. but i was feeling distant. the next hour or so was disorienting, trying not to think about it too hard. i think there was one possibly interesting person we talked to. i don't remember any of the other conversations. they were not important. but i couldn't think through them regardless.
-later, i decided to read some of her code. i either have a huge dislike towards the Rust programming language and/or it was not high quality code. welp, so just is a person studying. my perception was just a perception, inaccurate but impacting. i know i need to become myself, whoever that is. otherwise, i'm just going to feel this shit at higher doses. i think about this every day, and the amount of time i feel being consumed by these problems only grows.
-getting through it all is a lonely feeling. not because no one is around, but because i am isolated emotionally. i know other people hit these feelings, but we all are too afraid to speak up, and it's all lonely.
-waiting on a reply from someone from healthcare. it'll be slow, but it will be okay.
-
-i've learned that even when i feel alone, it doesn't have to feel lonely. i know it's hard, dear. i know it's scary. but i promise it's possible. we're all in this together. struggling together. sacrificing together. we dedicate our lives to each you, and our art for everyone. -
- -
- and then we knew,
- just like paper airplanes: that we could fly...
-
- fin. -
-f.kind === MediaFile.Kind.file), - ), - }} - >- ); -} - -function ChatView({ file }: { file: MediaFile }) { - const contents = file.contents; - return ( -
- .blend
files can be open in{" "}
-
- Blender
-
- .
-
- .{ext}
files are{" "}
-
- Fusion
- {" "}
- {name}.
-
- Blackmagic Fusion is paid-only since mid-2019 (the "Studio" version). - The last free version is Fusion 9.0.2, which exists for MacOS, Linux, - and Microsoft Windows on{" "} - - Blackmagic Design's support page - - . -
-- Alternatively, you can use DaVinci Resolve, however I do not recommend - it as it is very unstable, at least as of when I last tried version 18. -
- > - ); -} -function DownloadView({ file }: { file: MediaFile }) { - const help = helptexts[path.extname(file.basename)]; - return help - ? ( - <> - {help} -- File download:{" "} - - {file.basename} - - . -
- > - ) - : ( - <> -
- No built-in viewer for {path.extname(file.basename)}
{" "}
- files.
-
- Instead, you may download it:{" "} - - {file.basename} - - . -
- > - ); -} - -function DownloadViewTooBig({ file }: { file: MediaFile }) { - return ( -
- This file is too large to view in the browser right now (
- {formatSize(file.size)}
). Later, this file viewer will
- properly process video files for web streaming.
-
- Instead, you may download it:{" "} - - {file.basename} - - . -
-
- This file has not been pre-processed for code highlighting. This either
- means it is not a valid UTF-8 text file (.ts
{" "}
- can be TypeScript or an MPEG "Transport Stream"), or there is a bug in
- the file indexing logic.
-
- Instead, you may download it:{" "} - - {file.basename} - - . -
-