├── LICENSE ├── README.md ├── _examples └── server.tsx ├── deno.jsonc ├── handlers.ts ├── import-map.json ├── init.ts ├── jsx.d.ts ├── mod.ts ├── setup.ts └── styles.ts /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2022 the oak authors 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nat 2 | 3 | A server side rendering framework for Deno CLI and Deploy. 4 | 5 | Incorporating [acorn](https://deno.land/x/acorn/), 6 | [nano-jsx](https://nanojsx.io/), and [twind](https://twind.dev/), it provides 7 | the tooling to provide a server centric framework for providing dynamic websites 8 | served from the edge. 9 | 10 | ## Getting started 11 | 12 | nat as a setup script which makes it easy to scaffold out a project and is the 13 | easiest way to get started. You will need the Deno CLI [installed]() locally and 14 | will want to run the following command within the current directory you want to 15 | setup: 16 | 17 | ``` 18 | > deno run https://deno.land/x/nat/setup.ts 19 | ``` 20 | 21 | The script will prompt you for read and write permissions to the current 22 | directory as well as ask for your confirmation to write out the initial setup 23 | files for the project. 24 | 25 | Once the project is setup, edit the `main.tsx` and use `deno task start` to run 26 | your server locally. 27 | 28 | You can also deploy the project to [Deno Deploy](https://dash.deno.com/new). 29 | 30 | ## Concepts 31 | 32 | The framework includes the acorn 33 | [Router](https://doc.deno.land/https://deno.land/x/acorn/mod.ts/~/Router). An 34 | instance of the router is returned from the 35 | [init()](https://doc.deno.land/https://deno.land/x/nat/mod.ts/~/init) function. 36 | The acorn router is based of web standard [URLPattern]() API which allows 37 | matching URLs and parsing out values to variables. Those variables are available 38 | in the handler's 39 | [context `.params` property](https://doc.deno.land/https://deno.land/x/acorn/mod.ts/~/Context#params). 40 | 41 | The framework comes with [nano-jsx](https://nanojsx.io/) built in, which makes 42 | it easy to server-side render JSX/TSX as a response. 43 | 44 | The framework also comes with [twind](https://twind.dev/) integrated which is 45 | well suited to server side rendering of tailwind's functional CSS styles in a 46 | super efficient way. 47 | 48 | --- 49 | 50 | Copyright 2022 the oak authors. All rights reserved. MIT License. 51 | -------------------------------------------------------------------------------- /_examples/server.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx h */ 2 | /** @jsxFrag Fragment */ 3 | import { createStyle, Fragment, h, init, render } from "../mod.ts"; 4 | 5 | const router = init(); 6 | const style = createStyle({ 7 | title: "text-xl", 8 | }); 9 | 10 | const App = ({ name }: { name: string }) => ( 11 | <> 12 |
Hello {name}!
13 | 14 | ); 15 | 16 | router.get("/", render()); 17 | 18 | router.listen(); 19 | -------------------------------------------------------------------------------- /deno.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "importMap": "./import-map.json", 3 | "tasks": { 4 | "example": "deno run --check --allow-net --allow-hrtime _examples/server.tsx" 5 | } 6 | } -------------------------------------------------------------------------------- /handlers.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2022 the oak authors. All rights reserved. MIT License. 2 | 3 | /** 4 | * Handlers, 5 | * @module 6 | */ 7 | 8 | import { Helmet } from "nano-jsx/helmet"; 9 | import { renderSSR } from "nano-jsx/ssr"; 10 | import { contentType } from "std/media_types"; 11 | import { getStyleTag, virtualSheet } from "twind/sheets"; 12 | 13 | export const sheet = virtualSheet(); 14 | 15 | /** A handler function which renders JSX and send it to the client. 16 | * 17 | * ### Example 18 | * 19 | * ```ts 20 | * import { h, init, render } from "https://deno.land/x/nat/mod.ts"; 21 | * import { App } from "./App.tsx"; 22 | * 23 | * const router = init(); 24 | * router.get("/", render()); 25 | * router.listen(); 26 | * ``` 27 | */ 28 | export function render(jsx: unknown) { 29 | return function handler() { 30 | sheet.reset(); 31 | const page = renderSSR(jsx); 32 | const styles = getStyleTag(sheet); 33 | const { 34 | body, 35 | head, 36 | footer, 37 | attributes: { body: bodyAttributes, html: htmlAttributes }, 38 | } = Helmet.SSR(page); 39 | return new Response( 40 | ` 41 | 42 | 43 | ${styles} 44 | ${head.join("\n")} 45 | 46 | 47 | ${body} 48 | ${footer.join("\n")} 49 | 50 | `, 51 | { 52 | headers: { 53 | "content-type": contentType("text/html")!, 54 | }, 55 | }, 56 | ); 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /import-map.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "acorn": "https://deno.land/x/acorn@0.0.10/mod.ts", 4 | "nano-jsx/core": "https://deno.land/x/nano_jsx@v0.0.30/core.ts", 5 | "nano-jsx/fragment": "https://deno.land/x/nano_jsx@v0.0.30/fragment.ts", 6 | "nano-jsx/helmet": "https://deno.land/x/nano_jsx@v0.0.30/components/helmet.ts", 7 | "nano-jsx/ssr": "https://deno.land/x/nano_jsx@v0.0.30/ssr.ts", 8 | "oak_commons/types": "https://deno.land/x/oak_commons@0.3.1/types.d.ts", 9 | "std/media_types": "https://deno.land/std@0.142.0/media_types/mod.ts", 10 | "twind": "https://esm.sh/twind@0.16.17?pin=v82", 11 | "twind/css": "https://esm.sh/twind@0.16.17/css?pin=v82", 12 | "twind/sheets": "https://esm.sh/twind@0.16.17/sheets?pin=v82" 13 | } 14 | } -------------------------------------------------------------------------------- /init.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2022 the oak authors. All rights reserved. MIT License. 2 | 3 | /** Contains initialization and setup functionality. 4 | * 5 | * @module 6 | */ 7 | 8 | import { Router } from "acorn"; 9 | import { type Configuration as TwindConfig, setup } from "twind"; 10 | import { type KeyRing } from "oak_commons/types"; 11 | 12 | import { sheet } from "./handlers.ts"; 13 | 14 | export interface StartOptions extends TwindConfig { 15 | /** A key ring which will be used for signing and validating cookies. */ 16 | keys?: KeyRing; 17 | /** When providing internal responses, like on unhandled errors, prefer JSON 18 | * responses to HTML responses. When set to `false` HTML will be preferred 19 | * when responding, but content type negotiation will still be respected. 20 | * Defaults to `true`. */ 21 | preferJson?: boolean; 22 | /** When `true` skip setting up default logging on the router. */ 23 | quiet?: boolean; 24 | } 25 | 26 | /** Initialize the environment optionally using the provided options, returning 27 | * an instance of {@linkcode Router}. 28 | * 29 | * ### Example 30 | * 31 | * ```ts 32 | * import { init, render } from "https://deno.land/x/nat/mod.ts"; 33 | * import { App } from "./App.tsx"; 34 | * 35 | * const router = init(); 36 | * router.get("/", render()); 37 | * 38 | * router.listen(); 39 | * ``` 40 | */ 41 | export function init(options: StartOptions = {}): Router { 42 | options.sheet = sheet; 43 | setup(options); 44 | const router = new Router(options); 45 | if (!options.quiet) { 46 | router.addEventListener( 47 | "listen", 48 | ({ secure, hostname, port }) => 49 | console.log( 50 | `%cListening: %c${ 51 | secure ? "https://" : "http://" 52 | }${hostname}:${port}`, 53 | "color:green;font-weight:bold;", 54 | "color:yellow", 55 | ), 56 | ); 57 | router.addEventListener( 58 | "handled", 59 | ( 60 | { 61 | response: { status }, 62 | route, 63 | request: { url, method }, 64 | measure: { duration }, 65 | }, 66 | ) => { 67 | const responseColor = status < 400 68 | ? "color:green" 69 | : status < 500 70 | ? "color:yellow" 71 | : "color:red"; 72 | let path = route?.route; 73 | if (!path) { 74 | try { 75 | path = new URL(url).pathname; 76 | } catch { 77 | // just swallow errors here 78 | } 79 | } 80 | console.log( 81 | `%c${method} ${path} - [${status}] ${duration.toFixed(2)}ms`, 82 | responseColor, 83 | ); 84 | }, 85 | ); 86 | } 87 | return router; 88 | } 89 | -------------------------------------------------------------------------------- /jsx.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2022 the oak authors. All rights reserved. MIT License. 2 | 3 | // deno-lint-ignore-file no-explicit-any ban-types no-empty-interface 4 | 5 | /** 6 | * Provides a scaffold of JSX types intended to be neutral to a specific JSX 7 | * implementation. Largely borrowed from [preact](https://preactjs.com/) type 8 | * definitions. 9 | * 10 | * @module 11 | */ 12 | 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | 20 | type Key = string | number | any; 21 | 22 | type Component = any; 23 | 24 | interface ComponentClass

{ 25 | new (props: P): Component; 26 | } 27 | 28 | type RefObject = { current: T | null }; 29 | type RefCallback = (instance: T | null) => void; 30 | type Ref = RefObject | RefCallback; 31 | 32 | type RenderableProps = 33 | & P 34 | & Readonly }>; 35 | 36 | interface FunctionComponent

{ 37 | (props: RenderableProps

, context?: any): VNode | null; 38 | displayName?: string; 39 | defaultProps?: Partial

; 40 | } 41 | 42 | type ComponentType

= ComponentClass

| FunctionComponent

; 43 | 44 | type ComponentChild = any; 45 | 46 | type ComponentChildren = ComponentChild[] | ComponentChild; 47 | 48 | interface VNode

{ 49 | type: ComponentType

| string; 50 | props: P & { children: ComponentChildren }; 51 | key: Key; 52 | ref?: Ref | null; 53 | } 54 | 55 | interface BaseDOMAttributes { 56 | children?: any; 57 | dangerouslySetInnerHTML?: { 58 | __html: string; 59 | }; 60 | } 61 | 62 | interface Attributes { 63 | key?: Key; 64 | jsx?: boolean; 65 | } 66 | 67 | interface ClassAttributes extends Attributes { 68 | ref?: Ref; 69 | } 70 | 71 | type Defaultize = 72 | // Distribute over unions 73 | Props extends any // Make any properties included in Default optional 74 | ? 75 | & Partial>> 76 | & // Include the remaining properties from Props 77 | Pick> 78 | : never; 79 | 80 | declare namespace JSX { 81 | export type LibraryManagedAttributes = Component extends { 82 | defaultProps: infer Defaults; 83 | } ? Defaultize 84 | : Props; 85 | 86 | export interface IntrinsicAttributes { 87 | key?: any; 88 | } 89 | 90 | export interface Element extends VNode {} 91 | 92 | export interface ElementClass extends Component {} 93 | 94 | export interface ElementAttributesProperty { 95 | props: any; 96 | } 97 | 98 | export interface ElementChildrenAttribute { 99 | children: any; 100 | } 101 | 102 | export type DOMCSSProperties = { 103 | [ 104 | key in keyof Omit< 105 | CSSStyleDeclaration, 106 | | "item" 107 | | "setProperty" 108 | | "removeProperty" 109 | | "getPropertyValue" 110 | | "getPropertyPriority" 111 | > 112 | ]?: string | number | null | undefined; 113 | }; 114 | export type AllCSSProperties = { 115 | [key: string]: string | number | null | undefined; 116 | }; 117 | export interface CSSProperties extends AllCSSProperties, DOMCSSProperties { 118 | cssText?: string | null; 119 | } 120 | 121 | export interface SVGAttributes 122 | extends HTMLAttributes { 123 | accentHeight?: number | string; 124 | accumulate?: "none" | "sum"; 125 | additive?: "replace" | "sum"; 126 | alignmentBaseline?: 127 | | "auto" 128 | | "baseline" 129 | | "before-edge" 130 | | "text-before-edge" 131 | | "middle" 132 | | "central" 133 | | "after-edge" 134 | | "text-after-edge" 135 | | "ideographic" 136 | | "alphabetic" 137 | | "hanging" 138 | | "mathematical" 139 | | "inherit"; 140 | allowReorder?: "no" | "yes"; 141 | alphabetic?: number | string; 142 | amplitude?: number | string; 143 | arabicForm?: "initial" | "medial" | "terminal" | "isolated"; 144 | ascent?: number | string; 145 | attributeName?: string; 146 | attributeType?: string; 147 | autoReverse?: number | string; 148 | azimuth?: number | string; 149 | baseFrequency?: number | string; 150 | baselineShift?: number | string; 151 | baseProfile?: number | string; 152 | bbox?: number | string; 153 | begin?: number | string; 154 | bias?: number | string; 155 | by?: number | string; 156 | calcMode?: number | string; 157 | capHeight?: number | string; 158 | clip?: number | string; 159 | clipPath?: string; 160 | clipPathUnits?: number | string; 161 | clipRule?: number | string; 162 | colorInterpolation?: number | string; 163 | colorInterpolationFilters?: "auto" | "sRGB" | "linearRGB" | "inherit"; 164 | colorProfile?: number | string; 165 | colorRendering?: number | string; 166 | contentScriptType?: number | string; 167 | contentStyleType?: number | string; 168 | cursor?: number | string; 169 | cx?: number | string; 170 | cy?: number | string; 171 | d?: string; 172 | decelerate?: number | string; 173 | descent?: number | string; 174 | diffuseConstant?: number | string; 175 | direction?: number | string; 176 | display?: number | string; 177 | divisor?: number | string; 178 | dominantBaseline?: number | string; 179 | dur?: number | string; 180 | dx?: number | string; 181 | dy?: number | string; 182 | edgeMode?: number | string; 183 | elevation?: number | string; 184 | enableBackground?: number | string; 185 | end?: number | string; 186 | exponent?: number | string; 187 | externalResourcesRequired?: number | string; 188 | fill?: string; 189 | fillOpacity?: number | string; 190 | fillRule?: "nonzero" | "evenodd" | "inherit"; 191 | filter?: string; 192 | filterRes?: number | string; 193 | filterUnits?: number | string; 194 | floodColor?: number | string; 195 | floodOpacity?: number | string; 196 | focusable?: number | string; 197 | fontFamily?: string; 198 | fontSize?: number | string; 199 | fontSizeAdjust?: number | string; 200 | fontStretch?: number | string; 201 | fontStyle?: number | string; 202 | fontVariant?: number | string; 203 | fontWeight?: number | string; 204 | format?: number | string; 205 | from?: number | string; 206 | fx?: number | string; 207 | fy?: number | string; 208 | g1?: number | string; 209 | g2?: number | string; 210 | glyphName?: number | string; 211 | glyphOrientationHorizontal?: number | string; 212 | glyphOrientationVertical?: number | string; 213 | glyphRef?: number | string; 214 | gradientTransform?: string; 215 | gradientUnits?: string; 216 | hanging?: number | string; 217 | horizAdvX?: number | string; 218 | horizOriginX?: number | string; 219 | ideographic?: number | string; 220 | imageRendering?: number | string; 221 | in2?: number | string; 222 | in?: string; 223 | intercept?: number | string; 224 | k1?: number | string; 225 | k2?: number | string; 226 | k3?: number | string; 227 | k4?: number | string; 228 | k?: number | string; 229 | kernelMatrix?: number | string; 230 | kernelUnitLength?: number | string; 231 | kerning?: number | string; 232 | keyPoints?: number | string; 233 | keySplines?: number | string; 234 | keyTimes?: number | string; 235 | lengthAdjust?: number | string; 236 | letterSpacing?: number | string; 237 | lightingColor?: number | string; 238 | limitingConeAngle?: number | string; 239 | local?: number | string; 240 | markerEnd?: string; 241 | markerHeight?: number | string; 242 | markerMid?: string; 243 | markerStart?: string; 244 | markerUnits?: number | string; 245 | markerWidth?: number | string; 246 | mask?: string; 247 | maskContentUnits?: number | string; 248 | maskUnits?: number | string; 249 | mathematical?: number | string; 250 | mode?: number | string; 251 | numOctaves?: number | string; 252 | offset?: number | string; 253 | opacity?: number | string; 254 | operator?: number | string; 255 | order?: number | string; 256 | orient?: number | string; 257 | orientation?: number | string; 258 | origin?: number | string; 259 | overflow?: number | string; 260 | overlinePosition?: number | string; 261 | overlineThickness?: number | string; 262 | paintOrder?: number | string; 263 | panose1?: number | string; 264 | pathLength?: number | string; 265 | patternContentUnits?: string; 266 | patternTransform?: number | string; 267 | patternUnits?: string; 268 | pointerEvents?: number | string; 269 | points?: string; 270 | pointsAtX?: number | string; 271 | pointsAtY?: number | string; 272 | pointsAtZ?: number | string; 273 | preserveAlpha?: number | string; 274 | preserveAspectRatio?: string; 275 | primitiveUnits?: number | string; 276 | r?: number | string; 277 | radius?: number | string; 278 | refX?: number | string; 279 | refY?: number | string; 280 | renderingIntent?: number | string; 281 | repeatCount?: number | string; 282 | repeatDur?: number | string; 283 | requiredExtensions?: number | string; 284 | requiredFeatures?: number | string; 285 | restart?: number | string; 286 | result?: string; 287 | rotate?: number | string; 288 | rx?: number | string; 289 | ry?: number | string; 290 | scale?: number | string; 291 | seed?: number | string; 292 | shapeRendering?: number | string; 293 | slope?: number | string; 294 | spacing?: number | string; 295 | specularConstant?: number | string; 296 | specularExponent?: number | string; 297 | speed?: number | string; 298 | spreadMethod?: string; 299 | startOffset?: number | string; 300 | stdDeviation?: number | string; 301 | stemh?: number | string; 302 | stemv?: number | string; 303 | stitchTiles?: number | string; 304 | stopColor?: string; 305 | stopOpacity?: number | string; 306 | strikethroughPosition?: number | string; 307 | strikethroughThickness?: number | string; 308 | string?: number | string; 309 | stroke?: string; 310 | strokeDasharray?: string | number; 311 | strokeDashoffset?: string | number; 312 | strokeLinecap?: "butt" | "round" | "square" | "inherit"; 313 | strokeLinejoin?: "miter" | "round" | "bevel" | "inherit"; 314 | strokeMiterlimit?: string | number; 315 | strokeOpacity?: number | string; 316 | strokeWidth?: number | string; 317 | surfaceScale?: number | string; 318 | systemLanguage?: number | string; 319 | tableValues?: number | string; 320 | targetX?: number | string; 321 | targetY?: number | string; 322 | textAnchor?: string; 323 | textDecoration?: number | string; 324 | textLength?: number | string; 325 | textRendering?: number | string; 326 | to?: number | string; 327 | transform?: string; 328 | u1?: number | string; 329 | u2?: number | string; 330 | underlinePosition?: number | string; 331 | underlineThickness?: number | string; 332 | unicode?: number | string; 333 | unicodeBidi?: number | string; 334 | unicodeRange?: number | string; 335 | unitsPerEm?: number | string; 336 | vAlphabetic?: number | string; 337 | values?: string; 338 | vectorEffect?: number | string; 339 | version?: string; 340 | vertAdvY?: number | string; 341 | vertOriginX?: number | string; 342 | vertOriginY?: number | string; 343 | vHanging?: number | string; 344 | vIdeographic?: number | string; 345 | viewBox?: string; 346 | viewTarget?: number | string; 347 | visibility?: number | string; 348 | vMathematical?: number | string; 349 | widths?: number | string; 350 | wordSpacing?: number | string; 351 | writingMode?: number | string; 352 | x1?: number | string; 353 | x2?: number | string; 354 | x?: number | string; 355 | xChannelSelector?: string; 356 | xHeight?: number | string; 357 | xlinkActuate?: string; 358 | xlinkArcrole?: string; 359 | xlinkHref?: string; 360 | xlinkRole?: string; 361 | xlinkShow?: string; 362 | xlinkTitle?: string; 363 | xlinkType?: string; 364 | xmlBase?: string; 365 | xmlLang?: string; 366 | xmlns?: string; 367 | xmlnsXlink?: string; 368 | xmlSpace?: string; 369 | y1?: number | string; 370 | y2?: number | string; 371 | y?: number | string; 372 | yChannelSelector?: string; 373 | z?: number | string; 374 | zoomAndPan?: string; 375 | } 376 | 377 | export interface PathAttributes { 378 | d: string; 379 | } 380 | 381 | export type TargetedEvent< 382 | Target extends EventTarget = EventTarget, 383 | TypedEvent extends Event = Event, 384 | > = Omit & { 385 | readonly currentTarget: Target; 386 | }; 387 | 388 | export type TargetedAnimationEvent< 389 | Target extends EventTarget, 390 | > = TargetedEvent; 391 | export type TargetedClipboardEvent< 392 | Target extends EventTarget, 393 | > = TargetedEvent; 394 | export type TargetedCompositionEvent< 395 | Target extends EventTarget, 396 | > = TargetedEvent; 397 | export type TargetedDragEvent = TargetedEvent< 398 | Target, 399 | DragEvent 400 | >; 401 | export type TargetedFocusEvent = TargetedEvent< 402 | Target, 403 | FocusEvent 404 | >; 405 | export type TargetedKeyboardEvent = TargetedEvent< 406 | Target, 407 | KeyboardEvent 408 | >; 409 | export type TargetedMouseEvent = TargetedEvent< 410 | Target, 411 | MouseEvent 412 | >; 413 | export type TargetedPointerEvent = TargetedEvent< 414 | Target, 415 | PointerEvent 416 | >; 417 | export type TargetedTouchEvent = TargetedEvent< 418 | Target, 419 | TouchEvent 420 | >; 421 | export type TargetedTransitionEvent< 422 | Target extends EventTarget, 423 | > = TargetedEvent; 424 | export type TargetedUIEvent = TargetedEvent< 425 | Target, 426 | UIEvent 427 | >; 428 | export type TargetedWheelEvent = TargetedEvent< 429 | Target, 430 | WheelEvent 431 | >; 432 | 433 | export interface EventHandler { 434 | /** 435 | * The `this` keyword always points to the DOM element the event handler 436 | * was invoked on. See: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Event_handlers#Event_handlers_parameters_this_binding_and_the_return_value 437 | */ 438 | (this: never, event: E): void; 439 | } 440 | 441 | export type AnimationEventHandler = EventHandler< 442 | TargetedAnimationEvent 443 | >; 444 | export type ClipboardEventHandler = EventHandler< 445 | TargetedClipboardEvent 446 | >; 447 | export type CompositionEventHandler< 448 | Target extends EventTarget, 449 | > = EventHandler>; 450 | export type DragEventHandler = EventHandler< 451 | TargetedDragEvent 452 | >; 453 | export type FocusEventHandler = EventHandler< 454 | TargetedFocusEvent 455 | >; 456 | export type GenericEventHandler = EventHandler< 457 | TargetedEvent 458 | >; 459 | export type KeyboardEventHandler = EventHandler< 460 | TargetedKeyboardEvent 461 | >; 462 | export type MouseEventHandler = EventHandler< 463 | TargetedMouseEvent 464 | >; 465 | export type PointerEventHandler = EventHandler< 466 | TargetedPointerEvent 467 | >; 468 | export type TouchEventHandler = EventHandler< 469 | TargetedTouchEvent 470 | >; 471 | export type TransitionEventHandler = EventHandler< 472 | TargetedTransitionEvent 473 | >; 474 | export type UIEventHandler = EventHandler< 475 | TargetedUIEvent 476 | >; 477 | export type WheelEventHandler = EventHandler< 478 | TargetedWheelEvent 479 | >; 480 | 481 | export interface DOMAttributes 482 | extends BaseDOMAttributes { 483 | // Image Events 484 | onLoad?: GenericEventHandler; 485 | onLoadCapture?: GenericEventHandler; 486 | onError?: GenericEventHandler; 487 | onErrorCapture?: GenericEventHandler; 488 | 489 | // Clipboard Events 490 | onCopy?: ClipboardEventHandler; 491 | onCopyCapture?: ClipboardEventHandler; 492 | onCut?: ClipboardEventHandler; 493 | onCutCapture?: ClipboardEventHandler; 494 | onPaste?: ClipboardEventHandler; 495 | onPasteCapture?: ClipboardEventHandler; 496 | 497 | // Composition Events 498 | onCompositionEnd?: CompositionEventHandler; 499 | onCompositionEndCapture?: CompositionEventHandler; 500 | onCompositionStart?: CompositionEventHandler; 501 | onCompositionStartCapture?: CompositionEventHandler; 502 | onCompositionUpdate?: CompositionEventHandler; 503 | onCompositionUpdateCapture?: CompositionEventHandler; 504 | 505 | // Details Events 506 | onToggle?: GenericEventHandler; 507 | 508 | // Focus Events 509 | onFocus?: FocusEventHandler; 510 | onFocusCapture?: FocusEventHandler; 511 | onfocusin?: FocusEventHandler; 512 | onfocusinCapture?: FocusEventHandler; 513 | onfocusout?: FocusEventHandler; 514 | onfocusoutCapture?: FocusEventHandler; 515 | onBlur?: FocusEventHandler; 516 | onBlurCapture?: FocusEventHandler; 517 | 518 | // Form Events 519 | onChange?: GenericEventHandler; 520 | onChangeCapture?: GenericEventHandler; 521 | onInput?: GenericEventHandler; 522 | onInputCapture?: GenericEventHandler; 523 | onBeforeInput?: GenericEventHandler; 524 | onBeforeInputCapture?: GenericEventHandler; 525 | onSearch?: GenericEventHandler; 526 | onSearchCapture?: GenericEventHandler; 527 | onSubmit?: GenericEventHandler; 528 | onSubmitCapture?: GenericEventHandler; 529 | onInvalid?: GenericEventHandler; 530 | onInvalidCapture?: GenericEventHandler; 531 | onReset?: GenericEventHandler; 532 | onResetCapture?: GenericEventHandler; 533 | onFormData?: GenericEventHandler; 534 | onFormDataCapture?: GenericEventHandler; 535 | 536 | // Keyboard Events 537 | onKeyDown?: KeyboardEventHandler; 538 | onKeyDownCapture?: KeyboardEventHandler; 539 | onKeyPress?: KeyboardEventHandler; 540 | onKeyPressCapture?: KeyboardEventHandler; 541 | onKeyUp?: KeyboardEventHandler; 542 | onKeyUpCapture?: KeyboardEventHandler; 543 | 544 | // Media Events 545 | onAbort?: GenericEventHandler; 546 | onAbortCapture?: GenericEventHandler; 547 | onCanPlay?: GenericEventHandler; 548 | onCanPlayCapture?: GenericEventHandler; 549 | onCanPlayThrough?: GenericEventHandler; 550 | onCanPlayThroughCapture?: GenericEventHandler; 551 | onDurationChange?: GenericEventHandler; 552 | onDurationChangeCapture?: GenericEventHandler; 553 | onEmptied?: GenericEventHandler; 554 | onEmptiedCapture?: GenericEventHandler; 555 | onEncrypted?: GenericEventHandler; 556 | onEncryptedCapture?: GenericEventHandler; 557 | onEnded?: GenericEventHandler; 558 | onEndedCapture?: GenericEventHandler; 559 | onLoadedData?: GenericEventHandler; 560 | onLoadedDataCapture?: GenericEventHandler; 561 | onLoadedMetadata?: GenericEventHandler; 562 | onLoadedMetadataCapture?: GenericEventHandler; 563 | onLoadStart?: GenericEventHandler; 564 | onLoadStartCapture?: GenericEventHandler; 565 | onPause?: GenericEventHandler; 566 | onPauseCapture?: GenericEventHandler; 567 | onPlay?: GenericEventHandler; 568 | onPlayCapture?: GenericEventHandler; 569 | onPlaying?: GenericEventHandler; 570 | onPlayingCapture?: GenericEventHandler; 571 | onProgress?: GenericEventHandler; 572 | onProgressCapture?: GenericEventHandler; 573 | onRateChange?: GenericEventHandler; 574 | onRateChangeCapture?: GenericEventHandler; 575 | onSeeked?: GenericEventHandler; 576 | onSeekedCapture?: GenericEventHandler; 577 | onSeeking?: GenericEventHandler; 578 | onSeekingCapture?: GenericEventHandler; 579 | onStalled?: GenericEventHandler; 580 | onStalledCapture?: GenericEventHandler; 581 | onSuspend?: GenericEventHandler; 582 | onSuspendCapture?: GenericEventHandler; 583 | onTimeUpdate?: GenericEventHandler; 584 | onTimeUpdateCapture?: GenericEventHandler; 585 | onVolumeChange?: GenericEventHandler; 586 | onVolumeChangeCapture?: GenericEventHandler; 587 | onWaiting?: GenericEventHandler; 588 | onWaitingCapture?: GenericEventHandler; 589 | 590 | // MouseEvents 591 | onClick?: MouseEventHandler; 592 | onClickCapture?: MouseEventHandler; 593 | onContextMenu?: MouseEventHandler; 594 | onContextMenuCapture?: MouseEventHandler; 595 | onDblClick?: MouseEventHandler; 596 | onDblClickCapture?: MouseEventHandler; 597 | onDrag?: DragEventHandler; 598 | onDragCapture?: DragEventHandler; 599 | onDragEnd?: DragEventHandler; 600 | onDragEndCapture?: DragEventHandler; 601 | onDragEnter?: DragEventHandler; 602 | onDragEnterCapture?: DragEventHandler; 603 | onDragExit?: DragEventHandler; 604 | onDragExitCapture?: DragEventHandler; 605 | onDragLeave?: DragEventHandler; 606 | onDragLeaveCapture?: DragEventHandler; 607 | onDragOver?: DragEventHandler; 608 | onDragOverCapture?: DragEventHandler; 609 | onDragStart?: DragEventHandler; 610 | onDragStartCapture?: DragEventHandler; 611 | onDrop?: DragEventHandler; 612 | onDropCapture?: DragEventHandler; 613 | onMouseDown?: MouseEventHandler; 614 | onMouseDownCapture?: MouseEventHandler; 615 | onMouseEnter?: MouseEventHandler; 616 | onMouseEnterCapture?: MouseEventHandler; 617 | onMouseLeave?: MouseEventHandler; 618 | onMouseLeaveCapture?: MouseEventHandler; 619 | onMouseMove?: MouseEventHandler; 620 | onMouseMoveCapture?: MouseEventHandler; 621 | onMouseOut?: MouseEventHandler; 622 | onMouseOutCapture?: MouseEventHandler; 623 | onMouseOver?: MouseEventHandler; 624 | onMouseOverCapture?: MouseEventHandler; 625 | onMouseUp?: MouseEventHandler; 626 | onMouseUpCapture?: MouseEventHandler; 627 | 628 | // Selection Events 629 | onSelect?: GenericEventHandler; 630 | onSelectCapture?: GenericEventHandler; 631 | 632 | // Touch Events 633 | onTouchCancel?: TouchEventHandler; 634 | onTouchCancelCapture?: TouchEventHandler; 635 | onTouchEnd?: TouchEventHandler; 636 | onTouchEndCapture?: TouchEventHandler; 637 | onTouchMove?: TouchEventHandler; 638 | onTouchMoveCapture?: TouchEventHandler; 639 | onTouchStart?: TouchEventHandler; 640 | onTouchStartCapture?: TouchEventHandler; 641 | 642 | // Pointer Events 643 | onPointerOver?: PointerEventHandler; 644 | onPointerOverCapture?: PointerEventHandler; 645 | onPointerEnter?: PointerEventHandler; 646 | onPointerEnterCapture?: PointerEventHandler; 647 | onPointerDown?: PointerEventHandler; 648 | onPointerDownCapture?: PointerEventHandler; 649 | onPointerMove?: PointerEventHandler; 650 | onPointerMoveCapture?: PointerEventHandler; 651 | onPointerUp?: PointerEventHandler; 652 | onPointerUpCapture?: PointerEventHandler; 653 | onPointerCancel?: PointerEventHandler; 654 | onPointerCancelCapture?: PointerEventHandler; 655 | onPointerOut?: PointerEventHandler; 656 | onPointerOutCapture?: PointerEventHandler; 657 | onPointerLeave?: PointerEventHandler; 658 | onPointerLeaveCapture?: PointerEventHandler; 659 | onGotPointerCapture?: PointerEventHandler; 660 | onGotPointerCaptureCapture?: PointerEventHandler; 661 | onLostPointerCapture?: PointerEventHandler; 662 | onLostPointerCaptureCapture?: PointerEventHandler; 663 | 664 | // UI Events 665 | onScroll?: UIEventHandler; 666 | onScrollCapture?: UIEventHandler; 667 | 668 | // Wheel Events 669 | onWheel?: WheelEventHandler; 670 | onWheelCapture?: WheelEventHandler; 671 | 672 | // Animation Events 673 | onAnimationStart?: AnimationEventHandler; 674 | onAnimationStartCapture?: AnimationEventHandler; 675 | onAnimationEnd?: AnimationEventHandler; 676 | onAnimationEndCapture?: AnimationEventHandler; 677 | onAnimationIteration?: AnimationEventHandler; 678 | onAnimationIterationCapture?: AnimationEventHandler; 679 | 680 | // Transition Events 681 | onTransitionEnd?: TransitionEventHandler; 682 | onTransitionEndCapture?: TransitionEventHandler; 683 | } 684 | 685 | export interface HTMLAttributes 686 | extends ClassAttributes, DOMAttributes { 687 | // Standard HTML Attributes 688 | accept?: string; 689 | acceptCharset?: string; 690 | accessKey?: string; 691 | action?: string; 692 | allow?: string; 693 | allowFullScreen?: boolean; 694 | allowTransparency?: boolean; 695 | alt?: string; 696 | as?: string; 697 | async?: boolean; 698 | autocomplete?: string; 699 | autoComplete?: string; 700 | autocorrect?: string; 701 | autoCorrect?: string; 702 | autofocus?: boolean; 703 | autoFocus?: boolean; 704 | autoPlay?: boolean; 705 | capture?: boolean | string; 706 | cellPadding?: number | string; 707 | cellSpacing?: number | string; 708 | charSet?: string; 709 | challenge?: string; 710 | checked?: boolean; 711 | cite?: string; 712 | class?: string; 713 | className?: string; 714 | cols?: number; 715 | colSpan?: number; 716 | content?: string; 717 | contentEditable?: boolean; 718 | contextMenu?: string; 719 | controls?: boolean; 720 | controlsList?: string; 721 | coords?: string; 722 | crossOrigin?: string; 723 | data?: string; 724 | dateTime?: string; 725 | default?: boolean; 726 | defaultChecked?: boolean; 727 | defaultValue?: string; 728 | defer?: boolean; 729 | dir?: "auto" | "rtl" | "ltr"; 730 | disabled?: boolean; 731 | disableRemotePlayback?: boolean; 732 | download?: any; 733 | decoding?: "sync" | "async" | "auto"; 734 | draggable?: boolean; 735 | encType?: string; 736 | enterkeyhint?: 737 | | "enter" 738 | | "done" 739 | | "go" 740 | | "next" 741 | | "previous" 742 | | "search" 743 | | "send"; 744 | form?: string; 745 | formAction?: string; 746 | formEncType?: string; 747 | formMethod?: string; 748 | formNoValidate?: boolean; 749 | formTarget?: string; 750 | frameBorder?: number | string; 751 | headers?: string; 752 | height?: number | string; 753 | hidden?: boolean; 754 | high?: number; 755 | href?: string; 756 | hrefLang?: string; 757 | for?: string; 758 | htmlFor?: string; 759 | httpEquiv?: string; 760 | icon?: string; 761 | id?: string; 762 | inputMode?: string; 763 | integrity?: string; 764 | is?: string; 765 | keyParams?: string; 766 | keyType?: string; 767 | kind?: string; 768 | label?: string; 769 | lang?: string; 770 | list?: string; 771 | loading?: "eager" | "lazy"; 772 | loop?: boolean; 773 | low?: number; 774 | manifest?: string; 775 | marginHeight?: number; 776 | marginWidth?: number; 777 | max?: number | string; 778 | maxLength?: number; 779 | media?: string; 780 | mediaGroup?: string; 781 | method?: string; 782 | min?: number | string; 783 | minLength?: number; 784 | multiple?: boolean; 785 | muted?: boolean; 786 | name?: string; 787 | nomodule?: boolean; 788 | nonce?: string; 789 | noValidate?: boolean; 790 | open?: boolean; 791 | optimum?: number; 792 | pattern?: string; 793 | ping?: string; 794 | placeholder?: string; 795 | playsInline?: boolean; 796 | poster?: string; 797 | preload?: string; 798 | radioGroup?: string; 799 | readonly?: boolean; 800 | readOnly?: boolean; 801 | referrerpolicy?: 802 | | "no-referrer" 803 | | "no-referrer-when-downgrade" 804 | | "origin" 805 | | "origin-when-cross-origin" 806 | | "same-origin" 807 | | "strict-origin" 808 | | "strict-origin-when-cross-origin" 809 | | "unsafe-url"; 810 | rel?: string; 811 | required?: boolean; 812 | reversed?: boolean; 813 | role?: string; 814 | rows?: number; 815 | rowSpan?: number; 816 | sandbox?: string; 817 | scope?: string; 818 | scoped?: boolean; 819 | scrolling?: string; 820 | seamless?: boolean; 821 | selected?: boolean; 822 | shape?: string; 823 | size?: number; 824 | sizes?: string; 825 | slot?: string; 826 | span?: number; 827 | spellcheck?: boolean; 828 | spellCheck?: boolean; 829 | src?: string; 830 | srcset?: string; 831 | srcDoc?: string; 832 | srcLang?: string; 833 | srcSet?: string; 834 | start?: number; 835 | step?: number | string; 836 | style?: string | CSSProperties; 837 | summary?: string; 838 | tabIndex?: number; 839 | target?: string; 840 | title?: string; 841 | type?: string; 842 | useMap?: string; 843 | value?: string | string[] | number; 844 | volume?: string | number; 845 | width?: number | string; 846 | wmode?: string; 847 | wrap?: string; 848 | 849 | // Non-standard Attributes 850 | autocapitalize?: 851 | | "off" 852 | | "none" 853 | | "on" 854 | | "sentences" 855 | | "words" 856 | | "characters"; 857 | autoCapitalize?: 858 | | "off" 859 | | "none" 860 | | "on" 861 | | "sentences" 862 | | "words" 863 | | "characters"; 864 | disablePictureInPicture?: boolean; 865 | results?: number; 866 | translate?: "yes" | "no"; 867 | 868 | // RDFa Attributes 869 | about?: string; 870 | datatype?: string; 871 | inlist?: any; 872 | prefix?: string; 873 | property?: string; 874 | resource?: string; 875 | typeof?: string; 876 | vocab?: string; 877 | 878 | // Microdata Attributes 879 | itemProp?: string; 880 | itemScope?: boolean; 881 | itemType?: string; 882 | itemID?: string; 883 | itemRef?: string; 884 | } 885 | 886 | export type DetailedHTMLProps< 887 | HA extends HTMLAttributes, 888 | RefType extends EventTarget = EventTarget, 889 | > = HA; 890 | 891 | export interface HTMLMarqueeElement extends HTMLElement { 892 | behavior?: "scroll" | "slide" | "alternate"; 893 | bgColor?: string; 894 | direction?: "left" | "right" | "up" | "down"; 895 | height?: number | string; 896 | hspace?: number | string; 897 | loop?: number | string; 898 | scrollAmount?: number | string; 899 | scrollDelay?: number | string; 900 | trueSpeed?: boolean; 901 | vspace?: number | string; 902 | width?: number | string; 903 | } 904 | 905 | export interface IntrinsicElements { 906 | // HTML 907 | a: HTMLAttributes; 908 | abbr: HTMLAttributes; 909 | address: HTMLAttributes; 910 | area: HTMLAttributes; 911 | article: HTMLAttributes; 912 | aside: HTMLAttributes; 913 | audio: HTMLAttributes; 914 | b: HTMLAttributes; 915 | base: HTMLAttributes; 916 | bdi: HTMLAttributes; 917 | bdo: HTMLAttributes; 918 | big: HTMLAttributes; 919 | blockquote: HTMLAttributes; 920 | body: HTMLAttributes; 921 | br: HTMLAttributes; 922 | button: HTMLAttributes; 923 | canvas: HTMLAttributes; 924 | caption: HTMLAttributes; 925 | cite: HTMLAttributes; 926 | code: HTMLAttributes; 927 | col: HTMLAttributes; 928 | colgroup: HTMLAttributes; 929 | data: HTMLAttributes; 930 | datalist: HTMLAttributes; 931 | dd: HTMLAttributes; 932 | del: HTMLAttributes; 933 | details: HTMLAttributes; 934 | dfn: HTMLAttributes; 935 | dialog: HTMLAttributes; 936 | div: HTMLAttributes; 937 | dl: HTMLAttributes; 938 | dt: HTMLAttributes; 939 | em: HTMLAttributes; 940 | embed: HTMLAttributes; 941 | fieldset: HTMLAttributes; 942 | figcaption: HTMLAttributes; 943 | figure: HTMLAttributes; 944 | footer: HTMLAttributes; 945 | form: HTMLAttributes; 946 | h1: HTMLAttributes; 947 | h2: HTMLAttributes; 948 | h3: HTMLAttributes; 949 | h4: HTMLAttributes; 950 | h5: HTMLAttributes; 951 | h6: HTMLAttributes; 952 | head: HTMLAttributes; 953 | header: HTMLAttributes; 954 | hgroup: HTMLAttributes; 955 | hr: HTMLAttributes; 956 | html: HTMLAttributes; 957 | i: HTMLAttributes; 958 | iframe: HTMLAttributes; 959 | img: HTMLAttributes; 960 | input: HTMLAttributes & { defaultValue?: string }; 961 | ins: HTMLAttributes; 962 | kbd: HTMLAttributes; 963 | keygen: HTMLAttributes; 964 | label: HTMLAttributes; 965 | legend: HTMLAttributes; 966 | li: HTMLAttributes; 967 | link: HTMLAttributes; 968 | main: HTMLAttributes; 969 | map: HTMLAttributes; 970 | mark: HTMLAttributes; 971 | marquee: HTMLAttributes; 972 | menu: HTMLAttributes; 973 | menuitem: HTMLAttributes; 974 | meta: HTMLAttributes; 975 | meter: HTMLAttributes; 976 | nav: HTMLAttributes; 977 | noscript: HTMLAttributes; 978 | object: HTMLAttributes; 979 | ol: HTMLAttributes; 980 | optgroup: HTMLAttributes; 981 | option: HTMLAttributes; 982 | output: HTMLAttributes; 983 | p: HTMLAttributes; 984 | param: HTMLAttributes; 985 | picture: HTMLAttributes; 986 | pre: HTMLAttributes; 987 | progress: HTMLAttributes; 988 | q: HTMLAttributes; 989 | rp: HTMLAttributes; 990 | rt: HTMLAttributes; 991 | ruby: HTMLAttributes; 992 | s: HTMLAttributes; 993 | samp: HTMLAttributes; 994 | script: HTMLAttributes; 995 | section: HTMLAttributes; 996 | select: HTMLAttributes; 997 | slot: HTMLAttributes; 998 | small: HTMLAttributes; 999 | source: HTMLAttributes; 1000 | span: HTMLAttributes; 1001 | strong: HTMLAttributes; 1002 | style: HTMLAttributes; 1003 | sub: HTMLAttributes; 1004 | summary: HTMLAttributes; 1005 | sup: HTMLAttributes; 1006 | table: HTMLAttributes; 1007 | tbody: HTMLAttributes; 1008 | td: HTMLAttributes; 1009 | textarea: HTMLAttributes; 1010 | tfoot: HTMLAttributes; 1011 | th: HTMLAttributes; 1012 | thead: HTMLAttributes; 1013 | time: HTMLAttributes; 1014 | title: HTMLAttributes; 1015 | tr: HTMLAttributes; 1016 | track: HTMLAttributes; 1017 | u: HTMLAttributes; 1018 | ul: HTMLAttributes; 1019 | var: HTMLAttributes; 1020 | video: HTMLAttributes; 1021 | wbr: HTMLAttributes; 1022 | 1023 | //SVG 1024 | svg: SVGAttributes; 1025 | animate: SVGAttributes; 1026 | circle: SVGAttributes; 1027 | animateTransform: SVGAttributes; 1028 | clipPath: SVGAttributes; 1029 | defs: SVGAttributes; 1030 | desc: SVGAttributes; 1031 | ellipse: SVGAttributes; 1032 | feBlend: SVGAttributes; 1033 | feColorMatrix: SVGAttributes; 1034 | feComponentTransfer: SVGAttributes; 1035 | feComposite: SVGAttributes; 1036 | feConvolveMatrix: SVGAttributes; 1037 | feDiffuseLighting: SVGAttributes; 1038 | feDisplacementMap: SVGAttributes; 1039 | feDropShadow: SVGAttributes; 1040 | feFlood: SVGAttributes; 1041 | feFuncA: SVGAttributes; 1042 | feFuncB: SVGAttributes; 1043 | feFuncG: SVGAttributes; 1044 | feFuncR: SVGAttributes; 1045 | feGaussianBlur: SVGAttributes; 1046 | feImage: SVGAttributes; 1047 | feMerge: SVGAttributes; 1048 | feMergeNode: SVGAttributes; 1049 | feMorphology: SVGAttributes; 1050 | feOffset: SVGAttributes; 1051 | feSpecularLighting: SVGAttributes; 1052 | feTile: SVGAttributes; 1053 | feTurbulence: SVGAttributes; 1054 | filter: SVGAttributes; 1055 | foreignObject: SVGAttributes; 1056 | g: SVGAttributes; 1057 | image: SVGAttributes; 1058 | line: SVGAttributes; 1059 | linearGradient: SVGAttributes; 1060 | marker: SVGAttributes; 1061 | mask: SVGAttributes; 1062 | path: SVGAttributes; 1063 | pattern: SVGAttributes; 1064 | polygon: SVGAttributes; 1065 | polyline: SVGAttributes; 1066 | radialGradient: SVGAttributes; 1067 | rect: SVGAttributes; 1068 | stop: SVGAttributes; 1069 | symbol: SVGAttributes; 1070 | text: SVGAttributes; 1071 | tspan: SVGAttributes; 1072 | use: SVGAttributes; 1073 | } 1074 | } 1075 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2022 the oak authors. All rights reserved. MIT License. 2 | 3 | /** A server side rendering framework for Deno CLI and Deploy. 4 | * 5 | * Incorporating [acorn](https://deno.land/x/acorn/), 6 | * [nano-jsx](https://nanojsx.io/), and [twind](https://twind.dev/), it provides 7 | * the tooling to provide a server centric framework for providing dynamic 8 | * websites served from the edge. 9 | * 10 | * ### Example 11 | * 12 | * ```ts 13 | * import { Fragment, h, init, render, tw } from "../mod.ts"; 14 | * 15 | * const router = init(); 16 | * const style = createStyle({ 17 | * title: "text-xl", 18 | * }); 19 | * 20 | * const App = ({ name }: { name: string }) => ( 21 | * <> 22 | *

Hello {name}!
23 | * 24 | * ); 25 | * 26 | * router.get("/", render()); 27 | * 28 | * router.listen(); 29 | * ``` 30 | * 31 | * @module 32 | */ 33 | 34 | import "./jsx.d.ts"; 35 | 36 | export { h } from "nano-jsx/core"; 37 | export { Fragment } from "nano-jsx/fragment"; 38 | export { apply, tw } from "twind"; 39 | export { css } from "twind/css"; 40 | 41 | export { render } from "./handlers.ts"; 42 | export { init } from "./init.ts"; 43 | export { createStyle } from "./styles.ts"; 44 | -------------------------------------------------------------------------------- /setup.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2022 the oak authors. All rights reserved. MIT License. 2 | 3 | import originalImportMap from "./import-map.json" assert { type: "json" }; 4 | 5 | async function exists(path: string): Promise { 6 | try { 7 | await Deno.lstat(path); 8 | } catch { 9 | return false; 10 | } 11 | return true; 12 | } 13 | 14 | const VERSION = "0.0.1"; 15 | 16 | const importMapJson = { 17 | imports: { 18 | nat: `https://deno.land/x/nat@${VERSION}/mod.ts`, 19 | ...originalImportMap.imports, 20 | }, 21 | }; 22 | 23 | const denoJsonc = { 24 | importMap: "./import-map.json", 25 | tasks: { 26 | start: "deno run --check --allow-net --allow-hrtime main.tsx", 27 | }, 28 | }; 29 | 30 | const mainTsx = `/** @jsx h */ 31 | /** @jsxFrag Fragment */ 32 | import { Fragment, init, h, render, tw } from "nat"; 33 | 34 | function App() { 35 | return

Hello from nat!

; 36 | } 37 | 38 | const router = init(); 39 | 40 | // add routes here, like: 41 | router.get("/", render()); 42 | 43 | router.listen(); 44 | `; 45 | 46 | console.log( 47 | `%cnat %cv${VERSION} %c- setup`, 48 | "color:cyan", 49 | "color:yellow", 50 | "color:none", 51 | ); 52 | 53 | // check permissions and existing files 54 | try { 55 | await Deno.permissions.request({ name: "read", path: "." }); 56 | await Deno.permissions.request({ name: "write", path: "." }); 57 | } catch { 58 | console.log( 59 | "ERROR: setup requires read and write permissions to the current directory", 60 | ); 61 | } 62 | if (await exists("./deno.json")) { 63 | console.log( 64 | "%cERROR%c: an existing deno.json exists in the current directory.", 65 | "color:red", 66 | "color:none", 67 | ); 68 | Deno.exit(1); 69 | } 70 | if (await exists("./deno.jsonc")) { 71 | console.log( 72 | "%cERROR%c: an existing deno.jsonc exists in the current directory.", 73 | "color:red", 74 | "color:none", 75 | ); 76 | Deno.exit(1); 77 | } 78 | if (await exists("./import-map.json")) { 79 | console.log( 80 | "%cERROR%c: an existing import-map.json exists in the current directory.", 81 | "color:red", 82 | "color:none", 83 | ); 84 | Deno.exit(1); 85 | } 86 | if (await exists("./main.tsx")) { 87 | console.log( 88 | "%cERROR%c: an existing main.tsx exists in the current directory.", 89 | "color:red", 90 | "color:none", 91 | ); 92 | Deno.exit(1); 93 | } 94 | 95 | console.log( 96 | `Setup will write out "deno.jsonc", "import-map.json", and "main.tsx".`, 97 | ); 98 | if (!globalThis.confirm("Continue?")) { 99 | console.log( 100 | "%cExiting%c without writing files.", 101 | "color:yellow", 102 | "color:none", 103 | ); 104 | Deno.exit(0); 105 | } 106 | 107 | console.log( 108 | "%cWriting %cdeno.jsonc%c...", 109 | "color:green", 110 | "color:yellow", 111 | "color:none", 112 | ); 113 | await Deno.writeTextFile( 114 | "./deno.jsonc", 115 | JSON.stringify(denoJsonc, undefined, " "), 116 | ); 117 | console.log( 118 | "%cWriting %cimport-map.json%c...", 119 | "color:green", 120 | "color:yellow", 121 | "color:none", 122 | ); 123 | await Deno.writeTextFile( 124 | "./import-map.json", 125 | JSON.stringify(importMapJson, undefined, " "), 126 | ); 127 | console.log( 128 | "%cWriting %cmain.tsx%c...", 129 | "color:green", 130 | "color:yellow", 131 | "color:none", 132 | ); 133 | await Deno.writeTextFile("./main.tsx", mainTsx); 134 | 135 | console.log("%cFinished.", "color:green"); 136 | 137 | console.log( 138 | `\nTo get started, edit "main.tsx" and run "deno task start" to run locally.`, 139 | ); 140 | -------------------------------------------------------------------------------- /styles.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2022 the oak authors. All rights reserved. MIT License. 2 | 3 | /** An abstraction over twind which allows creating re-usable styles that can 4 | * then be inlined into HTML or JSX classes. 5 | * 6 | * ### Example 7 | * 8 | * ```ts 9 | * import { createStyle, h } from "https://deno.land/x/nat/mod.ts"; 10 | * 11 | * const style = createStyle({ 12 | * title: "font-xl bold", 13 | * link: "underline", 14 | * }); 15 | * 16 | * function App() { 17 | * return ( 18 | *
19 | *

Title

20 | * a 21 | *

22 | * ); 23 | * } 24 | * ``` 25 | * 26 | * @module 27 | */ 28 | 29 | import { type CSSRules, type Directive, type Token, tw } from "twind"; 30 | 31 | function isIterator( 32 | value: unknown, 33 | ): value is Iterable<[Keys, string]> { 34 | return typeof value === "object" && value !== null && 35 | Symbol.iterator in value; 36 | } 37 | 38 | export interface StyleFunction { 39 | /** For a provided style key, return a string value after having been 40 | * processed by the {@linkcode tw} function. */ 41 | (style: Keys): string; 42 | /** For a provided style key, return the original value passed to the the 43 | * factory {@linkcode createStyle} function. */ 44 | (style: Keys, raw: boolean): Token; 45 | } 46 | 47 | /** A factory function that will return a {@link StyleFunction} that will make 48 | * it easy to re-use groupings of twind/tailwind classes. */ 49 | export function createStyle( 50 | styles: 51 | | Record> 52 | | Iterable<[Keys, Directive]>, 53 | ): StyleFunction { 54 | const s = 55 | (isIterator(styles) 56 | ? Object.fromEntries(styles) as Record 57 | : styles) as Record>; 58 | return function style(style: Keys, raw: boolean) { 59 | const value = s[style] ?? ""; 60 | return raw ? value : tw(value); 61 | } as StyleFunction; 62 | } 63 | --------------------------------------------------------------------------------