{"id":31588541,"url":"https://github.com/hrutavmodha/twiggle","last_synced_at":"2025-10-06T02:11:34.365Z","repository":{"id":317007835,"uuid":"1055710431","full_name":"hrutavmodha/twiggle","owner":"hrutavmodha","description":"A basic frontend framework for building UI through JSX","archived":false,"fork":false,"pushed_at":"2025-10-05T13:43:57.000Z","size":229,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-05T15:24:38.046Z","etag":null,"topics":["framework","frontend","jsx","reactive","web"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hrutavmodha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-12T17:18:27.000Z","updated_at":"2025-10-05T13:44:00.000Z","dependencies_parsed_at":"2025-09-29T22:17:28.406Z","dependency_job_id":null,"html_url":"https://github.com/hrutavmodha/twiggle","commit_stats":null,"previous_names":["hrutavmodha/twiggle"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hrutavmodha/twiggle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrutavmodha%2Ftwiggle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrutavmodha%2Ftwiggle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrutavmodha%2Ftwiggle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrutavmodha%2Ftwiggle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hrutavmodha","download_url":"https://codeload.github.com/hrutavmodha/twiggle/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hrutavmodha%2Ftwiggle/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278547821,"owners_count":26004775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-06T02:00:05.630Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["framework","frontend","jsx","reactive","web"],"created_at":"2025-10-06T02:10:36.597Z","updated_at":"2025-10-06T02:11:34.358Z","avatar_url":"https://github.com/hrutavmodha.png","language":"TypeScript","readme":"# Twiggle\n\nTiny, focused front-end primitives: a custom JSX runtime, minimal DOM renderer, a tiny router, and a reactive state primitive.\n\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](#license)\n[![Version](https://img.shields.io/badge/version-1.1.1-brightgreen)](https://www.npmjs.com/package/twiggle)\n\n\u003e **Note:** Twiggle is currently in its early stage and should not be used in the production apps.\n\nMaintainer: Hrutav Modha\n\nLicense: MIT\n\n---\n\n## Table of Contents\n\n- [Why Twiggle](#why-twiggle)\n- [Installation](#installation)\n- [Quick start](#quick-start)\n- [Core concepts](#core-concepts)\n  - [JSX \u0026 createElement](#jsx--createelement)\n  - [Renderer](#renderer)\n  - [State primitive](#state-primitive)\n  - [Router](#router)\n- [Vite plugin](#vite-plugin)\n- [API reference](#api-reference)\n- [Development](#development)\n- [Contributing](#contributing)\n- [FAQ](#faq)\n- [License](#license)\n\n---\n\n## Why Twiggle\n\nTwiggle is intentionally minimal. It focuses on clarity and a small runtime surface for use-cases like:\n\n- Learning how frameworks work under the hood.\n- Small demos, playgrounds, documentation sites.\n- Projects that prefer direct DOM manipulation and minimal dependencies.\n\nDesign principles:\n\n- Minimal and transparent (small, well-commented code).\n- Simple reactivity — explicit effect tracking and subscription.\n- Works with modern tooling via a small Vite plugin.\n\n---\n\n## Installation\n\nInstall from npm:\n\n```bash\nnpm install twiggle\n```\n\nOr use the package from the monorepo during local development (root of workspace):\n\n```bash\nnpm install\ncd packages/twiggle\nnpm run start\n```\n\nAvailable scripts (in `packages/twiggle/package.json`):\n\n- `start` — start Vite dev server\n- `build` — run Vite build\n- `test` — run Vitest\n- `test:ui` — run Vitest UI\n\n---\n\n## Quick Start\n\nMinimal example (TypeScript / TSX):\n\n```tsx\n/** src/main.tsx */\nimport render from 'twiggle'\nimport Home from './pages/Home'\n\nconst root = document.getElementById('root')!\nrender(\u003cHome /\u003e, root)\n```\n\nFunction component example:\n\n```tsx\nfunction Greeting(props: { name: string }) {\n  return \u003cdiv\u003eHello, {props.name}!\u003c/div\u003e\n}\n\nrender(\u003cGreeting name=\"Alice\" /\u003e, document.getElementById('root'))\n```\n\nNote: The JSX transform must target Twiggle's runtime (see Vite plugin section) or you can import the runtime directly in your build config.\n\n---\n\n## Core Concepts\n\n### JSX \u0026 CreateElement\n\nTwiggle implements a small JSX runtime. When JSX is compiled it calls `createElement(type, props)`. Supported `type` values:\n\n- string tag (e.g. `'div'`) — creates a DOM element and applies props/children\n- `'Fragment'` — returns a `DocumentFragment`\n- function component — `type(props)` is invoked and must return a DOM node or fragment\n\nProps convention:\n\n- `children` may be string, number, element, array of elements\n- DOM event handlers use lowercase names (e.g. `onclick`, not `onClick`)\n\n### Renderer\n\n`render(element, parent)` mounts a DOM node or fragment to the `parent` node. The current implementation clears `parent.innerHTML` and appends the element. This is intentionally simple to keep the runtime small.\n\n### State Primitive\n\nAPI:\n\n- `createState\u003cT\u003e(initial)` — returns `{ get: () =\u003e T, set: (v: T) =\u003e void }`\n- `runSideEffect(fn)` — runs `fn` and tracks any `get()` calls performed during the execution. When a tracked state updates, the effect is re-run.\n\nThis is a minimal reactive system built around an effect stack and per-state subscriber lists. It is not a full reactive framework but is tiny and easy to reason about.\n\nExample counter:\n\n```tsx\nimport { \n  createState, \n  runSideEffect \n} from 'twiggle'\n\nconst counter = createState(0)\n\nexport default function Counter() {\n  runSideEffect(() =\u003e {\n    // reads counter.get() to subscribe\n    const value = counter.get()\n    // re-run when counter.set is called\n    console.log('counter', value)\n  })\n\n  return (\n    \u003cdiv\u003e\n      \u003cspan\u003e{counter.get()}\u003c/span\u003e\n      \u003cbutton onclick={() =\u003e counter.set(counter.get() + 1)}\u003eIncrement\u003c/button\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\n### Router\n\nTwiggle ships a tiny client-side router built on the History API.\n\nComponents and functions:\n\n- `Routes` — collects `Route` children and registers them via `setRoutes`\n- `Route({ to, element })` — lightweight route descriptor; `element` is a function that returns the element to render\n- `Link({ to, children })` — anchor element that prevents default navigation and calls `navigate`\n- `navigate(to)` — programmatic navigation; pushes a new history entry and renders the route\n\nExample:\n\n```tsx\n\u003cRoutes\u003e\n  \u003cRoute to=\"/\" element={() =\u003e \u003cHome /\u003e} /\u003e\n  \u003cRoute to=\"/about\" element={() =\u003e \u003cAbout /\u003e} /\u003e\n\u003c/Routes\u003e\n\n\u003cLink to=\"/about\"\u003eAbout\u003c/Link\u003e\n```\n\n---\n\n## Vite Plugin\n\nUse the official Vite plugin in this monorepo to compile JSX for Twiggle and enable reactive transforms.\n\nInstall and add to `vite.config.js`:\n\n```js\nimport { defineConfig } from 'vite'\nimport twiggle from 'vite-plugin-twiggle'\n\nexport default defineConfig({\n  plugins: [twiggle()]\n})\n```\n\nWhat the plugin does:\n\n- Runs Babel to transform JSX using `@babel/preset-react` configured with `runtime: 'automatic'` and `importSource: 'twiggle/jsx'`.\n- Applies a small custom Babel plugin bundled in `packages/vite-plugin-twiggle` to support reactive expression transforms used by Twiggle's runtime.\n\nIf you don't use the plugin, ensure your JSX compiler targets the Twiggle runtime or import the proper runtime functions directly.\n\n---\n\n## API Reference\n\nRenderer\n\n- `createElement(type, props)` — internal JSX entry point.\n- `render(element: HTMLElement | DocumentFragment, parent: HTMLElement): void` — mount node to parent.\n\nRouter\n\n- `navigate(to: string): void`\n- `setRoutes(routes: Record\u003cstring, () =\u003e HTMLElement\u003e): void` — internal\n- `Routes(props: { children: any[] }): null`\n- `Route(props: { to: string, element: () =\u003e HTMLElement }): any`\n- `Link(props: { to: string, children: any }): HTMLElement`\n\nState\n\n- `createState\u003cT\u003e(value: T): { get: () =\u003e T; set: (v: T) =\u003e void }`\n- `runSideEffect(fn: () =\u003e void): void`\n\nSee the `src` folder for implementation details and comments.\n\n---\n\n## Development\n\nClone and install dependencies from the monorepo root:\n\n```bash\ngit clone https://github.com/hrutavmodha/twiggle.git\nnpm install\n```\n\nRun the example/dev server for the `twiggle` package:\n\n```bash\ncd packages/twiggle\nnpm run start\n```\n\nBuild the package:\n\n```bash\ncd packages/twiggle\nnpm run build\n```\n\nRun tests:\n\n```bash\ncd packages/twiggle\nnpm run test\n```\n\nQuality checks you should run before opening a PR:\n\n- Build: `npm run build`\n- Tests: `npm run test`\n- Type checks (optional): `tsc --noEmit` from the package directory\n\n---\n\n## Contributing\n\nContributions are welcome. Please follow these guidelines:\n\n1. Fork the repo and create a descriptive branch name.\n2. Keep changes small and focused. Add tests where applicable.\n3. Run the test suite and ensure the build passes.\n4. Open a pull request describing the problem, approach, and any migration notes.\n\nSee `CONTRIBUTING.md` for the project's broader contribution rules.\n\n---\n\n## FAQ\n\n1. Is Twiggle a full framework?\n\n  No. Twiggle is a tiny set of primitives meant for learning, demos, and small apps. \nIt lacks many features of full frameworks (advanced component lifecycles, SSR, etc.)\n\n2. Can I use Twiggle in production?\n\n  You can, but consider the tradeoffs (small feature set, simpler reactivity). For production apps that need scaling, a more feature-complete framework is recommended.\n\n---\n\n## License\n\nMIT — see the `LICENSE` file for details.\n\n---\n\nMaintainer: Hrutav Modha — please open issues for bugs or feature requests.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhrutavmodha%2Ftwiggle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhrutavmodha%2Ftwiggle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhrutavmodha%2Ftwiggle/lists"}