├── .gitignore ├── .dockerignore ├── packages ├── parabola │ ├── src │ │ ├── index.ts │ │ ├── bus.ts │ │ ├── dispatcher.ts │ │ ├── renderer.ts │ │ ├── server.tsx │ │ └── parabola.js │ ├── package.json │ ├── tsconfig.json │ └── .gitignore └── example │ ├── src │ ├── styles.css │ ├── pages │ │ ├── counter.tsx │ │ ├── poll.tsx │ │ ├── grid.tsx │ │ ├── chat.tsx │ │ ├── views.tsx │ │ └── main.tsx │ └── index.tsx │ ├── tailwind.config.js │ ├── README.md │ ├── tsconfig.json │ ├── package.json │ └── .gitignore ├── bun.lockb ├── package.json ├── README.md ├── Dockerfile └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .git/ -------------------------------------------------------------------------------- /packages/parabola/src/index.ts: -------------------------------------------------------------------------------- 1 | export { Parabola } from "./server"; 2 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webdevcody/parabolajs/HEAD/bun.lockb -------------------------------------------------------------------------------- /packages/example/src/styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parabolajs", 3 | "private": true, 4 | "workspaces": [ 5 | "packages/*" 6 | ], 7 | "scripts": { 8 | "dev": "cd packages/example && bun run dev", 9 | "example": "cd packages/example && bun run start" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/example/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./src/**/*.{html,js,tsx,jsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | daisyui: { 8 | themes: ["night"], 9 | }, 10 | plugins: [require("daisyui")], 11 | }; 12 | -------------------------------------------------------------------------------- /packages/example/README.md: -------------------------------------------------------------------------------- 1 | # @parabolajs/example 2 | 3 | To install dependencies: 4 | 5 | ```bash 6 | bun install 7 | ``` 8 | 9 | To run: 10 | 11 | ```bash 12 | bun run index.js 13 | ``` 14 | 15 | This project was created using `bun init` in bun v1.1.12. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # parabolajs 2 | 3 | this is a work in progress and a prototype 4 | 5 | ```bash 6 | bun install 7 | ``` 8 | 9 | To run the example website: 10 | 11 | ```bash 12 | bun run dev 13 | ``` 14 | 15 | ## Workflow 16 | 17 | Just run the example website and then modify the framework code as needed inside packages/parabola. 18 | -------------------------------------------------------------------------------- /packages/parabola/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@parabolajs/parabola", 3 | "main": "src/index", 4 | "type": "module", 5 | "devDependencies": { 6 | "@types/bun": "latest", 7 | "tailwindcss": "^3.4.6" 8 | }, 9 | "peerDependencies": { 10 | "typescript": "^5.0.0" 11 | }, 12 | "dependencies": { 13 | "hono": "^4.5.0" 14 | }, 15 | "scripts": { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Enable latest features 4 | "lib": ["ESNext", "DOM"], 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "moduleDetection": "force", 8 | "allowJs": true, 9 | "jsx": "react-jsx", 10 | "jsxImportSource": "hono/jsx", 11 | // Bundler mode 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "verbatimModuleSyntax": true, 15 | "noEmit": true, 16 | 17 | // Best practices 18 | "strict": true, 19 | "skipLibCheck": true, 20 | "noFallthroughCasesInSwitch": true, 21 | 22 | // Some stricter flags (disabled by default) 23 | "noUnusedLocals": false, 24 | "noUnusedParameters": false, 25 | "noPropertyAccessFromIndexSignature": false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/parabola/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Enable latest features 4 | "lib": ["ESNext", "DOM"], 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "moduleDetection": "force", 8 | "allowJs": true, 9 | "jsx": "react-jsx", 10 | "jsxImportSource": "hono/jsx", 11 | // Bundler mode 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "verbatimModuleSyntax": true, 15 | "noEmit": true, 16 | 17 | // Best practices 18 | "strict": true, 19 | "skipLibCheck": true, 20 | "noFallthroughCasesInSwitch": true, 21 | 22 | // Some stricter flags (disabled by default) 23 | "noUnusedLocals": false, 24 | "noUnusedParameters": false, 25 | "noPropertyAccessFromIndexSignature": false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BUN_VERSION=1.1.12 2 | FROM oven/bun:${BUN_VERSION}-slim as base 3 | 4 | WORKDIR /app 5 | 6 | ENV NODE_ENV="production" 7 | 8 | FROM base as build 9 | 10 | RUN apt-get update -qq && \ 11 | apt-get install --no-install-recommends -y build-essential pkg-config python-is-python3 12 | 13 | RUN mkdir -p packages/example && \ 14 | mkdir -p packages/parabola 15 | COPY --link bun.lockb package.json ./ 16 | COPY --link ./packages/example/package.json ./packages/example 17 | COPY --link ./packages/parabola/package.json ./packages/parabola 18 | RUN bun install --ci 19 | 20 | COPY --link . . 21 | 22 | RUN cd packages/example && bun build:tailwind 23 | 24 | FROM base 25 | 26 | COPY --from=build /app /app 27 | 28 | ARG RAILWAY_GIT_COMMIT_SHA 29 | ENV COMMIT_SHA=${RAILWAY_GIT_COMMIT_SHA} 30 | 31 | EXPOSE 3000 32 | CMD [ "bun", "example" ] -------------------------------------------------------------------------------- /packages/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@parabolajs/example", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "dev": "run-p server tailwind", 7 | "server": "bun --watch src/index.tsx", 8 | "start": "bun src/index.tsx", 9 | "tailwind": "tailwindcss -i src/styles.css -o dist/styles.css --watch", 10 | "build:tailwind": "tailwindcss -i src/styles.css -o dist/styles.css" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "MIT", 15 | "description": "", 16 | "dependencies": { 17 | "@parabolajs/parabola": "workspace:*", 18 | "bad-words": "^3.0.4", 19 | "daisyui": "^4.12.10", 20 | "hono": "^4.5.0", 21 | "npm-run-all": "^4.1.5" 22 | }, 23 | "devDependencies": { 24 | "@types/bun": "latest" 25 | }, 26 | "peerDependencies": { 27 | "typescript": "^5.0.0" 28 | } 29 | } -------------------------------------------------------------------------------- /packages/parabola/src/bus.ts: -------------------------------------------------------------------------------- 1 | import type { Renderer } from "./renderer"; 2 | 3 | type Invalidator = (key: string) => void; 4 | 5 | export class ControlBus { 6 | private renderer: Renderer; 7 | private actions = new Map< 8 | string, 9 | (invalidate: Invalidator, data: any) => void 10 | >(); 11 | 12 | constructor(renderer: Renderer) { 13 | this.renderer = renderer; 14 | } 15 | 16 | on(key: string, cb: (invalidate: Invalidator, data: any) => void) { 17 | if (this.actions.has(key)) { 18 | console.error(`Action with key ${key} already exists`); 19 | return; 20 | } 21 | this.actions.set(key, cb); 22 | } 23 | 24 | invoke(key: string, data: any) { 25 | const action = this.actions.get(key); 26 | if (!action) { 27 | return; 28 | } 29 | const invalidate: Invalidator = (key: string) => { 30 | this.renderer.update(key); 31 | }; 32 | action(invalidate, data.data); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/example/src/pages/counter.tsx: -------------------------------------------------------------------------------- 1 | import { Parabola } from "@parabolajs/parabola"; 2 | 3 | export function registerCounter(parabola: Parabola) { 4 | let count = 0; 5 | 6 | parabola.template("counter", () => { 7 | return ( 8 |
9 |
{count}
10 |
11 |
12 | 13 |
14 | 15 |
16 | 17 |
18 |
19 |
20 | ); 21 | }); 22 | 23 | parabola.action("increment", (invalidate) => { 24 | count++; 25 | invalidate("counter"); 26 | }); 27 | 28 | parabola.action("decrement", (invalidate) => { 29 | count--; 30 | invalidate("counter"); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /packages/parabola/src/dispatcher.ts: -------------------------------------------------------------------------------- 1 | import type { WSContext } from "hono/ws"; 2 | 3 | export class Dispatcher { 4 | private subscriptions = new Map>(); 5 | 6 | subscribe(ws: WSContext, key: string) { 7 | if (!this.subscriptions.has(key)) { 8 | this.subscriptions.set(key, new Set()); 9 | } 10 | this.subscriptions.get(key).add(ws); 11 | } 12 | 13 | unsubscribe(ws: WSContext, key: string) { 14 | this.subscriptions.get(key).delete(ws); 15 | } 16 | 17 | unsubscribeAll(ws: WSContext) { 18 | this.subscriptions.forEach((sockets) => { 19 | sockets.delete(ws); 20 | }); 21 | } 22 | 23 | dispatch(key: string, data: string, ws?: WSContext) { 24 | if (ws) { 25 | ws.send(data); 26 | return; 27 | } 28 | 29 | const sockets = this.subscriptions.get(key); 30 | if (!sockets) { 31 | return; 32 | } 33 | for (const socket of sockets) { 34 | socket.send(data.toString()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/parabola/src/renderer.ts: -------------------------------------------------------------------------------- 1 | import type { WSContext } from "hono/ws"; 2 | import type { Dispatcher } from "./dispatcher"; 3 | 4 | export class Renderer { 5 | private dispatcher: Dispatcher; 6 | private templates = new Map string>(); 7 | private cache = new Map(); 8 | 9 | constructor(dispatcher: Dispatcher) { 10 | this.dispatcher = dispatcher; 11 | } 12 | 13 | register(key: string, cb: () => string) { 14 | if (this.templates.has(key)) { 15 | console.error(`Template with key ${key} already exists`); 16 | return; 17 | } 18 | this.templates.set(key, cb); 19 | } 20 | 21 | getTemplateFromCache(key: string) { 22 | return this.cache.get(key); 23 | } 24 | 25 | update(key: string, ws?: WSContext) { 26 | const template = this.templates.get(key); 27 | if (!template) { 28 | return; 29 | } 30 | const html = template().toString(); 31 | this.cache.set(key, html); 32 | this.dispatcher.dispatch(key, JSON.stringify({ key, html }), ws); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Web Dev Cody 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. -------------------------------------------------------------------------------- /packages/example/src/pages/poll.tsx: -------------------------------------------------------------------------------- 1 | import { Parabola } from "@parabolajs/parabola"; 2 | 3 | const options = [ 4 | { 5 | id: "1", 6 | text: "Ice cream", 7 | votes: 0, 8 | }, 9 | { 10 | id: "2", 11 | text: "Banana Bread", 12 | votes: 0, 13 | }, 14 | { 15 | id: "3", 16 | text: "Cookies", 17 | votes: 0, 18 | }, 19 | ]; 20 | 21 | export function registerPoll(parabola: Parabola) { 22 | parabola.template("poll", () => { 23 | return ( 24 |
25 | {options.map((option) => ( 26 |
30 |
{option.text}
31 |
{option.votes} votes
32 | 33 | 34 |
35 | ))} 36 |
37 | ); 38 | }); 39 | 40 | parabola.action("vote", (invalidate, data) => { 41 | const voteId = data.optionId; 42 | const option = options.find((option) => option.id === voteId); 43 | if (!option) return; 44 | option.votes++; 45 | invalidate("poll"); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /packages/example/src/pages/grid.tsx: -------------------------------------------------------------------------------- 1 | import { Parabola } from "@parabolajs/parabola"; 2 | 3 | const GRID_SIZE = 20; 4 | 5 | const grid: boolean[][] = Array.from({ length: GRID_SIZE }, () => 6 | Array.from({ length: GRID_SIZE }, () => false) 7 | ); 8 | 9 | export function registerGrid(parabola: Parabola) { 10 | parabola.template("grid", () => { 11 | return ( 12 |
13 |

Toggle anything on this realtime grid!

14 | 15 | {grid.map((row, rowIndex) => ( 16 |
17 | ))} 18 |
19 | ); 20 | }); 21 | 22 | grid.forEach((row, rowIndex) => { 23 | const cols = row; 24 | 25 | parabola.template(`row:${rowIndex}`, () => { 26 | return ( 27 |
28 | {cols.map((isToggled, colIndex) => ( 29 |
30 | 31 | 32 |
33 | ))} 34 |
35 | ); 36 | }); 37 | 38 | parabola.action(`toggle:${rowIndex}`, (invalidate, data) => { 39 | const { col } = data; 40 | grid[rowIndex][col] = !grid[rowIndex][col]; 41 | invalidate(`row:${rowIndex}`); 42 | }); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /packages/example/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { Parabola } from "@parabolajs/parabola"; 2 | import { serveStatic } from "hono/bun"; 3 | import { registerMain } from "./pages/main"; 4 | import { registerPoll } from "./pages/poll"; 5 | import { registerCounter } from "./pages/counter"; 6 | import { registerViews } from "./pages/views"; 7 | import { registerChat } from "./pages/chat"; 8 | import { registerGrid } from "./pages/grid"; 9 | 10 | export const parabola = new Parabola({ 11 | styles: ["/styles.css"], 12 | routes: [ 13 | { 14 | path: "/", 15 | target: "content", 16 | template: "welcome", 17 | }, 18 | { 19 | path: "/poll", 20 | target: "content", 21 | template: "poll", 22 | }, 23 | { 24 | path: "/views", 25 | target: "content", 26 | template: "views", 27 | }, 28 | { 29 | path: "/counter", 30 | target: "content", 31 | template: "counter", 32 | }, 33 | { 34 | path: "/chat", 35 | target: "content", 36 | template: "chat", 37 | }, 38 | { 39 | path: "/grid", 40 | target: "content", 41 | template: "grid", 42 | }, 43 | ], 44 | }); 45 | 46 | parabola 47 | .getApp() 48 | .use("/styles.css", serveStatic({ path: "./dist/styles.css" })); 49 | 50 | registerMain(parabola); 51 | registerPoll(parabola); 52 | registerCounter(parabola); 53 | registerViews(parabola); 54 | registerChat(parabola); 55 | registerGrid(parabola); 56 | -------------------------------------------------------------------------------- /packages/example/src/pages/chat.tsx: -------------------------------------------------------------------------------- 1 | import { Parabola } from "@parabolajs/parabola"; 2 | import Filter from "bad-words"; 3 | 4 | const MAX_MESSAGES = 100; 5 | const messages: string[] = []; 6 | 7 | const filter = new Filter(); 8 | 9 | export function registerChat(parabola: Parabola) { 10 | parabola.template("chat", () => { 11 | return ( 12 |
13 |

Send Messages to Everyone!

14 | 15 |
16 |
17 | 23 | 24 |
25 | 26 |
27 |
28 |
29 | ); 30 | }); 31 | 32 | parabola.template("messageList", () => { 33 | return ( 34 |
35 | {messages.map((message) => ( 36 |
{message}
37 | ))} 38 |
39 | ); 40 | }); 41 | 42 | parabola.action("sendMessage", (invalidate, data) => { 43 | const message = filter.clean(data.message); 44 | messages.unshift(message); 45 | messages.splice(MAX_MESSAGES); 46 | invalidate("messageList"); 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /packages/example/src/pages/views.tsx: -------------------------------------------------------------------------------- 1 | import { Parabola } from "@parabolajs/parabola"; 2 | 3 | let views = 0; 4 | 5 | type User = { 6 | name: string; 7 | }; 8 | 9 | const users: User[] = []; 10 | 11 | function addUser(invalidate: any) { 12 | const name = Math.random().toString(36).substring(7); 13 | users.push({ 14 | name, 15 | }); 16 | setTimeout(() => { 17 | users.splice( 18 | users.findIndex((u) => u.name === name), 19 | 1 20 | ); 21 | invalidate("users"); 22 | }, 120000); 23 | invalidate("users"); 24 | } 25 | 26 | export function registerViews(parabola: Parabola) { 27 | parabola.template("views", () => { 28 | return ( 29 | <> 30 |
34 |
35 |
people have loaded this example 36 |
37 | 38 |
39 |
40 | 41 | ); 42 | }); 43 | 44 | parabola.template("users", () => { 45 | return ( 46 |
47 |

48 | Recent Users (random character, clears after 2 min) 49 |

50 | 51 |
52 | {users.map((user) => ( 53 |
54 | {user.name.substring(0, 1)} 55 |
56 | ))} 57 |
58 |
59 | ); 60 | }); 61 | 62 | parabola.template("views:count", () => { 63 | return {views}; 64 | }); 65 | 66 | parabola.action("views:increment", (invalidate) => { 67 | views++; 68 | invalidate("views:count"); 69 | addUser(invalidate); 70 | }); 71 | } 72 | -------------------------------------------------------------------------------- /packages/example/.gitignore: -------------------------------------------------------------------------------- 1 | # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore 2 | 3 | # Logs 4 | 5 | logs 6 | _.log 7 | npm-debug.log_ 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | .pnpm-debug.log* 12 | 13 | # Caches 14 | 15 | .cache 16 | 17 | # Diagnostic reports (https://nodejs.org/api/report.html) 18 | 19 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 20 | 21 | # Runtime data 22 | 23 | pids 24 | _.pid 25 | _.seed 26 | *.pid.lock 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | 30 | lib-cov 31 | 32 | # Coverage directory used by tools like istanbul 33 | 34 | coverage 35 | *.lcov 36 | 37 | # nyc test coverage 38 | 39 | .nyc_output 40 | 41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 42 | 43 | .grunt 44 | 45 | # Bower dependency directory (https://bower.io/) 46 | 47 | bower_components 48 | 49 | # node-waf configuration 50 | 51 | .lock-wscript 52 | 53 | # Compiled binary addons (https://nodejs.org/api/addons.html) 54 | 55 | build/Release 56 | 57 | # Dependency directories 58 | 59 | node_modules/ 60 | jspm_packages/ 61 | 62 | # Snowpack dependency directory (https://snowpack.dev/) 63 | 64 | web_modules/ 65 | 66 | # TypeScript cache 67 | 68 | *.tsbuildinfo 69 | 70 | # Optional npm cache directory 71 | 72 | .npm 73 | 74 | # Optional eslint cache 75 | 76 | .eslintcache 77 | 78 | # Optional stylelint cache 79 | 80 | .stylelintcache 81 | 82 | # Microbundle cache 83 | 84 | .rpt2_cache/ 85 | .rts2_cache_cjs/ 86 | .rts2_cache_es/ 87 | .rts2_cache_umd/ 88 | 89 | # Optional REPL history 90 | 91 | .node_repl_history 92 | 93 | # Output of 'npm pack' 94 | 95 | *.tgz 96 | 97 | # Yarn Integrity file 98 | 99 | .yarn-integrity 100 | 101 | # dotenv environment variable files 102 | 103 | .env 104 | .env.development.local 105 | .env.test.local 106 | .env.production.local 107 | .env.local 108 | 109 | # parcel-bundler cache (https://parceljs.org/) 110 | 111 | .parcel-cache 112 | 113 | # Next.js build output 114 | 115 | .next 116 | out 117 | 118 | # Nuxt.js build / generate output 119 | 120 | .nuxt 121 | dist 122 | 123 | # Gatsby files 124 | 125 | # Comment in the public line in if your project uses Gatsby and not Next.js 126 | 127 | # https://nextjs.org/blog/next-9-1#public-directory-support 128 | 129 | # public 130 | 131 | # vuepress build output 132 | 133 | .vuepress/dist 134 | 135 | # vuepress v2.x temp and cache directory 136 | 137 | .temp 138 | 139 | # Docusaurus cache and generated files 140 | 141 | .docusaurus 142 | 143 | # Serverless directories 144 | 145 | .serverless/ 146 | 147 | # FuseBox cache 148 | 149 | .fusebox/ 150 | 151 | # DynamoDB Local files 152 | 153 | .dynamodb/ 154 | 155 | # TernJS port file 156 | 157 | .tern-port 158 | 159 | # Stores VSCode versions used for testing VSCode extensions 160 | 161 | .vscode-test 162 | 163 | # yarn v2 164 | 165 | .yarn/cache 166 | .yarn/unplugged 167 | .yarn/build-state.yml 168 | .yarn/install-state.gz 169 | .pnp.* 170 | 171 | # IntelliJ based IDEs 172 | .idea 173 | 174 | # Finder (MacOS) folder config 175 | .DS_Store 176 | -------------------------------------------------------------------------------- /packages/parabola/.gitignore: -------------------------------------------------------------------------------- 1 | # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore 2 | 3 | # Logs 4 | 5 | logs 6 | _.log 7 | npm-debug.log_ 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | .pnpm-debug.log* 12 | 13 | # Caches 14 | 15 | .cache 16 | 17 | # Diagnostic reports (https://nodejs.org/api/report.html) 18 | 19 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 20 | 21 | # Runtime data 22 | 23 | pids 24 | _.pid 25 | _.seed 26 | *.pid.lock 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | 30 | lib-cov 31 | 32 | # Coverage directory used by tools like istanbul 33 | 34 | coverage 35 | *.lcov 36 | 37 | # nyc test coverage 38 | 39 | .nyc_output 40 | 41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 42 | 43 | .grunt 44 | 45 | # Bower dependency directory (https://bower.io/) 46 | 47 | bower_components 48 | 49 | # node-waf configuration 50 | 51 | .lock-wscript 52 | 53 | # Compiled binary addons (https://nodejs.org/api/addons.html) 54 | 55 | build/Release 56 | 57 | # Dependency directories 58 | 59 | node_modules/ 60 | jspm_packages/ 61 | 62 | # Snowpack dependency directory (https://snowpack.dev/) 63 | 64 | web_modules/ 65 | 66 | # TypeScript cache 67 | 68 | *.tsbuildinfo 69 | 70 | # Optional npm cache directory 71 | 72 | .npm 73 | 74 | # Optional eslint cache 75 | 76 | .eslintcache 77 | 78 | # Optional stylelint cache 79 | 80 | .stylelintcache 81 | 82 | # Microbundle cache 83 | 84 | .rpt2_cache/ 85 | .rts2_cache_cjs/ 86 | .rts2_cache_es/ 87 | .rts2_cache_umd/ 88 | 89 | # Optional REPL history 90 | 91 | .node_repl_history 92 | 93 | # Output of 'npm pack' 94 | 95 | *.tgz 96 | 97 | # Yarn Integrity file 98 | 99 | .yarn-integrity 100 | 101 | # dotenv environment variable files 102 | 103 | .env 104 | .env.development.local 105 | .env.test.local 106 | .env.production.local 107 | .env.local 108 | 109 | # parcel-bundler cache (https://parceljs.org/) 110 | 111 | .parcel-cache 112 | 113 | # Next.js build output 114 | 115 | .next 116 | out 117 | 118 | # Nuxt.js build / generate output 119 | 120 | .nuxt 121 | dist 122 | 123 | # Gatsby files 124 | 125 | # Comment in the public line in if your project uses Gatsby and not Next.js 126 | 127 | # https://nextjs.org/blog/next-9-1#public-directory-support 128 | 129 | # public 130 | 131 | # vuepress build output 132 | 133 | .vuepress/dist 134 | 135 | # vuepress v2.x temp and cache directory 136 | 137 | .temp 138 | 139 | # Docusaurus cache and generated files 140 | 141 | .docusaurus 142 | 143 | # Serverless directories 144 | 145 | .serverless/ 146 | 147 | # FuseBox cache 148 | 149 | .fusebox/ 150 | 151 | # DynamoDB Local files 152 | 153 | .dynamodb/ 154 | 155 | # TernJS port file 156 | 157 | .tern-port 158 | 159 | # Stores VSCode versions used for testing VSCode extensions 160 | 161 | .vscode-test 162 | 163 | # yarn v2 164 | 165 | .yarn/cache 166 | .yarn/unplugged 167 | .yarn/build-state.yml 168 | .yarn/install-state.gz 169 | .pnp.* 170 | 171 | # IntelliJ based IDEs 172 | .idea 173 | 174 | # Finder (MacOS) folder config 175 | .DS_Store 176 | -------------------------------------------------------------------------------- /packages/example/src/pages/main.tsx: -------------------------------------------------------------------------------- 1 | import type { Parabola } from "@parabolajs/parabola"; 2 | 3 | function Header() { 4 | return ( 5 |
6 | 27 |
28 | ); 29 | } 30 | 31 | function Footer() { 32 | return ( 33 |
34 |
ParabolaJs
35 |
36 | ); 37 | } 38 | 39 | export function registerMain(parabola: Parabola) { 40 | parabola.template("main", () => { 41 | return ( 42 | <> 43 |
44 | 45 |
46 |
51 |
52 | 53 |