├── lib ├── proxy.ts ├── constants.ts ├── utils │ ├── non-nullable.ts │ ├── fs.ts │ ├── url.ts │ └── import-map.ts ├── react │ ├── context.js │ ├── server.js │ └── mod.ts ├── build │ ├── plugins │ │ ├── vercel.ts │ │ └── netlify.ts │ ├── assert.ts │ └── deps.ts ├── create │ ├── common │ │ ├── config.ts │ │ ├── content │ │ │ ├── index.ts │ │ │ ├── style.ts │ │ │ ├── build.ts │ │ │ └── denoConfig.ts │ │ ├── ask.ts │ │ └── printer.ts │ └── modules │ │ └── trpc.ts ├── context │ ├── server.ts │ ├── env.ts │ ├── asset.ts │ └── serverInsertedHtml.ts ├── renderer.ts ├── dev │ └── ensureMinDenoVersion.ts └── handler.ts ├── app ├── .gitignore ├── components │ └── Test.tsx ├── client.tsx ├── deno.json └── app.tsx ├── .gitattributes ├── examples ├── basic │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ └── style.css │ ├── .vscode │ │ └── settings.json │ ├── .env │ ├── client.tsx │ ├── deno.json │ ├── build.ts │ ├── importMap.json │ └── server.test.ts ├── with-csr │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ └── style.css │ ├── client.tsx │ ├── deno.json │ ├── importMap.json │ ├── build.ts │ ├── server.test.ts │ ├── src │ │ └── app.tsx │ └── server.tsx ├── with-esm │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── client.js │ ├── deno.json │ ├── build.js │ ├── importMap.json │ ├── src │ │ └── app.js │ └── server.js ├── with-fly-io │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── Dockerfile │ ├── client.tsx │ ├── deno.json │ ├── importMap.json │ ├── src │ │ └── app.tsx │ ├── build.ts │ ├── server.tsx │ └── fly.toml ├── with-netlify-(WIP) │ ├── .gitignore │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ └── style.css │ ├── README.md │ ├── client.tsx │ ├── deno.json │ ├── importMap.json │ ├── build.ts │ ├── server.tsx │ └── src │ │ └── app.tsx ├── with-preact │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ └── style.css │ ├── client.tsx │ ├── deno.json │ ├── importMap.json │ ├── build.ts │ ├── server.tsx │ └── src │ │ ├── app.tsx │ │ └── Counter.tsx ├── with-trpc │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ └── style.css │ ├── src │ │ ├── trpc │ │ │ ├── trpc.ts │ │ │ └── client.tsx │ │ ├── server │ │ │ └── router.ts │ │ ├── query-client.tsx │ │ └── app.tsx │ ├── client.tsx │ ├── deno.json │ ├── build.ts │ └── importMap.json ├── with-twind │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── src │ │ ├── post.tsx │ │ ├── twind.config.js │ │ ├── twind.ts │ │ └── app.tsx │ ├── client.tsx │ ├── deno.json │ ├── build.ts │ ├── importMap.json │ └── server.tsx ├── with-unocss │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── .vscode │ │ └── settings.json │ ├── client.tsx │ ├── deno.json │ ├── importMap.json │ ├── build.ts │ ├── server.test.ts │ ├── server.tsx │ ├── dev.ts │ └── src │ │ └── app.tsx ├── with-wouter │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── src │ │ ├── pages │ │ │ ├── About.tsx │ │ │ └── Home.tsx │ │ ├── app.tsx │ │ └── context │ │ │ └── SearchParams.tsx │ ├── deno.json │ ├── client.tsx │ ├── build.ts │ ├── importMap.json │ ├── server.test.ts │ └── server.tsx ├── with-emotion │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── client.tsx │ ├── deno.json │ ├── build.ts │ ├── src │ │ └── app.tsx │ ├── importMap.json │ ├── server.tsx │ └── server │ │ └── emotion.ts ├── with-islands │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ └── style.css │ ├── deno.json │ ├── importMap.json │ ├── build.ts │ ├── server.tsx │ └── src │ │ └── Counter.tsx ├── with-service-worker │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ └── style.css │ ├── client.tsx │ ├── deno.json │ ├── importMap.json │ ├── server.tsx │ └── src │ │ └── app.tsx ├── with-stitches │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── client.tsx │ ├── deno.json │ ├── build.ts │ ├── importMap.json │ ├── src │ │ ├── theme.tsx │ │ ├── stitches.config.ts │ │ └── app.tsx │ └── server.tsx ├── ultra-website │ ├── content │ │ └── docs │ │ │ ├── use-env.mdx │ │ │ ├── use-async.mdx │ │ │ ├── use-island.mdx │ │ │ ├── use-server-context.mdx │ │ │ ├── use-server-inserted-html.mdx │ │ │ ├── use-preload.mdx │ │ │ ├── hooks.mdx │ │ │ ├── routing.mdx │ │ │ ├── middleware.mdx │ │ │ ├── fly.mdx │ │ │ ├── prerequisites.mdx │ │ │ ├── data-fetching.mdx │ │ │ ├── code-splitting.mdx │ │ │ ├── styling.mdx │ │ │ └── deno-deploy.mdx │ ├── public │ │ ├── robots.txt │ │ ├── beast.webp │ │ ├── orcs.webp │ │ ├── share.jpg │ │ ├── share.webp │ │ ├── favicon.ico │ │ ├── grid_1.webp │ │ ├── grid_2.webp │ │ ├── archangel.webp │ │ ├── paradise.webp │ │ ├── styles │ │ │ ├── fancy.woff2 │ │ │ └── SG-Linear.woff2 │ │ └── ultra.svg │ ├── README.md │ ├── src │ │ ├── components │ │ │ ├── Docs.tsx │ │ │ ├── HotTip.tsx │ │ │ ├── Philosophy.tsx │ │ │ ├── ModuleSource.tsx │ │ │ ├── Github.tsx │ │ │ └── Home.tsx │ │ └── api │ │ │ └── github.ts │ ├── client.tsx │ ├── dev.ts │ ├── deno.json │ ├── build.ts │ ├── server.tsx │ └── importMap.json ├── with-api-routes │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── client.tsx │ ├── deno.json │ ├── importMap.json │ ├── build.ts │ └── src │ │ └── app.tsx ├── with-earthstar │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── client.tsx │ ├── deno.json │ ├── build.ts │ ├── importMap.json │ └── server.tsx ├── with-react-query │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── .vscode │ │ └── settings.json │ ├── src │ │ ├── query-client.ts │ │ ├── slow-todo.tsx │ │ ├── todo.tsx │ │ ├── app.tsx │ │ └── hooks │ │ │ └── useDehydrateReactQuery.tsx │ ├── deno.json │ ├── build.ts │ ├── client.tsx │ └── importMap.json ├── with-use-gesture │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── client.tsx │ ├── deno.json │ ├── build.ts │ ├── importMap.json │ ├── server.tsx │ └── src │ │ └── app.tsx ├── with-mdx │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ └── grid_1.webp │ ├── .gitignore │ ├── content │ │ └── docs.mdx │ ├── client.tsx │ ├── README.md │ ├── dev.ts │ ├── deno.json │ ├── importMap.json │ ├── build.ts │ ├── server.tsx │ ├── src │ │ └── app.tsx │ └── mdx.ts ├── with-react-router │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── .vscode │ │ └── settings.json │ ├── src │ │ ├── pages │ │ │ ├── About.tsx │ │ │ └── Home.tsx │ │ ├── layouts │ │ │ └── DefaultLayout.tsx │ │ └── app.tsx │ ├── client.tsx │ ├── deno.json │ ├── build.ts │ ├── importMap.json │ └── server.tsx ├── lite │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── importMap.json │ ├── server.tsx │ └── app.tsx ├── react-experimental │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ └── style.css │ ├── README.md │ ├── client.tsx │ ├── deno.json │ ├── build.ts │ ├── importMap.json │ └── server.tsx ├── with-react-helmet-async │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ └── favicon.ico │ ├── client.tsx │ ├── deno.json │ ├── build.ts │ ├── importMap.json │ └── src │ │ └── app.tsx ├── with-react-three-fiber │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ └── style.css │ ├── .vscode │ │ └── settings.json │ ├── client.tsx │ ├── deno.json │ ├── build.ts │ ├── importMap.json │ └── server.tsx ├── bogus-marketing-or-blog │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ └── style.css │ ├── importMap.json │ ├── README.md │ ├── build.ts │ ├── deno.json │ ├── server.tsx │ └── src │ │ └── app.tsx └── README.md ├── test ├── fixture │ ├── README.md │ ├── public │ │ ├── robots.txt │ │ ├── share.webp │ │ ├── favicon.ico │ │ └── style.css │ ├── client.foo.tsx │ ├── theme.ts │ ├── src │ │ ├── trpc │ │ │ ├── trpc.ts │ │ │ └── client.tsx │ │ ├── hooks │ │ │ └── useTw.ts │ │ ├── components │ │ │ └── Post.tsx │ │ ├── server │ │ │ └── router.ts │ │ ├── context │ │ │ └── twind.tsx │ │ └── query-client.tsx │ ├── client.tsx │ ├── deno.json │ └── importMap.json ├── fixture.ts └── unit │ ├── use-env.test.tsx │ ├── use-server-inserted-html.test.tsx │ └── use-async.test.tsx ├── .github ├── FUNDING.yml └── workflows │ ├── basic-deno-deploy.yml │ └── www-deno-deploy.yml ├── stream.ts ├── version.ts ├── server.ts ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── hooks ├── asset-context.js ├── server-context.js ├── island-context.js ├── server-inserted-html-context.js ├── data-stream-context.js ├── env-context.js ├── use-island.d.ts ├── use-mounted-state.js ├── use-env.js ├── use-server-inserted-html.js ├── use-preload.js ├── use-server-context.js ├── use-asset.js └── use-island.js ├── .gitignore ├── tools └── test-examples.ts ├── LICENSE ├── hydrate.js └── .devcontainer └── devcontainer.json /lib/proxy.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /ultra -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /examples/basic/README.md: -------------------------------------------------------------------------------- 1 | # basic 2 | -------------------------------------------------------------------------------- /test/fixture/README.md: -------------------------------------------------------------------------------- 1 | # basic 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [mashaal] 2 | -------------------------------------------------------------------------------- /examples/with-csr/README.md: -------------------------------------------------------------------------------- 1 | # with-csr 2 | -------------------------------------------------------------------------------- /examples/with-esm/README.md: -------------------------------------------------------------------------------- 1 | # with-esm 2 | -------------------------------------------------------------------------------- /examples/with-fly-io/README.md: -------------------------------------------------------------------------------- 1 | # with-fly-io 2 | -------------------------------------------------------------------------------- /examples/with-netlify-(WIP)/.gitignore: -------------------------------------------------------------------------------- 1 | .netlify -------------------------------------------------------------------------------- /examples/with-preact/README.md: -------------------------------------------------------------------------------- 1 | # with-preact 2 | -------------------------------------------------------------------------------- /examples/with-trpc/README.md: -------------------------------------------------------------------------------- 1 | # with-trpc 2 | -------------------------------------------------------------------------------- /examples/with-twind/README.md: -------------------------------------------------------------------------------- 1 | # with-twind 2 | -------------------------------------------------------------------------------- /examples/with-unocss/README.md: -------------------------------------------------------------------------------- 1 | # with-unocss 2 | -------------------------------------------------------------------------------- /examples/with-wouter/README.md: -------------------------------------------------------------------------------- 1 | # with-wouter 2 | -------------------------------------------------------------------------------- /examples/with-emotion/README.md: -------------------------------------------------------------------------------- 1 | # with-emotion 2 | -------------------------------------------------------------------------------- /examples/with-islands/README.md: -------------------------------------------------------------------------------- 1 | # with-islands 2 | -------------------------------------------------------------------------------- /examples/with-service-worker/README.md: -------------------------------------------------------------------------------- 1 | # basic 2 | -------------------------------------------------------------------------------- /examples/with-stitches/README.md: -------------------------------------------------------------------------------- 1 | # with-stitches 2 | -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/use-env.mdx: -------------------------------------------------------------------------------- 1 | # useEnv -------------------------------------------------------------------------------- /examples/with-api-routes/README.md: -------------------------------------------------------------------------------- 1 | # with-api-routes 2 | -------------------------------------------------------------------------------- /examples/with-earthstar/README.md: -------------------------------------------------------------------------------- 1 | # with-earthstar 2 | -------------------------------------------------------------------------------- /examples/with-react-query/README.md: -------------------------------------------------------------------------------- 1 | # with-react-query 2 | -------------------------------------------------------------------------------- /examples/with-use-gesture/README.md: -------------------------------------------------------------------------------- 1 | # with-use-gesture 2 | -------------------------------------------------------------------------------- /test/fixture/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/basic/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/use-async.mdx: -------------------------------------------------------------------------------- 1 | # useAsync -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/use-island.mdx: -------------------------------------------------------------------------------- 1 | # useIsland -------------------------------------------------------------------------------- /examples/with-csr/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-esm/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-mdx/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-react-router/README.md: -------------------------------------------------------------------------------- 1 | # with-react-router 2 | -------------------------------------------------------------------------------- /examples/ultra-website/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-emotion/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-fly-io/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-islands/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-mdx/.gitignore: -------------------------------------------------------------------------------- 1 | # ignore generated js 2 | src/content/ -------------------------------------------------------------------------------- /examples/with-preact/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-stitches/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-trpc/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-twind/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-unocss/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-wouter/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/lite/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true 3 | } 4 | -------------------------------------------------------------------------------- /examples/react-experimental/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-api-routes/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-earthstar/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-netlify-(WIP)/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-react-helmet-async/README.md: -------------------------------------------------------------------------------- 1 | # with-react-helmet-async 2 | -------------------------------------------------------------------------------- /examples/with-react-query/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-react-router/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-react-three-fiber/README.md: -------------------------------------------------------------------------------- 1 | # with-react-three-fiber 2 | -------------------------------------------------------------------------------- /examples/with-use-gesture/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/basic/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true 3 | } 4 | -------------------------------------------------------------------------------- /examples/bogus-marketing-or-blog/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/use-server-context.mdx: -------------------------------------------------------------------------------- 1 | # useServerContext -------------------------------------------------------------------------------- /examples/with-react-helmet-async/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-react-three-fiber/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/with-service-worker/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /examples/ultra-website/README.md: -------------------------------------------------------------------------------- 1 | # [https://ultrajs.dev](https://ultrajs.dev) 2 | -------------------------------------------------------------------------------- /examples/with-unocss/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true 3 | } 4 | -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/use-server-inserted-html.mdx: -------------------------------------------------------------------------------- 1 | # useServerInsertedHTML -------------------------------------------------------------------------------- /examples/with-react-query/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true 3 | } 4 | -------------------------------------------------------------------------------- /examples/with-react-three-fiber/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true 3 | } 4 | -------------------------------------------------------------------------------- /app/components/Test.tsx: -------------------------------------------------------------------------------- 1 | export default function Test() { 2 | return
Test
; 3 | } 4 | -------------------------------------------------------------------------------- /examples/with-netlify-(WIP)/README.md: -------------------------------------------------------------------------------- 1 | # with-netlify 2 | 3 | > Warning: This is not ready to use. 4 | -------------------------------------------------------------------------------- /examples/with-mdx/content/docs.mdx: -------------------------------------------------------------------------------- 1 | # hello with mdx 2 | 3 | mm 4 | 5 | ![image description](/grid_1.webp) -------------------------------------------------------------------------------- /examples/with-react-router/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.unstable": true 4 | } 5 | -------------------------------------------------------------------------------- /stream.ts: -------------------------------------------------------------------------------- 1 | export { 2 | createHeadInsertionTransformStream, 3 | createTransformStream, 4 | } from "./lib/stream.ts"; 5 | -------------------------------------------------------------------------------- /test/fixture/public/share.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/test/fixture/public/share.webp -------------------------------------------------------------------------------- /examples/react-experimental/README.md: -------------------------------------------------------------------------------- 1 | # react-experimental 2 | 3 | Testing Ultra using the experimental build of React. 4 | -------------------------------------------------------------------------------- /test/fixture/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/test/fixture/public/favicon.ico -------------------------------------------------------------------------------- /version.ts: -------------------------------------------------------------------------------- 1 | /* Do not set this manually, run tools/patch.ts if releasing a new version */ 2 | export const VERSION = "2.3.8"; 3 | -------------------------------------------------------------------------------- /examples/basic/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/basic/public/favicon.ico -------------------------------------------------------------------------------- /lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const ULTRA_COMPILER_PATH = "/_ultra/compiler"; 2 | export const ULTRA_STATIC_PATH = "/_ultra/static"; 3 | -------------------------------------------------------------------------------- /examples/basic/.env: -------------------------------------------------------------------------------- 1 | # Example of defining environment variables 2 | # They must be prefixed with `ULTRA_PUBLIC_` 3 | ULTRA_PUBLIC_FOO=bar -------------------------------------------------------------------------------- /examples/with-csr/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-csr/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-esm/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-esm/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-fly-io/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM denoland/deno:1.24.3 2 | 3 | WORKDIR /app 4 | COPY .ultra /app 5 | 6 | CMD ["deno", "task", "start"] -------------------------------------------------------------------------------- /examples/with-mdx/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-mdx/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-mdx/public/grid_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-mdx/public/grid_1.webp -------------------------------------------------------------------------------- /examples/with-trpc/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-trpc/public/favicon.ico -------------------------------------------------------------------------------- /server.ts: -------------------------------------------------------------------------------- 1 | export { createRouter, createServer } from "./lib/server.ts"; 2 | export type { Context, StatusCode } from "./lib/types.ts"; 3 | -------------------------------------------------------------------------------- /examples/basic/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(document, ); 5 | -------------------------------------------------------------------------------- /examples/ultra-website/public/beast.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/ultra-website/public/beast.webp -------------------------------------------------------------------------------- /examples/ultra-website/public/orcs.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/ultra-website/public/orcs.webp -------------------------------------------------------------------------------- /examples/ultra-website/public/share.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/ultra-website/public/share.jpg -------------------------------------------------------------------------------- /examples/ultra-website/public/share.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/ultra-website/public/share.webp -------------------------------------------------------------------------------- /examples/with-emotion/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-emotion/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-fly-io/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-fly-io/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-islands/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-islands/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-preact/client.tsx: -------------------------------------------------------------------------------- 1 | import { hydrate } from "preact"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(, document); 5 | -------------------------------------------------------------------------------- /examples/with-preact/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-preact/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-twind/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-twind/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-unocss/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-unocss/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-wouter/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-wouter/public/favicon.ico -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "denoland.vscode-deno", 4 | "GitHub.vscode-pull-request-github" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /examples/ultra-website/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/ultra-website/public/favicon.ico -------------------------------------------------------------------------------- /examples/ultra-website/public/grid_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/ultra-website/public/grid_1.webp -------------------------------------------------------------------------------- /examples/ultra-website/public/grid_2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/ultra-website/public/grid_2.webp -------------------------------------------------------------------------------- /examples/with-earthstar/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-earthstar/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-mdx/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(document, ); 5 | -------------------------------------------------------------------------------- /examples/with-stitches/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-stitches/public/favicon.ico -------------------------------------------------------------------------------- /test/fixture/client.foo.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | 3 | hydrate( 4 | document, 5 |
Something else!
, 6 | ); 7 | -------------------------------------------------------------------------------- /examples/ultra-website/public/archangel.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/ultra-website/public/archangel.webp -------------------------------------------------------------------------------- /examples/ultra-website/public/paradise.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/ultra-website/public/paradise.webp -------------------------------------------------------------------------------- /examples/with-api-routes/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(document, ); 5 | -------------------------------------------------------------------------------- /examples/with-api-routes/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-api-routes/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-earthstar/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(document, ); 5 | -------------------------------------------------------------------------------- /examples/with-emotion/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(document, ); 5 | -------------------------------------------------------------------------------- /examples/with-fly-io/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(document, ); 5 | -------------------------------------------------------------------------------- /examples/with-react-query/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-react-query/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-react-router/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-react-router/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-stitches/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(document, ); 5 | -------------------------------------------------------------------------------- /examples/with-trpc/public/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 1rem; 5 | font-family: monospace; 6 | background: #ddd; 7 | } -------------------------------------------------------------------------------- /examples/with-unocss/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(document, ); 5 | -------------------------------------------------------------------------------- /examples/with-use-gesture/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-use-gesture/public/favicon.ico -------------------------------------------------------------------------------- /examples/react-experimental/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/react-experimental/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-netlify-(WIP)/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(document, ); 5 | -------------------------------------------------------------------------------- /examples/with-netlify-(WIP)/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-netlify-(WIP)/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-service-worker/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-service-worker/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-use-gesture/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(document, ); 5 | -------------------------------------------------------------------------------- /lib/utils/non-nullable.ts: -------------------------------------------------------------------------------- 1 | export function nonNullable(value: T): value is NonNullable { 2 | return value !== null && value !== undefined; 3 | } 4 | -------------------------------------------------------------------------------- /examples/ultra-website/public/styles/fancy.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/ultra-website/public/styles/fancy.woff2 -------------------------------------------------------------------------------- /examples/with-react-three-fiber/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(document, ); 5 | -------------------------------------------------------------------------------- /examples/with-react-three-fiber/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-react-three-fiber/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-wouter/src/pages/About.tsx: -------------------------------------------------------------------------------- 1 | export default function AboutPage() { 2 | return ( 3 |
4 | About page 5 |
6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /examples/with-wouter/src/pages/Home.tsx: -------------------------------------------------------------------------------- 1 | export default function HomePage() { 2 | return ( 3 |
4 | Home page 5 |
6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /examples/bogus-marketing-or-blog/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/bogus-marketing-or-blog/public/favicon.ico -------------------------------------------------------------------------------- /examples/ultra-website/public/styles/SG-Linear.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/ultra-website/public/styles/SG-Linear.woff2 -------------------------------------------------------------------------------- /examples/with-react-helmet-async/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exhibitionist-digital/ultra/main/examples/with-react-helmet-async/public/favicon.ico -------------------------------------------------------------------------------- /examples/with-react-router/src/pages/About.tsx: -------------------------------------------------------------------------------- 1 | export default function AboutPage() { 2 | return ( 3 |
4 | About page 5 |
6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /examples/with-react-router/src/pages/Home.tsx: -------------------------------------------------------------------------------- 1 | export default function HomePage() { 2 | return ( 3 |
4 | Home page 5 |
6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /examples/ultra-website/src/components/Docs.tsx: -------------------------------------------------------------------------------- 1 | import Content from "../content/docs.js"; 2 | 3 | export default function Markdown() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /examples/react-experimental/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate( 5 | document, 6 | , 7 | ); 8 | -------------------------------------------------------------------------------- /examples/with-csr/client.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from "react-dom/client"; 2 | import App from "./src/app.tsx"; 3 | 4 | createRoot(document.getElementById("root")).render(); 5 | -------------------------------------------------------------------------------- /lib/react/context.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | const UltraServerContext = createContext({ 4 | importMap: {}, 5 | }); 6 | 7 | export default UltraServerContext; 8 | -------------------------------------------------------------------------------- /test/fixture/theme.ts: -------------------------------------------------------------------------------- 1 | import { ThemeConfiguration } from "twind"; 2 | 3 | export const theme: ThemeConfiguration = { 4 | fontFamily: { 5 | sans: ["monospace"], 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /examples/with-esm/client.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import hydrate from "ultra/hydrate.js"; 3 | import App from "./src/app.js"; 4 | 5 | hydrate(document, React.createElement(App)); 6 | -------------------------------------------------------------------------------- /test/fixture/src/trpc/trpc.ts: -------------------------------------------------------------------------------- 1 | import { createTRPCReact } from "@trpc/react-query"; 2 | import { AppRouter } from "../server/router.ts"; 3 | 4 | export const trpc = createTRPCReact(); 5 | -------------------------------------------------------------------------------- /examples/with-trpc/src/trpc/trpc.ts: -------------------------------------------------------------------------------- 1 | import { createTRPCReact } from "@trpc/react-query"; 2 | import { AppRouter } from "../server/router.ts"; 3 | 4 | export const trpc = createTRPCReact(); 5 | -------------------------------------------------------------------------------- /app/client.tsx: -------------------------------------------------------------------------------- 1 | import UltraClient, { hydrate } from "ultra/lib/react/client.js"; 2 | import App from "/~/app.tsx"; 3 | 4 | hydrate( 5 | document, 6 | 7 | 8 | , 9 | ); 10 | -------------------------------------------------------------------------------- /examples/lite/README.md: -------------------------------------------------------------------------------- 1 | # lite 2 | 3 | This is a minimal config for Ultra. 4 | 5 | To start the dev server, run 6 | 7 | ``` 8 | deno run -A --no-check --watch --import-map=importMap.json ./server.tsx 9 | ``` 10 | -------------------------------------------------------------------------------- /lib/build/plugins/vercel.ts: -------------------------------------------------------------------------------- 1 | import type { BuildPlugin } from "../types.ts"; 2 | 3 | export const vercel: BuildPlugin = { 4 | name: "vercel", 5 | onBuild(_builder, _result) { 6 | throw new Error("TODO"); 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /hooks/asset-context.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | /** 4 | * @type {React.Context>} 5 | */ 6 | const AssetContext = createContext(undefined); 7 | 8 | export default AssetContext; 9 | -------------------------------------------------------------------------------- /lib/build/plugins/netlify.ts: -------------------------------------------------------------------------------- 1 | import type { BuildPlugin } from "../types.ts"; 2 | 3 | export const netlify: BuildPlugin = { 4 | name: "netlify-edge", 5 | onBuild(_builder, _result) { 6 | throw new Error("TODO"); 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /examples/ultra-website/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import { Router } from "wouter"; 3 | import App from "./src/app.tsx"; 4 | 5 | hydrate( 6 | document, 7 | 8 | 9 | , 10 | ); 11 | -------------------------------------------------------------------------------- /hooks/server-context.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | /** 4 | * @type {React.Context} 5 | */ 6 | const ServerContext = createContext(undefined); 7 | 8 | export default ServerContext; 9 | -------------------------------------------------------------------------------- /lib/utils/fs.ts: -------------------------------------------------------------------------------- 1 | import { fromFileUrl, relative } from "../deps.ts"; 2 | 3 | export function makeRelative(root: string, path: string) { 4 | return `./${ 5 | relative( 6 | root, 7 | fromFileUrl(path), 8 | ) 9 | }`; 10 | } 11 | -------------------------------------------------------------------------------- /examples/with-react-query/src/query-client.ts: -------------------------------------------------------------------------------- 1 | import { QueryClient } from "@tanstack/react-query"; 2 | 3 | export const queryClient = new QueryClient({ 4 | defaultOptions: { 5 | queries: { 6 | suspense: true, 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /examples/with-service-worker/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | 4 | hydrate(document, ); 5 | 6 | if ("serviceWorker" in navigator) { 7 | navigator.serviceWorker.register("./service-worker.js"); 8 | } 9 | -------------------------------------------------------------------------------- /examples/with-react-router/client.tsx: -------------------------------------------------------------------------------- 1 | import { BrowserRouter } from "react-router-dom"; 2 | import hydrate from "ultra/hydrate.js"; 3 | import App from "./src/app.tsx"; 4 | 5 | hydrate( 6 | document, 7 | 8 | 9 | , 10 | ); 11 | -------------------------------------------------------------------------------- /examples/with-react-helmet-async/client.tsx: -------------------------------------------------------------------------------- 1 | import { HelmetProvider } from "react-helmet-async"; 2 | import hydrate from "ultra/hydrate.js"; 3 | import App from "./src/app.tsx"; 4 | 5 | hydrate( 6 | document, 7 | 8 | 9 | , 10 | ); 11 | -------------------------------------------------------------------------------- /examples/with-trpc/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import App from "./src/app.tsx"; 3 | import { TRPCClientProvider } from "./src/trpc/client.tsx"; 4 | 5 | hydrate( 6 | document, 7 | 8 | 9 | , 10 | ); 11 | -------------------------------------------------------------------------------- /hooks/island-context.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | /** 4 | * @type {React.Context<((Component: React.ComponentType & { url: string }, props: any) => number) | undefined>} 5 | */ 6 | const IslandContext = createContext(() => {}); 7 | 8 | export default IslandContext; 9 | -------------------------------------------------------------------------------- /examples/with-twind/src/post.tsx: -------------------------------------------------------------------------------- 1 | import { tw } from "./twind.ts"; 2 | 3 | export default function Post({ color }: { color?: string }) { 4 | return ( 5 |
6 | Hello with-twind! 7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /hooks/server-inserted-html-context.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | /** 4 | * @type {React.Context React.ReactNode>)} 5 | */ 6 | const ServerInsertedHTMLContext = React.createContext( 7 | null, 8 | ); 9 | 10 | export default ServerInsertedHTMLContext; 11 | -------------------------------------------------------------------------------- /examples/ultra-website/public/ultra.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/with-mdx/README.md: -------------------------------------------------------------------------------- 1 | # with-mdx 2 | 3 | This example uses API routes with Dynamic MDX and React Query. 4 | 5 | > Note: The `--watch` command is modified to point to the directory containing 6 | > our `.mdx` files. This ensures your `.mdx` files will reload on change during 7 | > development. 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ultra 2 | deno.lock 3 | .DS_Store 4 | NOTES.md 5 | *.png 6 | **/src/content 7 | 8 | # test fixture output 9 | test/fixture/output 10 | 11 | # tools/dev.ts generated files 12 | examples/*/deno.dev.json 13 | examples/*/importMap.dev.json 14 | 15 | # Local Netlify folder 16 | .netlify 17 | -------------------------------------------------------------------------------- /test/fixture/src/hooks/useTw.ts: -------------------------------------------------------------------------------- 1 | import { type Token } from "twind"; 2 | import { useTwContext } from "../context/twind.tsx"; 3 | 4 | export function useTw() { 5 | const context = useTwContext(); 6 | return function tw(tokens?: Token | Token[]) { 7 | return context(tokens); 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /examples/with-esm/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.js", 4 | "build": "deno run -A ./build.js", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "importMap": "./importMap.json" 9 | } 10 | -------------------------------------------------------------------------------- /hooks/data-stream-context.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | /** 4 | * @template T 5 | * @type {React.Context Promise) => void>} 6 | */ 7 | const DataStreamContext = createContext( 8 | null, 9 | ); 10 | 11 | export default DataStreamContext; 12 | -------------------------------------------------------------------------------- /examples/with-twind/src/twind.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@twind/core"; 2 | import presetAutoprefix from "@twind/preset-autoprefix"; 3 | import presetTailwind from "@twind/preset-tailwind"; 4 | 5 | export default defineConfig({ 6 | theme: {}, 7 | presets: [presetAutoprefix(), presetTailwind()], 8 | }); 9 | -------------------------------------------------------------------------------- /hooks/env-context.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | 3 | /** 4 | * @type {React.Context>} 5 | */ 6 | const EnvContext = createContext( 7 | typeof Deno === "undefined" 8 | ? new Map(globalThis.__ULTRA_ENV || []) 9 | : new Map(), 10 | ); 11 | 12 | export default EnvContext; 13 | -------------------------------------------------------------------------------- /examples/ultra-website/src/components/HotTip.tsx: -------------------------------------------------------------------------------- 1 | import { PropsWithChildren } from "react"; 2 | 3 | export function HotTip({ children }: PropsWithChildren) { 4 | return ( 5 |
6 | Hot tip! 7 | {children} 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /examples/ultra-website/src/api/github.ts: -------------------------------------------------------------------------------- 1 | export async function getStarCount() { 2 | const response = await fetch( 3 | `https://api.github.com/repos/exhibitionist-digital/ultra`, 4 | ); 5 | 6 | const data = await response.json(); 7 | 8 | return { 9 | stargazers_count: data?.stargazers_count, 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /hooks/use-island.d.ts: -------------------------------------------------------------------------------- 1 | import type { ComponentType, FunctionComponent } from "react"; 2 | 3 | type HydrationStrategy = "visible" | "load" | "idle"; 4 | 5 | export default function useIsland( 6 | Component: ComponentType & { url: string }, 7 | ): FunctionComponent< 8 | T & { hydrationStrategy?: HydrationStrategy } 9 | >; 10 | -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/use-preload.mdx: -------------------------------------------------------------------------------- 1 | # usePreload 2 | 3 | This hook will insert a `` tag into the head of the server render document. During client side transitions, this won't do anything. 4 | 5 | ```tsx 6 | import usePreload from "ultra/hooks/use-preload.js"; 7 | 8 | usePreload(href) 9 | ``` 10 | 11 | -------------------------------------------------------------------------------- /examples/ultra-website/src/components/Philosophy.tsx: -------------------------------------------------------------------------------- 1 | import { MDXProvider } from "@mdx-js/react"; 2 | 3 | import Content from "../content/philosophy.js"; 4 | 5 | export default function Markdown() { 6 | return ( 7 | 8 |
9 | 10 |
11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-twind/client.tsx: -------------------------------------------------------------------------------- 1 | import { setup } from "@twind/core"; 2 | import hydrate from "ultra/hydrate.js"; 3 | import App from "./src/app.tsx"; 4 | import { sheet } from "./src/twind.ts"; 5 | import config from "./src/twind.config.js"; 6 | 7 | //@ts-ignore twind types issue 8 | setup(config, sheet); 9 | 10 | hydrate(document, ); 11 | -------------------------------------------------------------------------------- /lib/create/common/config.ts: -------------------------------------------------------------------------------- 1 | export type Libraries = 2 | | "twind" 3 | | "stitches" 4 | | "react-helmet-async" 5 | | "react-query" 6 | | "react-router" 7 | | "wouter" 8 | | "trpc" 9 | | "none"; 10 | 11 | export interface Config { 12 | name: string; 13 | ts: boolean; 14 | cwd: string; 15 | includes: Libraries[]; 16 | } 17 | -------------------------------------------------------------------------------- /examples/bogus-marketing-or-blog/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/": "https://esm.sh/v122/react-dom@18.2.0/", 7 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/with-mdx/dev.ts: -------------------------------------------------------------------------------- 1 | import { compile } from "./mdx.ts"; 2 | 3 | await compile("./content"); 4 | 5 | /** 6 | * Now start the server 7 | */ 8 | const command = new Deno.Command(Deno.execPath(), { 9 | args: [ 10 | "run", 11 | "-A", 12 | "./server.tsx", 13 | ], 14 | }); 15 | 16 | const server = command.spawn(); 17 | 18 | await server.status; 19 | -------------------------------------------------------------------------------- /lib/create/common/content/index.ts: -------------------------------------------------------------------------------- 1 | export { appContent } from "./app.ts"; 2 | export { serverContent } from "./server.ts"; 3 | export { clientContent } from "./client.ts"; 4 | export { denoConfigContent } from "./denoConfig.ts"; 5 | export { importMapContent } from "./importMap.ts"; 6 | export { buildContent } from "./build.ts"; 7 | export { styleContent } from "./style.ts"; 8 | -------------------------------------------------------------------------------- /examples/bogus-marketing-or-blog/README.md: -------------------------------------------------------------------------------- 1 | # Bogus marketing site or blog 2 | 3 | Example with full server side routing and Zero-JS* 4 | 5 | This example has two routes: 6 | 7 | - `/` => Homepage 8 | - `*` => 404 9 | 10 | This also uses Deno's new Flash server, the fastest in the land. Requires Deno 11 | v1.25.0 12 | 13 | \* No hydration is to be found here, just server rendered HTML 14 | -------------------------------------------------------------------------------- /examples/with-esm/build.js: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.js"), 5 | serverEntrypoint: import.meta.resolve("./server.js"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | await builder.build(); 16 | -------------------------------------------------------------------------------- /examples/ultra-website/dev.ts: -------------------------------------------------------------------------------- 1 | import { compile } from "./mdx.ts"; 2 | 3 | await compile("./content"); 4 | 5 | /** 6 | * Now start the server 7 | */ 8 | const command = new Deno.Command(Deno.execPath(), { 9 | args: [ 10 | "run", 11 | "-A", 12 | "--location=http://localhost:8000", 13 | "./server.tsx", 14 | ], 15 | }); 16 | const server = command.spawn(); 17 | 18 | await server.status; 19 | -------------------------------------------------------------------------------- /examples/ultra-website/src/components/ModuleSource.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from "react"; 2 | 3 | export function ModuleSource( 4 | { filename, children }: { filename: string; children: ReactNode }, 5 | ) { 6 | return ( 7 |
8 |
{filename}.tsx
9 |
{children}
10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /examples/with-earthstar/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-emotion/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-fly-io/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-islands/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-preact/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "preact" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-stitches/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-twind/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-wouter/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /test/fixture/src/components/Post.tsx: -------------------------------------------------------------------------------- 1 | import { trpc } from "../trpc/trpc.ts"; 2 | import { useTw } from "../hooks/useTw.ts"; 3 | 4 | export default function Post({ id }: { id: number }) { 5 | const { data } = trpc.post.get.useQuery({ id }); 6 | const tw = useTw(); 7 | return ( 8 |
9 |
{data?.title}
10 | Post {data?.id} 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /examples/bogus-marketing-or-blog/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | serverEntrypoint: import.meta.resolve("./server.tsx"), 5 | }); 6 | 7 | builder.ignore([ 8 | "./README.md", 9 | "./importMap.json", 10 | "./*.dev.json", 11 | "./*.test.ts", 12 | ]); 13 | 14 | // deno-lint-ignore no-unused-vars 15 | const result = await builder.build(); 16 | -------------------------------------------------------------------------------- /examples/with-api-routes/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-netlify-(WIP)/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-react-query/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-react-router/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-service-worker/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-use-gesture/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/bogus-marketing-or-blog/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-react-helmet-async/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-wouter/client.tsx: -------------------------------------------------------------------------------- 1 | import hydrate from "ultra/hydrate.js"; 2 | import { Router } from "wouter"; 3 | import App from "./src/app.tsx"; 4 | import { SearchParamsProvider } from "./src/context/SearchParams.tsx"; 5 | 6 | hydrate( 7 | document, 8 | 9 | 10 | 11 | 12 | , 13 | ); 14 | -------------------------------------------------------------------------------- /lib/utils/url.ts: -------------------------------------------------------------------------------- 1 | import { ULTRA_COMPILER_PATH, ULTRA_STATIC_PATH } from "../constants.ts"; 2 | import { fromFileUrl, join } from "../deps.ts"; 3 | import { Mode } from "../types.ts"; 4 | 5 | export function toUltraUrl(root: string, path: string, mode: Mode) { 6 | return join( 7 | mode === "development" ? ULTRA_COMPILER_PATH : ULTRA_STATIC_PATH, 8 | fromFileUrl(path.replace(root.replaceAll("\\", "/"), "")), 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /examples/basic/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "test": "deno test --allow-all", 5 | "build": "deno run -A ./build.ts", 6 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 7 | }, 8 | "lock": false, 9 | "compilerOptions": { 10 | "jsx": "react-jsxdev", 11 | "jsxImportSource": "react" 12 | }, 13 | "importMap": "./importMap.json" 14 | } 15 | -------------------------------------------------------------------------------- /examples/lite/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/with-csr/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "test": "deno test --allow-all", 5 | "build": "deno run -A ./build.ts", 6 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 7 | }, 8 | "lock": false, 9 | "compilerOptions": { 10 | "jsx": "react-jsxdev", 11 | "jsxImportSource": "react" 12 | }, 13 | "importMap": "./importMap.json" 14 | } 15 | -------------------------------------------------------------------------------- /examples/with-csr/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/with-esm/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/with-trpc/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "test": "deno test --allow-all", 5 | "build": "deno run -A ./build.ts", 6 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 7 | }, 8 | "lock": false, 9 | "compilerOptions": { 10 | "jsx": "react-jsxdev", 11 | "jsxImportSource": "react" 12 | }, 13 | "importMap": "./importMap.json" 14 | } 15 | -------------------------------------------------------------------------------- /examples/with-fly-io/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/with-islands/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/with-mdx/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch=content,src/app.tsx,server.tsx ./dev.ts", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --location http://localhost:8000 ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/basic/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/react-experimental/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "test": "deno test --allow-all", 5 | "build": "deno run -A ./build.ts", 6 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 7 | }, 8 | "lock": false, 9 | "compilerOptions": { 10 | "jsx": "react-jsxdev", 11 | "jsxImportSource": "react" 12 | }, 13 | "importMap": "./importMap.json" 14 | } 15 | -------------------------------------------------------------------------------- /examples/with-api-routes/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/with-csr/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/with-netlify-(WIP)/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/with-react-router/src/layouts/DefaultLayout.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Outlet } from "react-router-dom"; 2 | 3 | export function DefaultLayout() { 4 | return ( 5 |
6 | 11 |
12 | 13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /examples/with-react-three-fiber/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch ./server.tsx", 4 | "test": "deno test --allow-all", 5 | "build": "deno run -A ./build.ts", 6 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 7 | }, 8 | "lock": false, 9 | "compilerOptions": { 10 | "jsx": "react-jsxdev", 11 | "jsxImportSource": "react" 12 | }, 13 | "importMap": "./importMap.json" 14 | } 15 | -------------------------------------------------------------------------------- /examples/with-service-worker/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/with-trpc/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/with-emotion/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/with-stitches/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/with-twind/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/with-wouter/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/react-experimental/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/with-api-routes/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/with-fly-io/src/app.tsx: -------------------------------------------------------------------------------- 1 | export default function App() { 2 | return ( 3 | 4 | 5 | 6 | with-fly-io 7 | 8 | 9 | 10 | 11 |
Hello with-fly-io!
12 | 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /examples/with-react-query/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/with-react-router/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/with-use-gesture/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/with-react-helmet-async/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /examples/with-react-three-fiber/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./importMap.json", 11 | "./*.dev.json", 12 | "./*.test.ts", 13 | ]); 14 | 15 | // deno-lint-ignore no-unused-vars 16 | const result = await builder.build(); 17 | -------------------------------------------------------------------------------- /hooks/use-mounted-state.js: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useRef } from "react"; 2 | 3 | /** 4 | * @returns {() => boolean} 5 | */ 6 | export default function useMountedState() { 7 | const mountedRef = useRef(false); 8 | const get = useCallback(() => mountedRef.current, []); 9 | 10 | useEffect(() => { 11 | mountedRef.current = true; 12 | 13 | return () => { 14 | mountedRef.current = false; 15 | }; 16 | }, []); 17 | 18 | return get; 19 | } 20 | -------------------------------------------------------------------------------- /examples/with-preact/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/preact@10.11.1&external=preact/compat", 4 | "preact": "https://esm.sh/v122/preact@10.11.1", 5 | "preact/": "https://esm.sh/v122/preact@10.11.1/", 6 | "react-dom": "https://esm.sh/v122/preact@10.11.1&external=preact/compat", 7 | "react-dom/": "https://esm.sh/v122/preact@10.11.1&external=preact/compat/", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/with-fly-io/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./fly.toml", 11 | "./Dockerfile", 12 | "./importMap.json", 13 | "./*.dev.json", 14 | ]); 15 | 16 | // deno-lint-ignore no-unused-vars 17 | const result = await builder.build(); 18 | -------------------------------------------------------------------------------- /lib/context/server.ts: -------------------------------------------------------------------------------- 1 | import { createElement as h } from "react"; 2 | import ServerContext from "../../hooks/server-context.js"; 3 | import type { Context } from "../types.ts"; 4 | 5 | type ServerContextProviderProps = { 6 | children: JSX.Element; 7 | context: Context | undefined; 8 | }; 9 | 10 | export function ServerContextProvider( 11 | { children, context }: ServerContextProviderProps, 12 | ) { 13 | return h(ServerContext.Provider, { value: context, children }); 14 | } 15 | -------------------------------------------------------------------------------- /examples/ultra-website/src/components/Github.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { getStarCount } from "../api/github.ts"; 3 | 4 | const href = "https://github.com/exhibitionist-digital/ultra"; 5 | const starCount = getStarCount(); 6 | 7 | export default function GitHub() { 8 | //@ts-ignore whatever 9 | const stars = React.use(starCount); 10 | return ( 11 | 12 | ★ {stars?.stargazers_count} 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /examples/with-preact/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | jsxImportSource: "preact", 7 | }); 8 | 9 | builder.ignore([ 10 | "./README.md", 11 | "./importMap.json", 12 | "./*.dev.json", 13 | "./*.test.ts", 14 | ]); 15 | 16 | // deno-lint-ignore no-unused-vars 17 | const result = await builder.build(); 18 | -------------------------------------------------------------------------------- /examples/with-react-three-fiber/public/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100vh; 4 | margin: 0; 5 | padding: 0; 6 | background: #111; 7 | color: white; 8 | text-align: center; 9 | line-height: 100vh; 10 | font-family: monospace; 11 | } 12 | 13 | canvas { 14 | height: 100vh; 15 | opacity: 0; 16 | animation-fill-mode: forwards; 17 | animation-name: fadeIn; 18 | animation-duration: 1s; 19 | } 20 | 21 | @keyframes fadeIn { 22 | to { 23 | opacity: 1; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /hooks/use-env.js: -------------------------------------------------------------------------------- 1 | import { useContext, useMemo } from "react"; 2 | import EnvContext from "./env-context.js"; 3 | 4 | /** 5 | * @param {string} name 6 | */ 7 | export default function useEnv(name) { 8 | if ( 9 | typeof Deno === "undefined" && name.startsWith("ULTRA_PUBLIC_") === false 10 | ) { 11 | throw new Error(`Attempt to access non-public env variable. ${name}`); 12 | } 13 | const context = useContext(EnvContext); 14 | return useMemo(() => context.get(name), [name]); 15 | } 16 | -------------------------------------------------------------------------------- /test/fixture/client.tsx: -------------------------------------------------------------------------------- 1 | import { cssomSheet } from "twind"; 2 | import hydrate from "ultra/hydrate.js"; 3 | import App from "./src/app.tsx"; 4 | import { TWProvider } from "./src/context/twind.tsx"; 5 | import { TRPCClientProvider } from "./src/trpc/client.tsx"; 6 | import { theme } from "./theme.ts"; 7 | 8 | hydrate( 9 | document, 10 | 11 | 12 | 13 | 14 | , 15 | ); 16 | -------------------------------------------------------------------------------- /examples/with-earthstar/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | browserEntrypoint: import.meta.resolve("./client.tsx"), 5 | serverEntrypoint: import.meta.resolve("./server.tsx"), 6 | }); 7 | 8 | builder.ignore([ 9 | "./README.md", 10 | "./fly.toml", 11 | "./DockerFile", 12 | "./importMap.json", 13 | "./*.dev.json", 14 | "./*.test.ts", 15 | ]); 16 | 17 | // deno-lint-ignore no-unused-vars 18 | const result = await builder.build(); 19 | -------------------------------------------------------------------------------- /examples/with-unocss/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check ./dev.ts", 4 | "test": "deno test --allow-all", 5 | "build": "deno run -A ./build.ts", 6 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 7 | }, 8 | "lock": false, 9 | "compilerOptions": { 10 | "jsx": "react-jsxdev", 11 | "jsxImportSource": "react", 12 | "lib": ["dom", "deno.ns", "dom.iterable", "dom.asynciterable"] 13 | }, 14 | "importMap": "./importMap.json" 15 | } 16 | -------------------------------------------------------------------------------- /examples/with-unocss/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/", 9 | "unocss/cli": "https://esm.sh/v122/@unocss/cli" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/build/assert.ts: -------------------------------------------------------------------------------- 1 | import { crayon, sprintf } from "./deps.ts"; 2 | 3 | export async function assertEntrypointExists( 4 | path: string, 5 | entrypoint: "browser" | "server", 6 | ) { 7 | try { 8 | await Deno.readFile(path); 9 | } catch (cause) { 10 | throw new Error( 11 | sprintf( 12 | "Could not find your %s entrypoint %s, please check that it exists.", 13 | entrypoint, 14 | crayon.lightBlue(path), 15 | ), 16 | { cause }, 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/basic/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/", 9 | "@ultra/qrcode/": "https://deno.land/x/qrcode@v2.0.0/" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/with-react-query/client.tsx: -------------------------------------------------------------------------------- 1 | import { Hydrate, QueryClientProvider } from "@tanstack/react-query"; 2 | import hydrate from "ultra/hydrate.js"; 3 | import App from "./src/app.tsx"; 4 | import { queryClient } from "./src/query-client.ts"; 5 | 6 | declare const __REACT_QUERY_DEHYDRATED_STATE: unknown; 7 | 8 | hydrate( 9 | document, 10 | 11 | 12 | 13 | 14 | , 15 | ); 16 | -------------------------------------------------------------------------------- /app/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/stable/react@18.2.0?dev", 4 | "react/": "https://esm.sh/stable/react@18.2.0&dev/", 5 | "react-dom": "https://esm.sh/react-dom@18.2.0?external=react&dev", 6 | "react-dom/": "https://esm.sh/react-dom@18.2.0&external=react&dev/", 7 | "ultra/": "../", 8 | "/~/": "./" 9 | }, 10 | "tasks": { 11 | "dev": "deno run -A server.tsx" 12 | }, 13 | "compilerOptions": { 14 | "jsx": "react-jsxdev", 15 | "jsxImportSource": "react" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/ultra-website/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "dev": "deno run -A --no-check --watch=content,src/components,src/app.tsx,src/hooks,src/layout,server.tsx --location=http://localhost:8000 ./dev.ts", 4 | "build": "deno run -A ./build.ts", 5 | "start": "ULTRA_MODE=production deno run -A --no-remote --location=http://localhost:8000 ./server.js" 6 | }, 7 | "lock": false, 8 | "compilerOptions": { 9 | "jsx": "react-jsxdev", 10 | "jsxImportSource": "react" 11 | }, 12 | "importMap": "./importMap.json" 13 | } 14 | -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/hooks.mdx: -------------------------------------------------------------------------------- 1 | # Hooks 2 | 3 | Ultra provides various hooks to help with the most common functionality when building your project. 4 | 5 | #### [useAsset](/docs/hooks/use-asset) 6 | 7 | #### [useAsync](/docs/hooks/use-async) 8 | 9 | #### [useEnv](/docs/hooks/use-env) 10 | 11 | #### [useIlsand](/docs/hooks/use-island) 12 | 13 | #### [usePreload](/docs/hooks/use-preload) 14 | 15 | #### [useServerContext](/docs/hooks/use-server-context) 16 | 17 | #### [useServerInsertedHTML](/docs/hooks/use-server-inserted-html) -------------------------------------------------------------------------------- /examples/with-esm/src/app.js: -------------------------------------------------------------------------------- 1 | import { createElement as h } from "react"; 2 | 3 | export default function App() { 4 | return ( 5 | h( 6 | "html", 7 | null, 8 | h( 9 | "head", 10 | null, 11 | h( 12 | "title", 13 | null, 14 | "Basic", 15 | ), 16 | ), 17 | h( 18 | "body", 19 | null, 20 | h( 21 | "div", 22 | null, 23 | "Hello with-esm!", 24 | ), 25 | ), 26 | ) 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /examples/with-stitches/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/", 9 | "@stitches/react": "https://esm.sh/v122/@stitches/react@1.2.8?external=react" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/with-react-three-fiber/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "@react-three/fiber": "https://esm.sh/v122/@react-three/fiber?external=react", 9 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/with-netlify-(WIP)/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | import { netlify } from "ultra/lib/build/plugins/netlify.ts"; 3 | 4 | const builder = createBuilder({ 5 | browserEntrypoint: import.meta.resolve("./client.tsx"), 6 | serverEntrypoint: import.meta.resolve("./server.tsx"), 7 | plugin: netlify, 8 | }); 9 | 10 | builder.ignore([ 11 | "./README.md", 12 | "./importMap.json", 13 | "./*.dev.json", 14 | "./*.test.ts", 15 | ]); 16 | 17 | // deno-lint-ignore no-unused-vars 18 | const result = await builder.build(); 19 | -------------------------------------------------------------------------------- /examples/with-react-helmet-async/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/", 9 | "react-helmet-async": "https://esm.sh/v122/react-helmet-async@1.3.0?external=react" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /hooks/use-server-inserted-html.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import ServerInsertedHTMLContext from "./server-inserted-html-context.js"; 3 | 4 | /** 5 | * @param {() => React.ReactNode} callback 6 | * @returns {void} 7 | */ 8 | export default function useServerInsertedHTML(callback) { 9 | const addInsertedServerHTMLCallback = useContext(ServerInsertedHTMLContext); 10 | // Should have no effects on client where there's no flush effects provider 11 | if (addInsertedServerHTMLCallback) { 12 | addInsertedServerHTMLCallback(callback); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/renderer.ts: -------------------------------------------------------------------------------- 1 | // See: 2 | // https://react.dev/reference/react-dom/server/renderToReadableStream#usage 3 | // https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream 4 | export interface RendererOptions { 5 | root: string | URL; 6 | render: RenderFunction; 7 | } 8 | 9 | type RenderResult = Promise | T; 10 | type ResponseLike = Response | ReadableStream | null; 11 | 12 | export type RenderFunction = ( 13 | request: Request, 14 | params?: Map, 15 | ) => RenderResult; 16 | -------------------------------------------------------------------------------- /lib/utils/import-map.ts: -------------------------------------------------------------------------------- 1 | import { type ImportMapJson, resolve, toFileUrl } from "../deps.ts"; 2 | import { Mode } from "../types.ts"; 3 | 4 | export function resolveImportMapPath(mode: Mode, root: string, path: string) { 5 | if (mode === "development") { 6 | return path; 7 | } 8 | 9 | return toFileUrl(resolve(root, "importMap.browser.json")).href; 10 | } 11 | 12 | export const readImportMap = async (path: string) => { 13 | const importMap = await Deno.readTextFile(path); 14 | // TODO: zod-check the import map 15 | return JSON.parse(importMap) as ImportMapJson; 16 | } 17 | -------------------------------------------------------------------------------- /examples/with-react-query/src/slow-todo.tsx: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | 3 | type TodoProps = { 4 | id: number; 5 | }; 6 | 7 | export default function SlowTodo({ id }: TodoProps) { 8 | const query = useQuery( 9 | ["todo", { id }], 10 | async () => { 11 | await new Promise((resolve) => setTimeout(resolve, 3000)); 12 | return fetch( 13 | `https://jsonplaceholder.typicode.com/todos/${id}`, 14 | ).then((response) => response.json()); 15 | }, 16 | ); 17 | 18 | return
{JSON.stringify(query.data, null, 2)}
; 19 | } 20 | -------------------------------------------------------------------------------- /examples/with-unocss/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | import { build } from "unocss/cli"; 3 | 4 | const builder = createBuilder({ 5 | browserEntrypoint: import.meta.resolve("./client.tsx"), 6 | serverEntrypoint: import.meta.resolve("./server.tsx"), 7 | }); 8 | 9 | builder.ignore([ 10 | "./README.md", 11 | "./importMap.json", 12 | "./*.dev.json", 13 | "./*.test.ts", 14 | ]); 15 | 16 | await build({ 17 | patterns: ["src/**/*"], 18 | outFile: "public/uno.css", 19 | }); 20 | 21 | // deno-lint-ignore no-unused-vars 22 | const result = await builder.build(); 23 | -------------------------------------------------------------------------------- /examples/with-islands/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | 3 | const builder = createBuilder({ 4 | serverEntrypoint: import.meta.resolve("./server.tsx"), 5 | }); 6 | 7 | builder.ignore([ 8 | "./README.md", 9 | "./importMap.json", 10 | "./*.dev.json", 11 | "./*.test.ts", 12 | ]); 13 | 14 | /** 15 | * Add our own browser entrypoint, since we 16 | * aren't using the default 17 | */ 18 | builder.entrypoint("browser", { 19 | path: "./src/app.tsx", 20 | target: "browser", 21 | }); 22 | 23 | // deno-lint-ignore no-unused-vars 24 | const result = await builder.build(); 25 | -------------------------------------------------------------------------------- /examples/with-mdx/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/", 9 | "@mdx-js/run": "https://esm.sh/v122/@mdx-js/mdx@2.1.3/lib/run.js", 10 | "@mdx-js/react": "https://esm.sh/v122/@mdx-js/react@2.1.3?external=react" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/react-experimental/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@0.0.0-experimental-8951c5fc9-20220915?dev", 4 | "react/": "https://esm.sh/v122/react@0.0.0-experimental-8951c5fc9-20220915/", 5 | "react-dom": "https://esm.sh/v122/react-dom@0.0.0-experimental-8951c5fc9-20220915", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@0.0.0-experimental-8951c5fc9-20220915/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@0.0.0-experimental-8951c5fc9-20220915/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/with-emotion/src/app.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | const Text = styled.div` 4 | font-size: 3em; 5 | `; 6 | 7 | export default function App() { 8 | return ( 9 | 10 | 11 | 12 | with-emotion 13 | 14 | 15 | 16 | 17 | 18 | Hello with-emotion! 19 | 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /examples/with-islands/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | }); 9 | 10 | server.get("*", async (context) => { 11 | /** 12 | * Render the request 13 | */ 14 | const result = await server.render(); 15 | 16 | return context.body(result, 200, { 17 | "content-type": "text/html", 18 | }); 19 | }); 20 | 21 | Deno.serve(server.fetch); 22 | -------------------------------------------------------------------------------- /examples/with-wouter/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/", 9 | "wouter": "https://esm.sh/v122/wouter@2.10.0?external=react", 10 | "wouter/static-location": "https://esm.sh/v122/wouter@2.10.0/static-location?external=wouter" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/react/server.js: -------------------------------------------------------------------------------- 1 | import { createElement as h, Fragment } from "react"; 2 | import UltraServerContext from "./context.js"; 3 | 4 | /** 5 | * @typedef {Object} UltraServerProps 6 | * @property {Request} [request] 7 | * @property {import('../importMap.ts').ImportMap} [importMap] 8 | * @property {import('react').ReactNode} [children] 9 | */ 10 | 11 | /** 12 | * @param {UltraServerProps} props 13 | */ 14 | export default function UltraServer({ request, importMap, children }) { 15 | return h( 16 | UltraServerContext.Provider, 17 | { value: { importMap } }, 18 | h(Fragment, undefined, children), 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /examples/ultra-website/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | import { compile } from "./mdx.ts"; 3 | 4 | await compile("./content"); 5 | 6 | const builder = createBuilder({ 7 | browserEntrypoint: import.meta.resolve("./client.tsx"), 8 | serverEntrypoint: import.meta.resolve("./server.tsx"), 9 | }); 10 | 11 | builder.ignore([ 12 | "./public/*.png", 13 | "./content/*.mdx", 14 | "./README.md", 15 | "./fly.toml", 16 | "./Dockerfile", 17 | "./dev.ts", 18 | "./importMap.json", 19 | "./*.dev.json", 20 | ]); 21 | 22 | // deno-lint-ignore no-unused-vars 23 | const result = await builder.build(); 24 | -------------------------------------------------------------------------------- /examples/with-earthstar/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "earthstar": "https://cdn.earthstar-project.org/js/earthstar.bundle.v9.3.3.js", 4 | "react-earthstar": "https://esm.sh/v122/react-earthstar?external=react,earthstar", 5 | "react": "https://esm.sh/v122/react@18.2.0?dev", 6 | "react/": "https://esm.sh/v122/react@18.2.0/", 7 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 8 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 9 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 10 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/with-use-gesture/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "@react-spring/web": "https://esm.sh/v122/@react-spring/web?external=react", 9 | "@use-gesture/react": "https://esm.sh/v122/@use-gesture/react?external=react&bundle", 10 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/routing.mdx: -------------------------------------------------------------------------------- 1 | # Routing 2 | 3 | Our favorite thing about **Ultra** is that it doesn't come with any FS BS. Choose your path, choose your router (or write your own). 4 | 5 | #### [wouter](#wouter) 6 | 7 | [https://github.com/molefrog/wouter](https://github.com/molefrog/wouter) 8 | 9 | [ultra-with-wouter](https://github.com/exhibitionist-digital/ultra/tree/main/examples/with-wouter) 10 | 11 | 12 | #### [react-router](#react-router) 13 | 14 | [https://reactrouter.com](https://reactrouter.com) 15 | 16 | [ultra-with-react-router](https://github.com/exhibitionist-digital/ultra/tree/main/examples/with-react-router) -------------------------------------------------------------------------------- /examples/with-emotion/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/", 9 | "@emotion/react": "https://esm.sh/v122/@emotion/react@11.10.0?external=react", 10 | "@emotion/styled": "https://esm.sh/v122/@emotion/styled@11.10.0?external=react,@emotion/react" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/with-mdx/build.ts: -------------------------------------------------------------------------------- 1 | import { createBuilder } from "ultra/build.ts"; 2 | import { compile } from "./mdx.ts"; 3 | 4 | const builder = createBuilder({ 5 | browserEntrypoint: import.meta.resolve("./client.tsx"), 6 | serverEntrypoint: import.meta.resolve("./server.tsx"), 7 | }); 8 | 9 | builder.ignore([ 10 | "./content/**/*", 11 | "./README.md", 12 | "./importMap.json", 13 | "./*.dev.json", 14 | "./*.test.ts", 15 | ]); 16 | 17 | /** 18 | * Compile our mdx 19 | */ 20 | await compile("./content"); 21 | builder.log.success("Compiled MDX"); 22 | 23 | // deno-lint-ignore no-unused-vars 24 | const result = await builder.build(); 25 | -------------------------------------------------------------------------------- /examples/lite/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: import.meta.resolve("./importMap.json"), 6 | browserEntrypoint: import.meta.resolve("./app.tsx"), 7 | }); 8 | 9 | server.get("*", async (context) => { 10 | /** 11 | * Render the request 12 | */ 13 | const result = await server.render(); 14 | 15 | return context.body(result, 200, { 16 | "content-type": "text/html; charset=utf-8", 17 | }); 18 | }); 19 | 20 | if (import.meta.main) { 21 | Deno.serve(server.fetch); 22 | } 23 | 24 | export default server; 25 | -------------------------------------------------------------------------------- /examples/with-react-router/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/", 9 | "react-router-dom": "https://esm.sh/v122/react-router-dom@6.3.0?external=react", 10 | "react-router-dom/server": "https://esm.sh/v122/react-router-dom@6.3.0/server?external=react" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/with-stitches/src/theme.tsx: -------------------------------------------------------------------------------- 1 | import { PropsWithChildren } from "react"; 2 | import useServerInsertedHTML from "ultra/hooks/use-server-inserted-html.js"; 3 | import { getCssText } from "./stitches.config.ts"; 4 | 5 | export function ThemeProvider({ children }: PropsWithChildren) { 6 | /** 7 | * useServerInsertedHTML will inject the returned output into the rendered stream. 8 | */ 9 | useServerInsertedHTML(() => { 10 | return ( 11 | 16 | ); 17 | }); 18 | 19 | return <>{children}; 20 | } 21 | -------------------------------------------------------------------------------- /examples/basic/public/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 1rem; 5 | font-family: monospace; 6 | background: #ddd; 7 | text-align: center; 8 | } 9 | 10 | h1 { 11 | text-align: center; 12 | margin: 1rem auto 3rem; 13 | font-size: clamp(2em, 10vw, 8em); 14 | font-weight: 400; 15 | } 16 | 17 | h1 span::before { 18 | content: '@'; 19 | animation: blink 3s infinite; 20 | } 21 | 22 | @keyframes blink { 23 | 24 | 0%, 25 | 50%, 26 | 70%, 27 | 95% { 28 | content: '@'; 29 | } 30 | 31 | 65%, 32 | 90% { 33 | content: '—'; 34 | } 35 | } 36 | 37 | p { 38 | max-width: 600px; 39 | margin: 0 auto 1em; 40 | } -------------------------------------------------------------------------------- /examples/with-csr/public/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 1rem; 5 | font-family: monospace; 6 | background: #ddd; 7 | text-align: center; 8 | } 9 | 10 | h1 { 11 | text-align: center; 12 | margin: 1rem auto 3rem; 13 | font-size: clamp(2em, 10vw, 8em); 14 | font-weight: 400; 15 | } 16 | 17 | h1 span::before { 18 | content: '@'; 19 | animation: blink 3s infinite; 20 | } 21 | 22 | @keyframes blink { 23 | 24 | 0%, 25 | 50%, 26 | 70%, 27 | 95% { 28 | content: '@'; 29 | } 30 | 31 | 65%, 32 | 90% { 33 | content: '—'; 34 | } 35 | } 36 | 37 | p { 38 | max-width: 600px; 39 | margin: 0 auto 1em; 40 | } -------------------------------------------------------------------------------- /hooks/use-preload.js: -------------------------------------------------------------------------------- 1 | import { createElement as h } from "react"; 2 | import useServerInsertedHTML from "./use-server-inserted-html.js"; 3 | 4 | /** 5 | * This hook will insert a `` tag into the head of the 6 | * server render document. During client side transitions, this won't do anything. 7 | * 8 | * @param {string} href 9 | * @param {React.LinkHTMLAttributes} props 10 | */ 11 | export default function usePreload(href, props) { 12 | useServerInsertedHTML(() => { 13 | return h("link", { 14 | rel: "preload", 15 | href, 16 | ...props, 17 | }); 18 | }); 19 | 20 | return href; 21 | } 22 | -------------------------------------------------------------------------------- /examples/with-islands/public/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 1rem; 5 | font-family: monospace; 6 | background: #ddd; 7 | text-align: center; 8 | } 9 | 10 | h1 { 11 | text-align: center; 12 | margin: 1rem auto 3rem; 13 | font-size: clamp(2em, 10vw, 8em); 14 | font-weight: 400; 15 | } 16 | 17 | h1 span::before { 18 | content: '@'; 19 | animation: blink 3s infinite; 20 | } 21 | 22 | @keyframes blink { 23 | 24 | 0%, 25 | 50%, 26 | 70%, 27 | 95% { 28 | content: '@'; 29 | } 30 | 31 | 65%, 32 | 90% { 33 | content: '—'; 34 | } 35 | } 36 | 37 | p { 38 | max-width: 600px; 39 | margin: 0 auto 1em; 40 | } -------------------------------------------------------------------------------- /examples/with-preact/public/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 1rem; 5 | font-family: monospace; 6 | background: #ddd; 7 | text-align: center; 8 | } 9 | 10 | h1 { 11 | text-align: center; 12 | margin: 1rem auto 3rem; 13 | font-size: clamp(2em, 10vw, 8em); 14 | font-weight: 400; 15 | } 16 | 17 | h1 span::before { 18 | content: '@'; 19 | animation: blink 3s infinite; 20 | } 21 | 22 | @keyframes blink { 23 | 24 | 0%, 25 | 50%, 26 | 70%, 27 | 95% { 28 | content: '@'; 29 | } 30 | 31 | 65%, 32 | 90% { 33 | content: '—'; 34 | } 35 | } 36 | 37 | p { 38 | max-width: 600px; 39 | margin: 0 auto 1em; 40 | } -------------------------------------------------------------------------------- /examples/react-experimental/public/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 1rem; 5 | font-family: monospace; 6 | background: #ddd; 7 | text-align: center; 8 | } 9 | 10 | h1 { 11 | text-align: center; 12 | margin: 1rem auto 3rem; 13 | font-size: clamp(2em, 10vw, 8em); 14 | font-weight: 400; 15 | } 16 | 17 | h1 span::before { 18 | content: '@'; 19 | animation: blink 3s infinite; 20 | } 21 | 22 | @keyframes blink { 23 | 24 | 0%, 25 | 50%, 26 | 70%, 27 | 95% { 28 | content: '@'; 29 | } 30 | 31 | 65%, 32 | 90% { 33 | content: '—'; 34 | } 35 | } 36 | 37 | p { 38 | max-width: 600px; 39 | margin: 0 auto 1em; 40 | } -------------------------------------------------------------------------------- /examples/with-netlify-(WIP)/public/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 1rem; 5 | font-family: monospace; 6 | background: #ddd; 7 | text-align: center; 8 | } 9 | 10 | h1 { 11 | text-align: center; 12 | margin: 1rem auto 3rem; 13 | font-size: clamp(2em, 10vw, 8em); 14 | font-weight: 400; 15 | } 16 | 17 | h1 span::before { 18 | content: '@'; 19 | animation: blink 3s infinite; 20 | } 21 | 22 | @keyframes blink { 23 | 24 | 0%, 25 | 50%, 26 | 70%, 27 | 95% { 28 | content: '@'; 29 | } 30 | 31 | 65%, 32 | 90% { 33 | content: '—'; 34 | } 35 | } 36 | 37 | p { 38 | max-width: 600px; 39 | margin: 0 auto 1em; 40 | } -------------------------------------------------------------------------------- /examples/bogus-marketing-or-blog/public/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 1rem; 5 | font-family: monospace; 6 | background: #ddd; 7 | text-align: center; 8 | } 9 | 10 | h1 { 11 | text-align: center; 12 | margin: 1rem auto 3rem; 13 | font-size: clamp(2em, 10vw, 8em); 14 | font-weight: 400; 15 | } 16 | 17 | h1 span::before { 18 | content: '@'; 19 | animation: blink 3s infinite; 20 | } 21 | 22 | @keyframes blink { 23 | 24 | 0%, 25 | 50%, 26 | 70%, 27 | 95% { 28 | content: '@'; 29 | } 30 | 31 | 65%, 32 | 90% { 33 | content: '—'; 34 | } 35 | } 36 | 37 | p { 38 | max-width: 600px; 39 | margin: 0 auto 1em; 40 | } -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/middleware.mdx: -------------------------------------------------------------------------------- 1 | # Middleware 2 | 3 | Since **Ultra** uses Hono under-the-covers, it includes middleware support. Here is an example snippet added to `server.tsx` that will add a 'Server' header to every response: 4 | ```js 5 | server.use('*', async (c, next) => { 6 | c.res.headers.set("Server", "Ultra Hono"); 7 | await next(); 8 | }); 9 | ``` 10 | If you look in the Dev Tools Network tab and click on localhost, you'll see this line in the Headers tab added to the Response Headers section: 11 | 12 | ```bash 13 | server: Ultra Hono 14 | ``` 15 | 16 | See the [Hono middleware docs](https://honojs.dev/docs/api/middleware/) for more details. -------------------------------------------------------------------------------- /examples/with-service-worker/public/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 1rem; 5 | font-family: monospace; 6 | background: #ddd; 7 | text-align: center; 8 | } 9 | 10 | h1 { 11 | text-align: center; 12 | margin: 1rem auto 3rem; 13 | font-size: clamp(2em, 10vw, 8em); 14 | font-weight: 400; 15 | } 16 | 17 | h1 span::before { 18 | content: '@'; 19 | animation: blink 3s infinite; 20 | } 21 | 22 | @keyframes blink { 23 | 24 | 0%, 25 | 50%, 26 | 70%, 27 | 95% { 28 | content: '@'; 29 | } 30 | 31 | 65%, 32 | 90% { 33 | content: '—'; 34 | } 35 | } 36 | 37 | p { 38 | max-width: 600px; 39 | margin: 0 auto 1em; 40 | } -------------------------------------------------------------------------------- /examples/with-mdx/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | browserEntrypoint: import.meta.resolve("./client.tsx"), 9 | }); 10 | 11 | server.get("*", async (context) => { 12 | /** 13 | * Render the request 14 | */ 15 | const result = await server.render(); 16 | 17 | return context.body(result, 200, { 18 | "content-type": "text/html", 19 | }); 20 | }); 21 | 22 | Deno.serve(server.fetch); 23 | -------------------------------------------------------------------------------- /examples/with-stitches/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | browserEntrypoint: import.meta.resolve("./client.tsx"), 9 | }); 10 | 11 | server.get("*", async (context) => { 12 | /** 13 | * Render the request 14 | */ 15 | const result = await server.render(); 16 | 17 | return context.body(result, 200, { 18 | "content-type": "text/html", 19 | }); 20 | }); 21 | 22 | Deno.serve(server.fetch); 23 | -------------------------------------------------------------------------------- /examples/with-twind/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/", 9 | "@twind/core": "https://esm.sh/v122/@twind/core@1.0.1", 10 | "@twind/preset-autoprefix": "https://esm.sh/v122/@twind/preset-autoprefix@1.0.1", 11 | "@twind/preset-tailwind": "https://esm.sh/v122/*@twind/preset-tailwind@1.0.1" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/with-service-worker/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | browserEntrypoint: import.meta.resolve("./client.tsx"), 9 | }); 10 | 11 | server.get("*", async (context) => { 12 | /** 13 | * Render the request 14 | */ 15 | const result = await server.render(); 16 | 17 | return context.body(result, 200, { 18 | "content-type": "text/html", 19 | }); 20 | }); 21 | 22 | Deno.serve(server.fetch); 23 | -------------------------------------------------------------------------------- /examples/with-use-gesture/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | browserEntrypoint: import.meta.resolve("./client.tsx"), 9 | }); 10 | 11 | server.get("*", async (context) => { 12 | /** 13 | * Render the request 14 | */ 15 | const result = await server.render(); 16 | 17 | return context.body(result, 200, { 18 | "content-type": "text/html", 19 | }); 20 | }); 21 | 22 | Deno.serve(server.fetch); 23 | -------------------------------------------------------------------------------- /lib/create/modules/trpc.ts: -------------------------------------------------------------------------------- 1 | export function trpcClientContent() { 2 | return ` 3 | import { createTRPCReact } from '@trpc/react'; 4 | import type { AppRouter } from './router.ts'; 5 | export const trpc = createTRPCReact(); 6 | `; 7 | } 8 | 9 | export function trpcRouterContent() { 10 | return ` 11 | import { initTRPC } from '@trpc/server'; 12 | import { z } from 'zod'; 13 | 14 | const t = initTRPC.create(); 15 | 16 | export const appRouter = t.router({ 17 | hello: t.procedure.input(z.string()).query(({ input }) => { 18 | return 'hello' + input; 19 | }), 20 | }); 21 | 22 | export type AppRouter = typeof appRouter; 23 | `; 24 | } 25 | -------------------------------------------------------------------------------- /examples/with-api-routes/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | export default function App() { 4 | useEffect(() => { 5 | // connect to websocket 6 | const socket = new WebSocket("ws://localhost:8000/ws"); 7 | console.log({ socket }); 8 | }, []); 9 | return ( 10 | 11 | 12 | 13 | With with-api-routes 14 | 15 | 16 | 17 | 18 |
Hello with-api-routes!
19 | 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /examples/with-preact/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | compilerOptions: { 9 | jsxImportSource: "preact", 10 | }, 11 | }); 12 | 13 | server.get("*", async (context) => { 14 | /** 15 | * Render the request 16 | */ 17 | const result = await server.render(); 18 | 19 | return context.body(result, 200, { 20 | "content-type": "text/html", 21 | }); 22 | }); 23 | 24 | Deno.serve(server.fetch); 25 | -------------------------------------------------------------------------------- /examples/with-react-query/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0", 6 | "react-dom/server": "https://esm.sh/v122/react-dom@18.2.0/server?dev", 7 | "react-dom/client": "https://esm.sh/v122/react-dom@18.2.0/client?dev", 8 | "ultra/": "https://deno.land/x/ultra@v2.3.8/", 9 | "@tanstack/react-query": "https://esm.sh/v122/@tanstack/react-query@4.1.3?external=react", 10 | "@tanstack/react-query-devtools": "https://esm.sh/v122/@tanstack/react-query-devtools@4.3.5?external=react,@tanstack/react-query&dev" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/with-fly-io/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | browserEntrypoint: import.meta.resolve("./client.tsx"), 9 | }); 10 | 11 | server.get("*", async (context) => { 12 | /** 13 | * Render the request 14 | */ 15 | const result = await server.render(); 16 | 17 | return context.body(result, 200, { 18 | "content-type": "text/html", 19 | }); 20 | }); 21 | 22 | Deno.serve({ port: 8080 }, server.fetch); 23 | -------------------------------------------------------------------------------- /examples/with-earthstar/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | browserEntrypoint: import.meta.resolve("./client.tsx"), 9 | }); 10 | 11 | server.get("*", async (context) => { 12 | /** 13 | * Render the request 14 | */ 15 | const result = await server.render(); 16 | 17 | return context.body(result, 200, { 18 | "content-type": "text/html", 19 | }); 20 | }); 21 | 22 | Deno.serve({ port: 8080 }, server.fetch); 23 | -------------------------------------------------------------------------------- /test/fixture/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "lock": false, 3 | "test": { 4 | "files": { 5 | "exclude": [".ultra/"] 6 | } 7 | }, 8 | "tasks": { 9 | "dev": "deno run -A --no-check --watch ./server.tsx", 10 | "build": "deno run -A ./build.ts", 11 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js", 12 | "start:no-browser": "ULTRA_MODE=production deno run -A --no-remote ./server.no-browser.js" 13 | }, 14 | "compilerOptions": { 15 | "jsx": "react-jsxdev", 16 | "jsxImportSource": "react", 17 | "lib": [ 18 | "deno.ns", 19 | "dom", 20 | "dom.iterable", 21 | "dom.asynciterable" 22 | ] 23 | }, 24 | "importMap": "./importMap.json" 25 | } 26 | -------------------------------------------------------------------------------- /examples/with-trpc/src/server/router.ts: -------------------------------------------------------------------------------- 1 | import { initTRPC } from "@trpc/server"; 2 | import { z } from "zod"; 3 | 4 | const posts = [ 5 | { name: "First Post" }, 6 | { name: "Second Post" }, 7 | { name: "Third Post" }, 8 | ]; 9 | 10 | const t = initTRPC.create(); 11 | 12 | const postRouter = t.router({ 13 | get: t.procedure.query(() => posts), 14 | create: t.procedure 15 | .input(z.object({ name: z.string() })) 16 | .mutation(({ input }) => { 17 | posts.push(input); 18 | return input; 19 | }), 20 | }); 21 | 22 | export const appRouter = t.router({ 23 | hello: t.procedure.query(() => "world"), 24 | post: postRouter, 25 | }); 26 | 27 | export type AppRouter = typeof appRouter; 28 | -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/fly.mdx: -------------------------------------------------------------------------------- 1 | # 🪰 Fly.io / Docker 2 | 3 | Use this `Dockerfile`. It is multi-stage and will both build and run the production ready app. 4 | 5 | ```dockerfile 6 | FROM denoland/deno as builder 7 | WORKDIR /app 8 | COPY . /app 9 | RUN deno task build 10 | 11 | FROM denoland/deno 12 | EXPOSE 8000 13 | COPY --from=builder /app/.ultra /app 14 | WORKDIR /app 15 | CMD ["deno", "task", "start"] 16 | ``` 17 | 18 | You can modify this as needed, another possible Dockerfile assumes you commit your build artifacts, or deploy locally from built files. 19 | 20 | ```dockerfile 21 | FROM denoland/deno 22 | EXPOSE 8000 23 | WORKDIR /app 24 | COPY .ultra /app 25 | CMD ["deno", "task", "start"] 26 | ``` 27 | -------------------------------------------------------------------------------- /examples/with-esm/server.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { createServer } from "ultra/server.ts"; 3 | import App from "./src/app.js"; 4 | 5 | const server = await createServer({ 6 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 7 | ? import.meta.resolve("./importMap.dev.json") 8 | : import.meta.resolve("./importMap.json"), 9 | browserEntrypoint: import.meta.resolve("./client.js"), 10 | }); 11 | 12 | server.get("*", async (context) => { 13 | /** 14 | * Render the request 15 | */ 16 | const result = await server.render(React.createElement(App)); 17 | 18 | return context.body(result, 200, { 19 | "content-type": "text/html", 20 | }); 21 | }); 22 | 23 | Deno.serve(server.fetch); 24 | -------------------------------------------------------------------------------- /hooks/use-server-context.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import ServerContext from "./server-context.js"; 3 | 4 | /** 5 | * @callback contextCallback 6 | * @param {import('../lib/types.ts').Context} context 7 | * @returns {Response | void} 8 | */ 9 | 10 | /** 11 | * @param {contextCallback | undefined} callback 12 | */ 13 | export default function useServerContext(callback) { 14 | const context = useContext(ServerContext); 15 | if (context && callback) { 16 | const response = callback(context); 17 | if (response) { 18 | /** 19 | * TODO(deckchairlabs) how to set the response here, while keeping the server rendered markup 20 | * if desired 21 | */ 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/create/common/content/style.ts: -------------------------------------------------------------------------------- 1 | export function styleContent() { 2 | // deno-fmt-ignore 3 | return`html, 4 | body { 5 | margin: 0; 6 | padding: 1rem; 7 | font-family: monospace; 8 | background: #ddd; 9 | text-align: center; 10 | } 11 | 12 | h1 { 13 | text-align: center; 14 | margin: 1rem auto 3rem; 15 | font-size: clamp(2em, 10vw, 8em); 16 | font-weight: 400; 17 | } 18 | 19 | h1 span::before { 20 | content: '@'; 21 | animation: blink 3s infinite; 22 | } 23 | 24 | @keyframes blink { 25 | 26 | 0%, 27 | 50%, 28 | 70%, 29 | 95% { 30 | content: '@'; 31 | } 32 | 33 | 65%, 34 | 90% { 35 | content: '—'; 36 | } 37 | } 38 | 39 | p { 40 | margin: 0 auto 1em; 41 | } 42 | `; 43 | } 44 | -------------------------------------------------------------------------------- /test/fixture/public/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 1rem; 5 | font-family: monospace; 6 | background: #ddd; 7 | text-align: center; 8 | } 9 | 10 | body { 11 | background-image: url('./share.webp'); 12 | } 13 | 14 | h1 { 15 | text-align: center; 16 | margin: 1rem auto 3rem; 17 | font-size: clamp(2em, 10vw, 8em); 18 | font-weight: 400; 19 | } 20 | 21 | h1 span::before { 22 | content: '@'; 23 | animation: blink 3s infinite; 24 | } 25 | 26 | @keyframes blink { 27 | 28 | 0%, 29 | 50%, 30 | 70%, 31 | 95% { 32 | content: '@'; 33 | } 34 | 35 | 65%, 36 | 90% { 37 | content: '—'; 38 | } 39 | } 40 | 41 | p { 42 | max-width: 600px; 43 | margin: 0 auto 1em; 44 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "request": "launch", 9 | "name": "Launch Program", 10 | "type": "pwa-node", 11 | "program": "${file}", 12 | "cwd": "${workspaceFolder}", 13 | "runtimeExecutable": "/deno/bin/deno", 14 | "runtimeArgs": [ 15 | "run", 16 | "--config", 17 | "./deno.json", 18 | "--inspect", 19 | "--allow-all" 20 | ], 21 | "attachSimplePort": 9229 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /examples/basic/server.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.176.0/testing/asserts.ts"; 2 | import server from "./server.tsx"; 3 | 4 | /** 5 | * This is here as an example of how to test your 6 | * server/rendering. 7 | */ 8 | Deno.test("it works", async (t) => { 9 | await t.step("it can render the homepage", async () => { 10 | const response = await server.request("http://localhost/"); 11 | const content = await response.text(); 12 | 13 | assertEquals(response.status, 200); 14 | assertEquals( 15 | response.headers.get("content-type"), 16 | "text/html; charset=utf-8", 17 | ); 18 | 19 | assertEquals(content.includes("Ultra"), true); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /examples/with-unocss/server.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.176.0/testing/asserts.ts"; 2 | import server from "./server.tsx"; 3 | 4 | /** 5 | * This is here as an example of how to test your 6 | * server/rendering. 7 | */ 8 | Deno.test("it works", async (t) => { 9 | await t.step("it can render the homepage", async () => { 10 | const response = await server.request("http://localhost/"); 11 | const content = await response.text(); 12 | 13 | assertEquals(response.status, 200); 14 | assertEquals( 15 | response.headers.get("content-type"), 16 | "text/html; charset=utf-8", 17 | ); 18 | 19 | assertEquals(content.includes("Ultra"), true); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /lib/dev/ensureMinDenoVersion.ts: -------------------------------------------------------------------------------- 1 | import { gte, log } from "../deps.ts"; 2 | 3 | const MIN_DENO_VERSION = "1.24.3"; 4 | 5 | export function ensureMinDenoVersion() { 6 | // Check that the minimum supported Deno version is being used. 7 | if (!gte(Deno.version.deno, MIN_DENO_VERSION)) { 8 | let message = 9 | `Deno version ${MIN_DENO_VERSION} or higher is required. Please update Deno.\n\n`; 10 | 11 | if (Deno.execPath().includes("homebrew")) { 12 | message += 13 | "You seem to have installed Deno via homebrew. To update, run: `brew upgrade deno`\n"; 14 | } else { 15 | message += "To update, run: `deno upgrade`\n"; 16 | } 17 | 18 | log.error(message); 19 | Deno.exit(1); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/with-react-query/src/todo.tsx: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | import useAsync from "ultra/hooks/use-async.js"; 3 | 4 | type TodoProps = { 5 | id: number; 6 | }; 7 | 8 | type Todo = { 9 | id: number; 10 | userId: number; 11 | title: string; 12 | completed: boolean; 13 | }; 14 | 15 | function fetchTodo(id: number): Promise { 16 | return fetch( 17 | `https://jsonplaceholder.typicode.com/todos/${id}`, 18 | ).then((response) => response.json()); 19 | } 20 | 21 | export default function Todo({ id }: TodoProps) { 22 | const query = useQuery( 23 | ["todo", { id }], 24 | useAsync(() => fetchTodo(id)), 25 | ); 26 | 27 | return
{JSON.stringify(query.data, null, 2)}
; 28 | } 29 | -------------------------------------------------------------------------------- /examples/with-stitches/src/stitches.config.ts: -------------------------------------------------------------------------------- 1 | import { createStitches } from "@stitches/react"; 2 | import type * as Stitches from "@stitches/react"; 3 | 4 | export const { 5 | styled, 6 | css, 7 | globalCss, 8 | keyframes, 9 | getCssText, 10 | theme, 11 | createTheme, 12 | config, 13 | } = createStitches({ 14 | theme: { 15 | colors: { 16 | gray400: "gainsboro", 17 | gray500: "lightgray", 18 | }, 19 | space: { 20 | 0: "0em", 21 | 1: "0.25em", 22 | }, 23 | }, 24 | media: { 25 | bp1: "(min-width: 480px)", 26 | }, 27 | utils: { 28 | marginX: (value: Stitches.PropertyValue<"margin">) => ({ 29 | marginLeft: value, 30 | marginRight: value, 31 | }), 32 | }, 33 | }); 34 | -------------------------------------------------------------------------------- /lib/create/common/content/build.ts: -------------------------------------------------------------------------------- 1 | import { fileExtension } from "../io.ts"; 2 | import { Config } from "../config.ts"; 3 | 4 | export function buildContent(config: Config) { 5 | const ext = fileExtension(config); 6 | return ` 7 | import { createBuilder } from "ultra/build.ts"; 8 | 9 | const builder = createBuilder({ 10 | browserEntrypoint: import.meta.resolve("${ext("./client", true)}"), 11 | serverEntrypoint: import.meta.resolve("${ext("./server", true)}"), 12 | }); 13 | 14 | builder.ignore([ 15 | "./README.md", 16 | "./importMap.json", 17 | "./.git/**", 18 | "./.vscode/**", 19 | "./.github/**", 20 | "./.gitignore" 21 | ]); 22 | 23 | // deno-lint-ignore no-unused-vars 24 | const result = await builder.build(); 25 | `; 26 | } 27 | -------------------------------------------------------------------------------- /examples/with-csr/server.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.176.0/testing/asserts.ts"; 2 | import server from "./server.tsx"; 3 | 4 | /** 5 | * This is here as an example of how to test your 6 | * server/rendering. 7 | */ 8 | Deno.test("it works", async (t) => { 9 | await t.step("it can render the homepage", async () => { 10 | const response = await server.request("http://localhost/"); 11 | const content = await response.text(); 12 | 13 | assertEquals(response.status, 200); 14 | assertEquals( 15 | (response.headers.get("content-type") || "").toLowerCase(), 16 | "text/html; charset=utf-8", 17 | ); 18 | 19 | assertEquals(content.includes("Ultra CSR"), true); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /lib/handler.ts: -------------------------------------------------------------------------------- 1 | export interface RequestHandler { 2 | handleRequest: (request: Request) => Promise; 3 | supportsRequest: (request: Request) => boolean; 4 | } 5 | 6 | export function executeHandler (request: Request, handler: RequestHandler) { 7 | try { 8 | if (handler.supportsRequest(request)) { 9 | return handler.handleRequest(request); 10 | } 11 | } catch (_) { 12 | return null; 13 | } 14 | } 15 | 16 | export function composeHandlers (...handlers: RequestHandler[]) { 17 | return function executeHandlerArray (request: Request) { 18 | for (const handler of handlers) { 19 | const response = executeHandler(request, handler); 20 | if (response) return response; 21 | } 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/with-unocss/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | browserEntrypoint: import.meta.resolve("./client.tsx"), 9 | }); 10 | 11 | server.get("*", async (context) => { 12 | /** 13 | * Render the request 14 | */ 15 | const result = await server.render(); 16 | 17 | return context.body(result, 200, { 18 | "content-type": "text/html; charset=utf-8", 19 | }); 20 | }); 21 | 22 | if (import.meta.main) { 23 | Deno.serve(server.fetch); 24 | } 25 | 26 | export default server; 27 | -------------------------------------------------------------------------------- /examples/lite/app.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import hydrate from "ultra/hydrate.js"; 3 | 4 | export default function App() { 5 | const [count, setCount] = useState(0); 6 | return ( 7 | 8 | 9 | 10 | lite 11 | 12 | 13 | 14 |
15 |

16 | Ultra Lite 17 |

18 | 21 |
22 | 23 | 24 | ); 25 | } 26 | 27 | typeof document !== "undefined" && hydrate(document, ); 28 | -------------------------------------------------------------------------------- /examples/react-experimental/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | browserEntrypoint: import.meta.resolve("./client.tsx"), 9 | }); 10 | 11 | server.get("*", async (context) => { 12 | /** 13 | * Render the request 14 | */ 15 | const result = await server.render(); 16 | 17 | return context.body(result, 200, { 18 | "content-type": "text/html; charset=utf-8", 19 | }); 20 | }); 21 | 22 | if (import.meta.main) { 23 | Deno.serve(server.fetch); 24 | } 25 | 26 | export default server; 27 | -------------------------------------------------------------------------------- /examples/with-react-three-fiber/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | browserEntrypoint: import.meta.resolve("./client.tsx"), 9 | }); 10 | 11 | server.get("*", async (context) => { 12 | /** 13 | * Render the request 14 | */ 15 | const result = await server.render(); 16 | 17 | return context.body(result, 200, { 18 | "content-type": "text/html; charset=utf-8", 19 | }); 20 | }); 21 | 22 | if (import.meta.main) { 23 | Deno.serve(server.fetch); 24 | } 25 | 26 | export default server; 27 | -------------------------------------------------------------------------------- /lib/create/common/ask.ts: -------------------------------------------------------------------------------- 1 | import { gradient } from "./styling.ts"; 2 | 3 | export async function ask(question = ":", answers?: T[]) { 4 | await Deno.stdout.write(new TextEncoder().encode(question + " ")); 5 | const buf = new Uint8Array(1024); 6 | const n = await Deno.stdin.read(buf); 7 | const answer = new TextDecoder().decode(buf.subarray(0, n)); 8 | if (answers) { 9 | return answers[parseInt(answer.trim())] || answers[0]; 10 | } 11 | return answer.trim(); 12 | } 13 | 14 | export async function confirm(question = "Are you sure?") { 15 | let a: string; 16 | while ( 17 | !/^(y|n)$/i.test(a = (await ask(question + gradient(" [y/n]", 12))).trim()) 18 | // deno-lint-ignore no-empty 19 | ) { 20 | } 21 | return a.toLowerCase() === "y"; 22 | } 23 | -------------------------------------------------------------------------------- /examples/bogus-marketing-or-blog/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | }); 9 | 10 | /** 11 | * Default route 12 | */ 13 | server.get("*", async (context) => { 14 | /** 15 | * Render the request 16 | */ 17 | const result = await server.render(, { 18 | generateStaticHTML: true, 19 | disableHydration: true, 20 | }); 21 | 22 | return context.body(result, 200, { 23 | "content-type": "text/html", 24 | }); 25 | }); 26 | 27 | Deno.serve({ port: 8000 }, server.fetch); 28 | -------------------------------------------------------------------------------- /examples/ultra-website/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import { Router } from "wouter"; 3 | import staticLocationHook from "wouter/static-location"; 4 | import App from "./src/app.tsx"; 5 | 6 | const server = await createServer({ 7 | importMapPath: import.meta.resolve("./importMap.json"), 8 | browserEntrypoint: import.meta.resolve("./client.tsx"), 9 | }); 10 | 11 | server.get("*", async (context) => { 12 | /** 13 | * Render the request 14 | */ 15 | const result = await server.render( 16 | 17 | 18 | , 19 | ); 20 | 21 | return context.body(result, 200, { 22 | "content-type": "text/html", 23 | }); 24 | }); 25 | 26 | Deno.serve(server.fetch); 27 | -------------------------------------------------------------------------------- /hooks/use-asset.js: -------------------------------------------------------------------------------- 1 | import { useContext, useMemo } from "react"; 2 | import AssetContext from "./asset-context.js"; 3 | 4 | /** 5 | * This hook returns the resolved path from the generated `asset-manifest.json` 6 | * It has no effect during development. 7 | * 8 | * @param {string} [path] 9 | */ 10 | export default function useAsset(path) { 11 | if (path === undefined) { 12 | throw new Error("a path must be supplied"); 13 | } 14 | 15 | if (path.startsWith("/") === false) { 16 | throw new Error( 17 | `The path provided to the useAsset hook must begin with "/" received: ${path}`, 18 | ); 19 | } 20 | 21 | const context = useContext(AssetContext) || 22 | new Map(globalThis.__ULTRA_ASSET_MAP || []); 23 | 24 | return useMemo(() => context.get(path) || path, [path]); 25 | } 26 | -------------------------------------------------------------------------------- /examples/with-emotion/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import { emotionTransformStream } from "./server/emotion.ts"; 3 | import App from "./src/app.tsx"; 4 | 5 | const server = await createServer({ 6 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 7 | ? import.meta.resolve("./importMap.dev.json") 8 | : import.meta.resolve("./importMap.json"), 9 | browserEntrypoint: import.meta.resolve("./client.tsx"), 10 | }); 11 | 12 | server.get("*", async (context) => { 13 | /** 14 | * Render the request 15 | */ 16 | const result = await server.render(); 17 | const transformed = emotionTransformStream(result); 18 | 19 | return context.body(transformed, 200, { 20 | "content-type": "text/html", 21 | }); 22 | }); 23 | 24 | Deno.serve(server.fetch); 25 | -------------------------------------------------------------------------------- /examples/with-csr/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | export default function App() { 4 | const [count, setCount] = useState(0); 5 | return ( 6 |
7 |

8 | __ 9 |

10 |

11 | Welcome to{" "} 12 | Ultra. This is a barebones starter for your web app. 13 |

14 | 15 |

16 | Take{" "} 17 | 18 | this 19 | 20 | , you may need it where you are going. It will show you how to customise 21 | your routing, data fetching, and styling with popular libraries. 22 |

23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /lib/create/common/printer.ts: -------------------------------------------------------------------------------- 1 | import { Config, Libraries } from "./config.ts"; 2 | 3 | function print(config: Config, value: Libraries) { 4 | return function (js: string, ts?: string) { 5 | if (config.includes.includes(value)) { 6 | if (config.ts && ts) { 7 | return ts; 8 | } else { 9 | return js; 10 | } 11 | } else { 12 | return ""; 13 | } 14 | }; 15 | } 16 | 17 | export function printer(config: Config) { 18 | return { 19 | twind: print(config, "twind"), 20 | stitches: print(config, "stitches"), 21 | reactHelmetAsync: print(config, "react-helmet-async"), 22 | reactQuery: print(config, "react-query"), 23 | reactRouter: print(config, "react-router"), 24 | wouter: print(config, "wouter"), 25 | trpc: print(config, "trpc"), 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /test/fixture/src/server/router.ts: -------------------------------------------------------------------------------- 1 | import { initTRPC } from "@trpc/server"; 2 | import { z } from "zod"; 3 | 4 | const t = initTRPC.create(); 5 | 6 | const getPostInput = z.object({ id: z.number() }); 7 | const getPostOutput = z.object({ 8 | id: z.number(), 9 | userId: z.number(), 10 | title: z.string(), 11 | completed: z.boolean(), 12 | }); 13 | 14 | const postRouter = t.router({ 15 | get: t.procedure 16 | .input(getPostInput) 17 | .output(getPostOutput).query(({ input }) => 18 | fetch(`https://jsonplaceholder.typicode.com/todos/${input.id}`).then( 19 | (response) => response.json(), 20 | ) 21 | ), 22 | }); 23 | 24 | export const appRouter = t.router({ 25 | hello: t.procedure.query(() => "world"), 26 | post: postRouter, 27 | }); 28 | 29 | export type AppRouter = typeof appRouter; 30 | -------------------------------------------------------------------------------- /examples/bogus-marketing-or-blog/src/app.tsx: -------------------------------------------------------------------------------- 1 | export default function App({ request }: { request: Request }) { 2 | console.log({ request: new URL(request.url) }); 3 | const path = new URL(request.url).pathname; 4 | return ( 5 | 6 | 7 | 8 | Ultra 9 | 10 | 11 | 12 | 13 | 14 |
15 |

16 | __ 17 |

18 |

19 | {path == "/" ? "Home" : "404"} 20 |

21 |
22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/prerequisites.mdx: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | These prerequisites are what we recommend to have knowledge and a basic understanding of before you begin your Ultra journey. 4 | 5 | #### [Deno](#deno) 6 | 7 | Ultra is built to run on the [Deno](https://deno.land) runtime (no NodeJS to be found here). So it's a good idea to familiarize yourself 8 | with the Deno runtime and what makes it tick. 9 | 10 | [Deno Introduction](https://deno.land/manual/introduction) 11 | 12 | #### [ESM](#esm) 13 | 14 | [Learn more about ESM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) 15 | 16 | #### [React](#react) 17 | 18 | [https://reactjs.org](https://reactjs.org/) 19 | 20 | #### [(Optional) TypeScript](#optional-typescript) 21 | 22 | [https://typescriptlang.org](https://www.typescriptlang.org/) -------------------------------------------------------------------------------- /examples/with-stitches/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from "./stitches.config.ts"; 2 | import { ThemeProvider } from "./theme.tsx"; 3 | 4 | const Text = styled("div", { 5 | fontSize: "3em", 6 | background: "blue", 7 | color: "white", 8 | padding: "$1", 9 | }); 10 | 11 | export default function App() { 12 | return ( 13 | 14 | 15 | 16 | 17 | with-stitches 18 | 19 | 20 | 21 | 22 | 23 | Hello with-stitches! 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /examples/with-twind/src/twind.ts: -------------------------------------------------------------------------------- 1 | // @see https://twind.style/library-mode 2 | import { 3 | cssom, 4 | injectGlobal as injectGlobal$, 5 | keyframes as keyframes$, 6 | stringify as stringify$, 7 | twind, 8 | tx as tx$, 9 | virtual, 10 | } from "@twind/core"; 11 | import config from "./twind.config.js"; 12 | 13 | const styleElementId = "__twind"; 14 | 15 | export const sheet = typeof Deno === "undefined" 16 | ? cssom(`style#${styleElementId}`) 17 | : virtual(); 18 | 19 | export const stringify = (target: unknown) => 20 | ``; 21 | 22 | //@ts-ignore twind type issue 23 | export const tw = twind( 24 | config, 25 | sheet, 26 | ); 27 | 28 | export const tx = tx$.bind(tw); 29 | export const injectGlobal = injectGlobal$.bind(tw); 30 | export const keyframes = keyframes$.bind(tw); 31 | -------------------------------------------------------------------------------- /examples/with-netlify-(WIP)/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import App from "./src/app.tsx"; 3 | 4 | const server = await createServer({ 5 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 6 | ? import.meta.resolve("./importMap.dev.json") 7 | : import.meta.resolve("./importMap.json"), 8 | browserEntrypoint: import.meta.resolve("./client.tsx"), 9 | }); 10 | 11 | server.get("*", async (context) => { 12 | /** 13 | * Render the request 14 | */ 15 | const result = await server.render(); 16 | 17 | return context.body(result, 200, { 18 | "content-type": "text/html", 19 | }); 20 | }); 21 | 22 | if (import.meta.main) { 23 | const { serve } = await import( 24 | "https://deno.land/std@0.176.0/http/server.ts" 25 | ); 26 | serve(server.fetch); 27 | } 28 | 29 | export default server.fetch; 30 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.lint": true, 4 | "deno.config": "./deno.json", 5 | "editor.defaultFormatter": "denoland.vscode-deno", 6 | "[css]": { 7 | "editor.defaultFormatter": "vscode.css-language-features" 8 | }, 9 | "[typescriptreact]": { 10 | "editor.defaultFormatter": "denoland.vscode-deno", 11 | }, 12 | "deno.enablePaths": [ 13 | "./examples/ultra-website", 14 | "./examples/basic", 15 | "./examples/with-api-routes", 16 | "./examples/with-emotion", 17 | "./examples/with-esm", 18 | "./examples/with-fly-io", 19 | "./examples/with-mdx", 20 | "./examples/with-react-helmet-async", 21 | "./examples/with-react-query", 22 | "./examples/with-react-router", 23 | "./examples/with-stitches", 24 | "./examples/with-twind", 25 | "./examples/with-wouter", 26 | "./" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /examples/with-preact/src/app.tsx: -------------------------------------------------------------------------------- 1 | import useAsset from "ultra/hooks/use-asset.js"; 2 | import island from "ultra/hooks/use-island.js"; 3 | import Counter from "./Counter.tsx"; 4 | 5 | const CounterIsland = island(Counter); 6 | 7 | export default function App() { 8 | return ( 9 | 10 | 11 | 12 | basic 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /examples/with-react-helmet-async/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { Helmet } from "react-helmet-async"; 2 | 3 | export default function App() { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | with-react-helmet-async 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
Hello with-react-helmet-async!
20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /examples/with-react-query/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { lazy, Suspense } from "react"; 2 | import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; 3 | import Todo from "./todo.tsx"; 4 | 5 | const SlowTodo = lazy(() => import("./slow-todo.tsx")); 6 | 7 | export default function App() { 8 | return ( 9 | 10 | 11 | 12 | with-react-query 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Loading}> 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /examples/with-fly-io/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml file generated for spring-morning-4944 on 2022-08-21T08:17:42Z 2 | 3 | app = "spring-morning-4944" 4 | kill_signal = "SIGINT" 5 | kill_timeout = 5 6 | processes = [] 7 | 8 | [env] 9 | 10 | [experimental] 11 | allowed_public_ports = [] 12 | auto_rollback = true 13 | 14 | [[services]] 15 | http_checks = [] 16 | internal_port = 8080 17 | processes = ["app"] 18 | protocol = "tcp" 19 | script_checks = [] 20 | [services.concurrency] 21 | hard_limit = 25 22 | soft_limit = 20 23 | type = "connections" 24 | 25 | [[services.ports]] 26 | force_https = true 27 | handlers = ["http"] 28 | port = 80 29 | 30 | [[services.ports]] 31 | handlers = ["tls", "http"] 32 | port = 443 33 | 34 | [[services.tcp_checks]] 35 | grace_period = "1s" 36 | interval = "15s" 37 | restart_limit = 0 38 | timeout = "2s" 39 | -------------------------------------------------------------------------------- /lib/create/common/content/denoConfig.ts: -------------------------------------------------------------------------------- 1 | import { Config } from "../config.ts"; 2 | import { fileExtension } from "../io.ts"; 3 | 4 | export function denoConfigContent(config: Config) { 5 | const ext = fileExtension(config); 6 | return `{ 7 | "tasks": { 8 | "dev": "deno run -A --no-check --watch ${ext("./server", true)}", 9 | "test": "deno test --allow-all", 10 | "build": "deno run -A ${ext("./build", false)}", 11 | "start": "ULTRA_MODE=production deno run -A --no-remote ./server.js" 12 | }, 13 | "compilerOptions": { 14 | "jsx": "react-jsxdev", 15 | "jsxImportSource": "react", 16 | "lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"] 17 | }, 18 | "fmt": { 19 | "files": { "exclude": [".ultra"] } 20 | }, 21 | "lint": { 22 | "files": { "exclude": [".ultra"] } 23 | }, 24 | "importMap": "./importMap.json" 25 | }`; 26 | } 27 | -------------------------------------------------------------------------------- /test/fixture/src/trpc/client.tsx: -------------------------------------------------------------------------------- 1 | import { type ReactNode } from "react"; 2 | import { httpBatchLink } from "@trpc/client/links/httpBatchLink"; 3 | import { Hydrate, QueryClientProvider } from "@tanstack/react-query"; 4 | import { queryClient } from "../query-client.tsx"; 5 | import { trpc } from "./trpc.ts"; 6 | 7 | declare const __REACT_QUERY_DEHYDRATED_STATE: unknown; 8 | 9 | const trpcClient = trpc.createClient({ 10 | links: [ 11 | httpBatchLink({ 12 | url: "/api/trpc", 13 | }), 14 | ], 15 | }); 16 | 17 | export function TRPCClientProvider({ children }: { children?: ReactNode }) { 18 | return ( 19 | 20 | 21 | {children} 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /examples/with-react-query/src/hooks/useDehydrateReactQuery.tsx: -------------------------------------------------------------------------------- 1 | import { dehydrate, QueryClient } from "@tanstack/react-query"; 2 | import useServerInsertedHTML from "ultra/hooks/use-server-inserted-html.js"; 3 | 4 | export function useDehydrateReactQuery(queryClient: QueryClient) { 5 | useServerInsertedHTML(() => { 6 | /** 7 | * Dehydrate the state from queryClient 8 | */ 9 | const dehydratedState = dehydrate(queryClient); 10 | 11 | return ( 12 | 20 | ); 21 | }); 22 | } 23 | 24 | // Note: There are known limitations to `dehydrate`, more info here. 25 | // https://github.com/TanStack/query/blob/main/docs/reference/hydration.md#limitations 26 | -------------------------------------------------------------------------------- /examples/with-trpc/src/trpc/client.tsx: -------------------------------------------------------------------------------- 1 | import { type ReactNode } from "react"; 2 | import { httpBatchLink } from "@trpc/client/links/httpBatchLink"; 3 | import { Hydrate, QueryClientProvider } from "@tanstack/react-query"; 4 | import { queryClient } from "../query-client.tsx"; 5 | import { trpc } from "./trpc.ts"; 6 | 7 | declare const __REACT_QUERY_DEHYDRATED_STATE: unknown; 8 | 9 | const trpcClient = trpc.createClient({ 10 | links: [ 11 | httpBatchLink({ 12 | url: "/api/trpc", 13 | }), 14 | ], 15 | }); 16 | 17 | export function TRPCClientProvider({ children }: { children?: ReactNode }) { 18 | return ( 19 | 20 | 21 | {children} 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /test/fixture.ts: -------------------------------------------------------------------------------- 1 | import { serve } from "https://deno.land/std@0.193.0/http/server.ts"; 2 | import { serveDir } from "https://deno.land/std@0.193.0/http/file_server.ts"; 3 | import { join } from "https://deno.land/std@0.193.0/path/mod.ts"; 4 | 5 | const abortController = new AbortController(); 6 | 7 | serve((request) => { 8 | return serveDir(request, { 9 | fsRoot: Deno.cwd(), 10 | quiet: true, 11 | }); 12 | }, { 13 | port: 3000, 14 | signal: abortController.signal, 15 | async onListen() { 16 | const test = await new Deno.Command(Deno.execPath(), { 17 | args: [ 18 | "test", 19 | "-A", 20 | "--no-check", 21 | "--reload", 22 | ], 23 | cwd: join(Deno.cwd(), "test", "fixture"), 24 | }).spawn(); 25 | 26 | const status = await test.status; 27 | abortController.abort(); 28 | 29 | Deno.exit(status.code); 30 | }, 31 | }); 32 | -------------------------------------------------------------------------------- /.github/workflows/basic-deno-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Basic / Deno Deploy 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | deploy: 9 | name: Basic / Deno Deploy 10 | runs-on: ubuntu-latest 11 | defaults: 12 | run: 13 | working-directory: ./examples/basic 14 | permissions: 15 | id-token: write 16 | contents: read 17 | 18 | steps: 19 | - name: Clone repository 20 | uses: actions/checkout@v3 21 | 22 | - name: Setup Deno 23 | uses: denoland/setup-deno@v1 24 | with: 25 | deno-version: v1.x 26 | 27 | - name: Build site 28 | run: deno run -A ./build.ts 29 | 30 | - name: Upload to Deno Deploy 31 | uses: denoland/deployctl@v1 32 | with: 33 | project: ultra 34 | entrypoint: server.js 35 | root: examples/basic/.ultra 36 | import-map: importMap.server.json -------------------------------------------------------------------------------- /examples/with-unocss/dev.ts: -------------------------------------------------------------------------------- 1 | import { build } from "unocss/cli"; 2 | 3 | let process: Deno.Process; 4 | let reloading = false; 5 | 6 | async function dev() { 7 | await build({ 8 | patterns: ["src/**/*"], 9 | outFile: "public/uno.css", 10 | }); 11 | const command = new Deno.Command(Deno.execPath(), { 12 | args: [ 13 | "run", 14 | "-A", 15 | "--location=http://localhost:8000", 16 | "./server.tsx", 17 | ], 18 | }); 19 | const server = command.spawn(); 20 | 21 | return server; 22 | } 23 | 24 | async function reload() { 25 | if (reloading) return; 26 | reloading = true; 27 | process.kill(); 28 | process = await dev(); 29 | reloading = false; 30 | } 31 | 32 | process = await dev(); 33 | const watcher = Deno.watchFs(["./src"]); 34 | for await (const { kind } of watcher) { 35 | if (kind === "modify" || kind === "create") { 36 | await reload(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/data-fetching.mdx: -------------------------------------------------------------------------------- 1 | # Data Fetching 2 | 3 | > **Note**: This section is a work-in-progress 4 | 5 | **Ultra** supports multiple use-cases for fetching data on the server side. 6 | 7 | #### [render as you fetch (Suspense)](#render-as-you-fetch-suspense) 8 | 9 | ✏️ 10 | 11 | #### [render as you fetch (Blocking)](#render-as-you-fetch-blocking) 12 | 13 | ✏️ 14 | 15 | #### [Route based pre-fetching](#route-based-pre-fetching) 16 | 17 | ✏️ 18 | 19 | --- 20 | 21 | #### [use](#use) 22 | 23 | **Ultra** supports React's `use` API to handle async data loading. 24 | 25 | [RFC: First class support for promises and async/await](https://github.com/reactjs/rfcs/pull/229) 26 | 27 | #### [react-query](#react-query) 28 | 29 | [https://tanstack.com/query](https://tanstack.com/query) 30 | 31 | [ultra-with-react-query](https://github.com/exhibitionist-digital/ultra/tree/main/examples/with-react-query) 32 | -------------------------------------------------------------------------------- /examples/with-react-router/server.tsx: -------------------------------------------------------------------------------- 1 | import { StaticRouter } from "react-router-dom/server"; 2 | import { createServer } from "ultra/server.ts"; 3 | import App from "./src/app.tsx"; 4 | 5 | const server = await createServer({ 6 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 7 | ? import.meta.resolve("./importMap.dev.json") 8 | : import.meta.resolve("./importMap.json"), 9 | browserEntrypoint: import.meta.resolve("./client.tsx"), 10 | }); 11 | 12 | server.get("*", async (context) => { 13 | /** 14 | * Render the request with context 15 | */ 16 | const result = await server.renderWithContext( 17 | 18 | 19 | , 20 | context, 21 | ); 22 | 23 | return context.body(result, undefined, { 24 | "content-type": "text/html; charset=utf-8", 25 | }); 26 | }); 27 | 28 | Deno.serve(server.fetch); 29 | -------------------------------------------------------------------------------- /examples/with-twind/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { lazy, Suspense } from "react"; 2 | import Post from "./post.tsx"; 3 | import { tw } from "./twind.ts"; 4 | 5 | const LazyPost = lazy(() => import("./post.tsx")); 6 | 7 | export default function App() { 8 | return ( 9 | 10 | 11 | 12 | with-twind 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /examples/with-mdx/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { MDXProvider } from "@mdx-js/react"; 2 | import useAsset from "ultra/hooks/use-asset.js"; 3 | 4 | import Docs from "./content/docs.js"; 5 | 6 | const Image = ( 7 | { src, ...props }: React.ImgHTMLAttributes, 8 | ) => { 9 | return ; 10 | }; 11 | 12 | export default function App() { 13 | return ( 14 | 19 | 20 | 21 | 22 | With with-mdx 23 | 24 | 25 | 26 | 27 |

Hello World

28 | 29 | 30 | 31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/code-splitting.mdx: -------------------------------------------------------------------------------- 1 | # Code Splitting 2 | 3 | Writing and shipping native ESM allows you to take advantage of some pretty neat optimisation features. Dynamic imports and the `lazy` API can be used to import components on the fly, in a way that is compatible with `Suspense`. 4 | 5 | [React Lazy Docs](https://reactjs.org/docs/code-splitting.html#reactlazy) 6 | 7 | ```js 8 | import { lazy, Suspense } from 'react'; 9 | 10 | const OtherComponent = lazy(() => import('./OtherComponent.jsx')); 11 | 12 | function MyComponent() { 13 | return ( 14 |
15 | Loading...
}> 16 | 17 | 18 | 19 | ); 20 | } 21 | ``` 22 | 23 | When both server and client rendering this component, the `OtherComponent.jsx` will only be imported when requested. Native code-splitting? YES! 24 | 25 | Using `lazy` imports with Routing is one of the best things ever -------------------------------------------------------------------------------- /test/fixture/src/context/twind.tsx: -------------------------------------------------------------------------------- 1 | import React, { type ReactNode } from "react"; 2 | import { create, type Sheet, ThemeConfiguration, type TW } from "twind"; 3 | 4 | const TWContext = React.createContext(null); 5 | 6 | type TWProviderProps = { 7 | sheet: Sheet; 8 | theme?: ThemeConfiguration; 9 | children?: ReactNode; 10 | }; 11 | 12 | export function TWProvider({ sheet, theme, children }: TWProviderProps) { 13 | const { tw } = create({ sheet, theme, preflight: false }); 14 | return {children}; 15 | } 16 | 17 | export function useTwContext() { 18 | const context = React.useContext(TWContext); 19 | if (!context) { 20 | throw new Error("No TWProvider found"); 21 | } 22 | return context; 23 | } 24 | 25 | export const serverSheet = (target = new Set()) => { 26 | return { 27 | target, 28 | insert: (rule: string) => { 29 | target.add(rule); 30 | }, 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /examples/ultra-website/src/components/Home.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from "wouter"; 2 | import Content from "../content/anti-bundle.js"; 3 | import { MDXProvider } from "@mdx-js/react"; 4 | import useAsset from "ultra/hooks/use-asset.js"; 5 | import { Ultra } from "../app.tsx"; 6 | 7 | const Image = ({ src, alt }: { src: string; alt: string }) => { 8 | return {alt}; 9 | }; 10 | 11 | export default function HomePage() { 12 | return ( 13 | 18 |
19 |

Ultra

20 |

21 | Ultra is an all ESM React/Deno framework that is built for Suspense 22 | Server Side Rendering.
Write ESM, ship ESM, simplify your life. 23 |

24 |
25 |
26 |
27 | 28 |
29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /examples/with-preact/src/Counter.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | 3 | interface CounterProps { 4 | start: number; 5 | } 6 | 7 | export default function Counter(props: CounterProps) { 8 | const [count, setCount] = useState(props.start); 9 | const [hydrated, setHydrated] = useState(false); 10 | const ref = useRef(null); 11 | 12 | useEffect(() => { 13 | if (ref.current) { 14 | console.log("hydrated"); 15 | setHydrated(true); 16 | } 17 | }, []); 18 | 19 | return ( 20 |
21 |

Hydrated

22 |

{count}

23 | 26 | 29 |
30 | ); 31 | } 32 | 33 | /** 34 | * This is key to make a component island compatible 35 | */ 36 | Counter.url = import.meta.url; 37 | -------------------------------------------------------------------------------- /examples/with-islands/src/Counter.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | 3 | interface CounterProps { 4 | start: number; 5 | } 6 | 7 | export default function Counter(props: CounterProps) { 8 | const [count, setCount] = useState(props.start); 9 | const [hydrated, setHydrated] = useState(false); 10 | const ref = useRef(null); 11 | 12 | useEffect(() => { 13 | if (ref.current) { 14 | console.log("hydrated", performance.now()); 15 | setHydrated(true); 16 | } 17 | }, []); 18 | 19 | return ( 20 |
21 |

Hydrated

22 |

{count}

23 | 26 | 29 |
30 | ); 31 | } 32 | 33 | /** 34 | * This is key to make a component island compatible 35 | */ 36 | Counter.url = import.meta.url; 37 | -------------------------------------------------------------------------------- /examples/ultra-website/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "@mdx-js/run": "https://esm.sh/v122/@mdx-js/mdx@2.1.3/lib/run.js", 4 | "@mdx-js/react": "https://esm.sh/v122/@mdx-js/react@2.1.3?external=react", 5 | "react": "https://esm.sh/v122/react@0.0.0-experimental-8951c5fc9-20220915", 6 | "react/": "https://esm.sh/v122/react@0.0.0-experimental-8951c5fc9-20220915/", 7 | "react-dom": "https://esm.sh/v122/react-dom@0.0.0-experimental-8951c5fc9-20220915", 8 | "react-dom/server": "https://esm.sh/v122/react-dom@0.0.0-experimental-8951c5fc9-20220915/server", 9 | "react-dom/client": "https://esm.sh/v122/react-dom@0.0.0-experimental-8951c5fc9-20220915/client", 10 | "ultra/": "https://deno.land/x/ultra@v2.3.8/", 11 | "@tanstack/react-query": "https://esm.sh/v122/@tanstack/react-query@4.1.3?external=react", 12 | "wouter": "https://esm.sh/v122/wouter@2.9.0?external=react", 13 | "wouter/static-location": "https://esm.sh/v122/wouter@2.9.0/static-location?external=react" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/with-trpc/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0&dev/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0?dev", 6 | "react-dom/": "https://esm.sh/v122/react-dom@18.2.0&dev/", 7 | "@tanstack/react-query": "https://esm.sh/v122/@tanstack/react-query@4.13.0?external=react", 8 | "@tanstack/react-query-devtools": "https://esm.sh/v122/@tanstack/react-query-devtools@4.13.0?external=react,@tanstack/react-query&dev", 9 | "@trpc/server": "https://esm.sh/v122/@trpc/server@10.0.0", 10 | "@trpc/server/": "https://esm.sh/v122/@trpc/server@10.0.0/", 11 | "@trpc/client": "https://esm.sh/v122/*@trpc/client@10.0.0", 12 | "@trpc/client/": "https://esm.sh/v122/*@trpc/client@10.0.0/", 13 | "@trpc/react-query": "https://esm.sh/v122/*@trpc/react-query@10.0.0", 14 | "zod": "https://deno.land/x/zod@v3.19.1/mod.ts", 15 | "ultra/": "https://deno.land/x/ultra@v2.3.8/" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/with-wouter/server.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.176.0/testing/asserts.ts"; 2 | import server from "./server.tsx"; 3 | 4 | async function testRenderedContainsText(location: string, text: string) { 5 | const response = await server.request(location); 6 | const content = await response.text(); 7 | 8 | assertEquals(response.status, 200); 9 | assertEquals( 10 | response.headers.get("content-type"), 11 | "text/html; charset=utf-8", 12 | ); 13 | 14 | assertEquals(content.includes(text), true); 15 | } 16 | 17 | /** 18 | * This is here as an example of how to test your 19 | * server/rendering. 20 | */ 21 | Deno.test("it works", async (t) => { 22 | await t.step("it can render the homepage", async () => { 23 | await testRenderedContainsText("http://localhost/", "Home page"); 24 | }); 25 | 26 | await t.step("it can render the about page", async () => { 27 | await testRenderedContainsText("http://localhost/about", "About page"); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/fixture/src/query-client.tsx: -------------------------------------------------------------------------------- 1 | import { dehydrate, QueryClient } from "@tanstack/react-query"; 2 | import useServerInsertedHTML from "ultra/hooks/use-server-inserted-html.js"; 3 | 4 | export const queryClient = new QueryClient({ 5 | defaultOptions: { 6 | queries: { 7 | suspense: true, 8 | }, 9 | }, 10 | }); 11 | 12 | export function useDehydrateReactQuery(queryClient: QueryClient) { 13 | useServerInsertedHTML(() => { 14 | /** 15 | * Dehydrate the state from queryClient 16 | */ 17 | const dehydratedState = dehydrate(queryClient); 18 | 19 | return ( 20 | 27 | ); 28 | }); 29 | } 30 | 31 | // Note: There are known limitations to 'dehydrate', more info here. 32 | // https://github.com/TanStack/query/blob/main/docs/reference/hydration.md#limitations 33 | -------------------------------------------------------------------------------- /lib/context/env.ts: -------------------------------------------------------------------------------- 1 | import { createElement as h } from "react"; 2 | import EnvContext from "../../hooks/env-context.js"; 3 | import useServerInsertedHTML from "../../hooks/use-server-inserted-html.js"; 4 | 5 | export function EnvProvider({ children }: { children: JSX.Element }) { 6 | const publicEnv = Object.entries(Deno.env.toObject()).filter(([key]) => 7 | key.startsWith("ULTRA_PUBLIC_") || key === "ULTRA_MODE" 8 | ); 9 | 10 | const value = new Map(publicEnv); 11 | 12 | useServerInsertedHTML(() => { 13 | const entries = Array.from(value.entries()); 14 | if (!entries.length) { 15 | return null; 16 | } 17 | 18 | return ( 19 | h("script", { 20 | type: "text/javascript", 21 | dangerouslySetInnerHTML: { 22 | __html: `globalThis.__ULTRA_ENV = ${ 23 | JSON.stringify(Array.from(value.entries())) 24 | }`, 25 | }, 26 | }) 27 | ); 28 | }); 29 | 30 | return h(EnvContext.Provider, { value }, children); 31 | } 32 | -------------------------------------------------------------------------------- /tools/test-examples.ts: -------------------------------------------------------------------------------- 1 | import { join } from "../lib/deps.ts"; 2 | import { initExampleConfig } from "./dev.ts"; 3 | 4 | async function testExample(example: string) { 5 | try { 6 | const examplePath = join("examples", example); 7 | await initExampleConfig(example); 8 | const command = new Deno.Command(Deno.execPath(), { 9 | args: [ 10 | "test", 11 | "-c", 12 | "deno.dev.json", 13 | "-A", 14 | "--no-check", 15 | ], 16 | cwd: examplePath, 17 | env: { 18 | ULTRA_MODE: "development", 19 | }, 20 | }); 21 | const process = command.spawn(); 22 | 23 | console.log("test ", examplePath); 24 | 25 | const status = await process.status; 26 | if (status.code > 0) { 27 | Deno.exit(status.code); 28 | } 29 | } catch (err) { 30 | console.error(err); 31 | Deno.exit(1); 32 | } 33 | } 34 | 35 | if (import.meta.main) { 36 | for (const example of Deno.args) { 37 | await testExample(example); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/www-deno-deploy.yml: -------------------------------------------------------------------------------- 1 | name: WWW / Deno Deploy 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | push: 8 | branches: [main] 9 | paths: 10 | - 'examples/ultra-website/**' 11 | 12 | jobs: 13 | deploy: 14 | name: WWW / Deno Deploy 15 | runs-on: ubuntu-latest 16 | defaults: 17 | run: 18 | working-directory: ./examples/ultra-website 19 | permissions: 20 | id-token: write 21 | contents: read 22 | 23 | steps: 24 | - name: Clone repository 25 | uses: actions/checkout@v3 26 | 27 | - name: Setup Deno 28 | uses: denoland/setup-deno@v1 29 | with: 30 | deno-version: v1.x 31 | 32 | - name: Build site 33 | run: deno run -A ./build.ts 34 | 35 | - name: Upload to Deno Deploy 36 | uses: denoland/deployctl@v1 37 | with: 38 | project: ultra-docs 39 | entrypoint: server.js 40 | root: examples/ultra-website/.ultra 41 | import-map: importMap.server.json -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/styling.mdx: -------------------------------------------------------------------------------- 1 | # Styling 2 | 3 | Did you know that browsers have a native way to style HTML documents? 4 | 5 | It's true, CSS is among the core languages of the open web and is standardized across many of the Web browsers we use today. 6 | 7 | [Learn more about CSS](https://developer.mozilla.org/en-US/docs/Web/CSS) 8 | 9 | --- 10 | 11 | If you would like to use an abstraction for CSS, **Ultra** has been known to support the following libraries: 12 | 13 | #### [twind](#twind) 14 | 15 | [https://twind.dev](https://twind.dev) 16 | 17 | [ultra-with-twind](https://github.com/exhibitionist-digital/ultra/tree/main/examples/with-twind) 18 | 19 | #### [emotion](#emotion) 20 | 21 | [https://emotion.sh](https://emotion.sh) 22 | 23 | [ultra-with-emotion](https://github.com/exhibitionist-digital/ultra/tree/main/examples/with-emotion) 24 | 25 | 26 | #### [stitches](#stitches) 27 | 28 | [https://stitches.dev](https://stitches.dev) 29 | 30 | [ultra-with-stitches](https://github.com/exhibitionist-digital/ultra/tree/main/examples/with-stitches) -------------------------------------------------------------------------------- /examples/with-trpc/src/app.tsx: -------------------------------------------------------------------------------- 1 | import useAsset from "ultra/hooks/use-asset.js"; 2 | import { trpc } from "./trpc/trpc.ts"; 3 | 4 | export default function App() { 5 | const posts = trpc.post.get.useQuery(); 6 | return ( 7 | 8 | 9 | 10 | basic 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

19 | Posts 20 |

21 | {posts.data 22 | ? ( 23 |
    24 | {posts.data.map((post) =>
  • {post.name}
  • )} 25 |
26 | ) 27 | :
Loading...
} 28 |
29 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /lib/context/asset.ts: -------------------------------------------------------------------------------- 1 | import type { ReactNode } from "react"; 2 | import { createElement as h } from "react"; 3 | import AssetContext from "../../hooks/asset-context.js"; 4 | import useServerInsertedHTML from "../../hooks/use-server-inserted-html.js"; 5 | 6 | export function AssetProvider( 7 | { children, value }: { 8 | children: ReactNode; 9 | value: Map | undefined; 10 | }, 11 | ) { 12 | useServerInsertedHTML(() => { 13 | /** 14 | * We don't need to inject if we don't have an assetManifest 15 | */ 16 | if (!value) { 17 | return; 18 | } 19 | 20 | const entries = Array.from(value.entries()); 21 | 22 | if (!entries.length) { 23 | return; 24 | } 25 | 26 | return ( 27 | h("script", { 28 | type: "text/javascript", 29 | dangerouslySetInnerHTML: { 30 | __html: `globalThis.__ULTRA_ASSET_MAP = ${ 31 | JSON.stringify(Array.from(value.entries())) 32 | }`, 33 | }, 34 | }) 35 | ); 36 | }); 37 | 38 | return h(AssetContext.Provider, { value }, children); 39 | } 40 | -------------------------------------------------------------------------------- /test/unit/use-env.test.tsx: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.176.0/testing/asserts.ts"; 2 | import useEnv from "../../hooks/use-env.js"; 3 | import { renderToStream } from "../../lib/render.ts"; 4 | 5 | Deno.test("useEnv hook", async () => { 6 | let value; 7 | 8 | Deno.env.set("ULTRA_MODE", "foo"); 9 | 10 | const App = () => { 11 | value = useEnv("ULTRA_MODE"); 12 | return ( 13 | 14 | 15 | useEnv 16 | 17 | 18 |
{value}
19 | 20 | 21 | ); 22 | }; 23 | 24 | const stream = await renderToStream( 25 | , 26 | undefined, 27 | { 28 | baseUrl: "/", 29 | importMap: { imports: {} }, 30 | assetManifest: new Map(), 31 | }, 32 | ); 33 | 34 | const response = new Response(stream); 35 | const text = await response.text(); 36 | 37 | assertEquals( 38 | text.includes("foo"), 39 | true, 40 | ); 41 | 42 | assertEquals( 43 | text.includes("globalThis.__ULTRA_ENV"), 44 | true, 45 | ); 46 | }); 47 | -------------------------------------------------------------------------------- /test/unit/use-server-inserted-html.test.tsx: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.176.0/testing/asserts.ts"; 2 | import { renderToStream } from "../../lib/render.ts"; 3 | import useServerInsertedHTML from "../../hooks/use-server-inserted-html.js"; 4 | 5 | Deno.test("useServerInsertedHTML hook", async () => { 6 | const App = () => { 7 | useServerInsertedHTML(() => { 8 | return
Testing useServerInsertedHTML
; 9 | }); 10 | 11 | return ( 12 | 13 | 14 | Testing 15 | 16 | 17 |
Hello World
18 | 19 | 20 | ); 21 | }; 22 | 23 | const stream = await renderToStream( 24 | , 25 | undefined, 26 | { 27 | baseUrl: "/", 28 | importMap: { imports: {} }, 29 | assetManifest: undefined, 30 | }, 31 | ); 32 | 33 | const response = new Response(stream); 34 | const text = await response.text(); 35 | 36 | assertEquals( 37 | text.includes("Testing useServerInsertedHTML"), 38 | true, 39 | ); 40 | }); 41 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | Welp, if you've made it here, you probably have a basic **Ultra** project 2 | running... 3 | 4 | **Ultra** gives you the control to use (or not use) many of the most popular 5 | React libraries out there. Use the examples above as a guide, you will probably 6 | need to update your `importMap.json` `server.tsx` and `client.tsx` 7 | 8 | Your app will probably need -- 9 | 10 | **`Routing`, choose from:** 11 | 12 | - Wouter (with-wouter) 13 | - React Router (with-react-router) 14 | 15 | **`Data Fetching`:** 16 | 17 | - React Query (with-react-query) 18 | - tRPC (with-trpc) 19 | 20 | **`CSS Styling`:** 21 | 22 | - Emotion (with-emotion) 23 | - Stitches (with-stitches) 24 | - Twind (with-twind) 25 | 26 | or just use plain old CSS, it's _way_ underrated. 27 | 28 | **`Head Management`:** 29 | 30 | - React Helmet Async (with-react-helmet-async) 31 | 32 | **`API Routing`:** (with-api-routes) 33 | 34 | --- 35 | 36 | If there is an existing library that you want to use, there is a good chance you 37 | can create a custom integration. Use some of the examples above as a guide -- 38 | open a PR if you are keen. 39 | -------------------------------------------------------------------------------- /app/app.tsx: -------------------------------------------------------------------------------- 1 | import { lazy, Suspense, useState } from "react"; 2 | import { ErrorBoundary } from "https://esm.sh/*react-error-boundary@4.0.11"; 3 | import { ImportMapScript } from "ultra/lib/react/client.js"; 4 | 5 | const LazyComponent = lazy(() => import("./components/Test.tsx")); 6 | 7 | const logError = (error: Error, info: { componentStack: string }) => { 8 | console.log(error, info); 9 | }; 10 | 11 | export default function App() { 12 | const [state, setState] = useState(0); 13 | return ( 14 | 15 | 16 | Testing 17 | 18 | 19 | 20 | 21 |
Hello World {state}
22 | Something went wrong} 24 | onError={logError} 25 | > 26 | Loading...}> 27 | 28 | 29 | 30 | 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Exhibitionist Digital 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/with-use-gesture/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { animated, useSpring } from "@react-spring/web"; 2 | import { useDrag } from "@use-gesture/react"; 3 | 4 | export default function App() { 5 | const [{ x, y }, api] = useSpring(() => ({ x: 0, y: 0 })); 6 | 7 | // @ts-ignore any 8 | const bind = useDrag(({ down, movement: [mx, my] }) => { 9 | api.start({ x: down ? mx : 0, y: down ? my : 0, immediate: down }); 10 | }); 11 | 12 | return ( 13 | 14 | 15 | 16 | With with-api-routes 17 | 18 | 19 | 20 | 21 |
Hello with-use-gesture! Drag the box!
22 | 33 | 34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /lib/context/serverInsertedHtml.ts: -------------------------------------------------------------------------------- 1 | import type { ReactNode } from "react"; 2 | import React, { createElement as h, Fragment } from "react"; 3 | import { renderToString } from "react-dom/server"; 4 | import ServerInsertedHTMLContext from "../../hooks/server-inserted-html-context.js"; 5 | 6 | const serverInsertedHTMLCallbacks: Set<() => ReactNode> = new Set(); 7 | 8 | export function InsertedHTML({ children }: { children: JSX.Element }) { 9 | // Reset flushEffectsHandler on each render 10 | serverInsertedHTMLCallbacks.clear(); 11 | 12 | const addInsertedHTML = React.useCallback( 13 | (handler: () => ReactNode) => { 14 | serverInsertedHTMLCallbacks.add(handler); 15 | }, 16 | [], 17 | ); 18 | 19 | return ( 20 | h(ServerInsertedHTMLContext.Provider, { value: addInsertedHTML }, children) 21 | ); 22 | } 23 | 24 | export const getServerInsertedHTML = (): Promise => { 25 | return Promise.resolve(renderToString( 26 | h( 27 | Fragment, 28 | null, 29 | Array.from(serverInsertedHTMLCallbacks).map((callback, index) => 30 | h(Fragment, { key: `server-insert-cb-${index}` }, callback()) 31 | ), 32 | ), 33 | )); 34 | }; 35 | -------------------------------------------------------------------------------- /test/fixture/importMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "react": "https://esm.sh/v122/react@18.2.0?dev", 4 | "react/": "https://esm.sh/v122/react@18.2.0&dev/", 5 | "react-dom": "https://esm.sh/v122/react-dom@18.2.0?dev", 6 | "react-dom/": "https://esm.sh/v122/react-dom@18.2.0&dev/", 7 | "@tanstack/react-query": "https://esm.sh/v122/@tanstack/react-query@4.13.0?external=react", 8 | "@tanstack/react-query-devtools": "https://esm.sh/v122/@tanstack/react-query-devtools@4.13.0?external=react,@tanstack/react-query&dev", 9 | "@trpc/server": "https://esm.sh/v122/@trpc/server@10.33.1", 10 | "@trpc/server/": "https://esm.sh/v122/@trpc/server@10.33.1/", 11 | "@trpc/client": "https://esm.sh/v122/*@trpc/client@10.33.1", 12 | "@trpc/client/": "https://esm.sh/v122/*@trpc/client@10.33.1/", 13 | "@trpc/react-query": "https://esm.sh/v122/*@trpc/react-query@10.33.1", 14 | "zod": "https://deno.land/x/zod@v3.19.1/mod.ts", 15 | "twind": "https://esm.sh/v122/twind@0.16.17", 16 | "twind/sheets": "https://esm.sh/v122/twind@0.16.17/sheets", 17 | "ultra/": "http://localhost:3000/", 18 | "@ultra/qrcode/": "https://deno.land/x/qrcode@v2.0.0/" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/with-wouter/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import { Router } from "wouter"; 3 | import staticLocationHook from "wouter/static-location"; 4 | import App from "./src/app.tsx"; 5 | import { SearchParamsProvider } from "./src/context/SearchParams.tsx"; 6 | 7 | const server = await createServer({ 8 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 9 | ? import.meta.resolve("./importMap.dev.json") 10 | : import.meta.resolve("./importMap.json"), 11 | browserEntrypoint: import.meta.resolve("./client.tsx"), 12 | }); 13 | 14 | server.get("*", async (context) => { 15 | /** 16 | * Render the request 17 | */ 18 | const requestUrl = new URL(context.req.url); 19 | const result = await server.render( 20 | 21 | 22 | 23 | 24 | , 25 | ); 26 | 27 | return context.body(result, 200, { 28 | "content-type": "text/html; charset=utf-8", 29 | }); 30 | }); 31 | 32 | if (import.meta.main) { 33 | Deno.serve(server.fetch); 34 | } 35 | 36 | export default server; 37 | -------------------------------------------------------------------------------- /lib/react/mod.ts: -------------------------------------------------------------------------------- 1 | import { RenderFunction } from "../renderer.ts"; 2 | import { createCompilerHandler } from "./compiler.ts"; 3 | import { createRenderHandler } from "./renderer.ts"; 4 | 5 | type CreateReactHandlerOptions = { 6 | root: string | URL; 7 | render: RenderFunction; 8 | }; 9 | 10 | export function createReactHandler(options: CreateReactHandlerOptions) { 11 | const renderer = createRenderHandler({ 12 | root: options.root, 13 | render: options.render, 14 | }); 15 | 16 | const compiler = createCompilerHandler({ 17 | root: options.root, 18 | }); 19 | 20 | const handleRequest = (request: Request) => { 21 | if (compiler.supportsRequest(request)) { 22 | return compiler.handleRequest(request); 23 | } 24 | 25 | if (renderer.supportsRequest(request)) { 26 | return renderer.handleRequest(request); 27 | } 28 | 29 | return new Response("Not Found", { status: 404 }); 30 | }; 31 | 32 | const supportsRequest = (request: Request) => { 33 | return compiler.supportsRequest(request) || 34 | renderer.supportsRequest(request); 35 | }; 36 | 37 | return { 38 | handleRequest, 39 | supportsRequest, 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /examples/ultra-website/content/docs/deno-deploy.mdx: -------------------------------------------------------------------------------- 1 | # 🦕 Deno Deploy 2 | 3 | Use this GitHub Action: 4 | 5 | ```yml 6 | name: Deploy 7 | on: [push] 8 | 9 | jobs: 10 | deploy: 11 | name: Deploy 12 | runs-on: ubuntu-latest 13 | permissions: 14 | id-token: write # Needed for auth with Deno Deploy 15 | contents: read # Needed to clone the repository 16 | 17 | steps: 18 | - name: Clone repository 19 | uses: actions/checkout@v3 20 | 21 | - name: Setup Deno 22 | uses: denoland/setup-deno@v1 23 | with: 24 | deno-version: v1.x 25 | 26 | - name: Build site 27 | run: deno run -A ./build.ts # Your build script 28 | 29 | - name: Upload to Deno Deploy 30 | uses: denoland/deployctl@v1 31 | with: 32 | project: "PROJECT_NAME" # Your deploy project name 33 | entrypoint: "server.js" 34 | root: .ultra 35 | import-map: importMap.server.json 36 | ``` 37 | 38 | > **Please note**: Deno Deploy does not currently [support dynamic imports](https://github.com/denoland/deploy_feedback/issues/1). Please refrain from using `lazy` in your codebase if Deno Deploy is your intended target. 39 | -------------------------------------------------------------------------------- /hooks/use-island.js: -------------------------------------------------------------------------------- 1 | /// 2 | import { createElement as h, Fragment, useContext } from "react"; 3 | import IslandContext from "./island-context.js"; 4 | import "ultra/hooks/island-hydrator.js"; 5 | 6 | export default function useIsland(Component) { 7 | const name = Component.displayName || Component.name; 8 | 9 | if (!Component.url) { 10 | throw new Error( 11 | `An island component must have a static url defined. Add "${name}.url = import.meta.url;" to your island component.`, 12 | ); 13 | } 14 | 15 | const IslandComponent = function ({ hydrationStrategy, ...props }) { 16 | const add = useContext(IslandContext); 17 | const id = add(Component, props); 18 | 19 | const WrappedComponent = h(Fragment, null, [ 20 | h("template", { 21 | key: `island-hydration-marker-${id}`, 22 | "data-hydration-marker": id, 23 | "data-hydration-strategy": hydrationStrategy, 24 | }), 25 | h(Component, { key: `island-suspense-boundary-${id}`, ...props }), 26 | ]); 27 | 28 | return WrappedComponent; 29 | }; 30 | 31 | IslandComponent.displayName = `Island(${name})`; 32 | 33 | return IslandComponent; 34 | } 35 | -------------------------------------------------------------------------------- /test/unit/use-async.test.tsx: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.176.0/testing/asserts.ts"; 2 | import { renderToStream } from "../../lib/render.ts"; 3 | import useAsync from "../../hooks/use-async.js"; 4 | 5 | Deno.test("useAsync hook", async () => { 6 | const App = () => { 7 | useAsync(() => 8 | new Promise((resolve) => { 9 | setTimeout(() => resolve("two"), 100); 10 | }) 11 | ); 12 | useAsync(() => Promise.resolve("one")); 13 | 14 | return ( 15 | 16 | 17 | Testing 18 | 19 | 20 |
Hello World
21 | 22 | 23 | ); 24 | }; 25 | 26 | const stream = await renderToStream( 27 | , 28 | undefined, 29 | { 30 | baseUrl: "/", 31 | importMap: { imports: {} }, 32 | assetManifest: undefined, 33 | }, 34 | ); 35 | 36 | const response = new Response(stream); 37 | const text = await response.text(); 38 | 39 | assertEquals( 40 | text.includes( 41 | '', 42 | ), 43 | true, 44 | ); 45 | }); 46 | -------------------------------------------------------------------------------- /hydrate.js: -------------------------------------------------------------------------------- 1 | import { createElement as h, startTransition, StrictMode } from "react"; 2 | import { hydrateRoot } from "react-dom/client"; 3 | 4 | /** 5 | * @see https://caniuse.com/requestidlecallback 6 | */ 7 | const requestIdleCallbackUltra = (typeof self !== "undefined" && 8 | self.requestIdleCallback && 9 | self.requestIdleCallback.bind(window)) || 10 | function (cb) { 11 | const start = Date.now(); 12 | return setTimeout(function () { 13 | cb({ 14 | didTimeout: false, 15 | timeRemaining() { 16 | return Math.max(0, 50 - (Date.now() - start)); 17 | }, 18 | }); 19 | }, 1); 20 | }; 21 | 22 | /** 23 | * @param {Element | Document} container 24 | * @param {React.ReactNode} element 25 | * @param {import('react-dom/client').HydrationOptions} [options] 26 | */ 27 | export default function hydrate(container, element, options) { 28 | requestIdleCallbackUltra(() => { 29 | /** 30 | * @see https://reactjs.org/docs/react-api.html#starttransition 31 | */ 32 | startTransition(() => { 33 | hydrateRoot( 34 | container, 35 | h(StrictMode, { children: element }), 36 | options, 37 | ); 38 | }); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /examples/with-twind/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | import { createHeadInsertionTransformStream } from "ultra/stream.ts"; 3 | import App from "./src/app.tsx"; 4 | import { stringify, tw } from "./src/twind.ts"; 5 | 6 | const server = await createServer({ 7 | importMapPath: Deno.env.get("ULTRA_MODE") === "development" 8 | ? import.meta.resolve("./importMap.dev.json") 9 | : import.meta.resolve("./importMap.json"), 10 | browserEntrypoint: import.meta.resolve("./client.tsx"), 11 | }); 12 | 13 | server.get("*", async (context) => { 14 | /** 15 | * Render the request 16 | */ 17 | const result = await server.render(); 18 | 19 | // Inject the style tag into the head of the streamed response 20 | const stylesInject = createHeadInsertionTransformStream(() => { 21 | if (Array.isArray(tw.target)) { 22 | return Promise.resolve(stringify(tw.target)); 23 | } 24 | 25 | throw new Error("Expected tw.target to be an instance of an Array"); 26 | }); 27 | 28 | const transformed = result.pipeThrough(stylesInject); 29 | 30 | return context.body(transformed, 200, { 31 | "content-type": "text/html", 32 | }); 33 | }); 34 | 35 | Deno.serve(server.fetch); 36 | -------------------------------------------------------------------------------- /examples/with-netlify-(WIP)/src/app.tsx: -------------------------------------------------------------------------------- 1 | export default function App() { 2 | console.log("Hello world!"); 3 | return ( 4 | 5 | 6 | 7 | basic 8 | 9 | 10 | 11 | 12 | 13 |
14 |

15 | __ 16 |

17 |

18 | Welcome to{" "} 19 | Ultra. This is a barebones starter for your web 20 | app. 21 |

22 |

23 | Take{" "} 24 | 28 | this 29 | , you may need it where you are going. It will show you how to 30 | customise your routing, data fetching, and styling with popular 31 | libraries. 32 |

33 |
34 | 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /examples/with-csr/server.tsx: -------------------------------------------------------------------------------- 1 | import { createServer } from "ultra/server.ts"; 2 | 3 | const importMapPath = Deno.env.get("ULTRA_MODE") === "development" 4 | ? "./importMap.dev.json" 5 | : "./importMap.json"; 6 | const server = await createServer({ 7 | importMapPath: import.meta.resolve(importMapPath), 8 | browserEntrypoint: import.meta.resolve("./client.tsx"), 9 | }); 10 | 11 | server.get("*", (context) => { 12 | /** 13 | * Render the request 14 | */ 15 | return context.html(` 16 | 17 | 18 | 19 | 20 | Ultra CSR 21 | 22 | 23 | 24 | 27 | 28 | 29 |
30 | 31 | 32 | 33 | `); 34 | }); 35 | 36 | if (import.meta.main) { 37 | Deno.serve(server.fetch); 38 | } 39 | 40 | export default server; 41 | -------------------------------------------------------------------------------- /examples/with-wouter/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Route, Switch } from "wouter"; 2 | import HomePage from "./pages/Home.tsx"; 3 | import AboutPage from "./pages/About.tsx"; 4 | import { useSearchParams } from "./context/SearchParams.tsx"; 5 | 6 | export default function App() { 7 | const searchParams = useSearchParams(); 8 | return ( 9 | 10 | 11 | 12 | with-wouter 13 | 14 | 15 | 16 | 17 | 22 |
23 |
Search params: {searchParams.toString()}
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 404 33 | 34 | 35 |
36 | 37 | 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /examples/with-unocss/src/app.tsx: -------------------------------------------------------------------------------- 1 | import useAsset from "ultra/hooks/use-asset.js"; 2 | 3 | export default function App() { 4 | return ( 5 | 6 | 7 | 8 | basic 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |

17 | Welcome to{" "} 18 | Ultra. This is a barebones starter for your web 19 | app. 20 |

21 |

22 | Take{" "} 23 | 27 | this 28 | , you may need it where you are going. It will show you how to 29 | customise your routing, data fetching, and styling with popular 30 | libraries. 31 |

32 |
33 | 34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /examples/with-emotion/server/emotion.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createHeadInsertionTransformStream, 3 | createTransformStream, 4 | } from "ultra/stream.ts"; 5 | 6 | const regex = new RegExp( 7 | `