diff --git a/bun.lock b/bun.lock
new file mode 100644
index 0000000..a84eec4
--- /dev/null
+++ b/bun.lock
@@ -0,0 +1,104 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "fast-web",
+ },
+ "examples/blog": {
+ "name": "fast-web",
+ "dependencies": {
+ "chokidar": "^4.0.3",
+ "lightningcss": "^1.30.1",
+ "lucide": "^0.525.0",
+ "serve-static-bun": "^0.5.3",
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ "csstype": "^3.1.3",
+ "typescript": "^5.8.3",
+ },
+ },
+ "examples/client-renderer": {
+ "name": "client-renderer",
+ "dependencies": {
+ "signal-polyfill": "^0.2.2",
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ },
+ "peerDependencies": {
+ "typescript": "^5",
+ },
+ },
+ "examples/didact": {
+ "name": "didact",
+ "version": "0.0.0",
+ "dependencies": {
+ "signal-polyfill": "^0.2.2",
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ "typescript": "^5.9.2",
+ },
+ },
+ },
+ "packages": {
+ "@types/bun": ["@types/bun@1.2.20", "", { "dependencies": { "bun-types": "1.2.20" } }, "sha512-dX3RGzQ8+KgmMw7CsW4xT5ITBSCrSbfHc36SNT31EOUg/LA9JWq0VDdEXDRSe1InVWpd2yLUM1FUF/kEOyTzYA=="],
+
+ "@types/node": ["@types/node@24.2.1", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ=="],
+
+ "@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="],
+
+ "bun-types": ["bun-types@1.2.20", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-pxTnQYOrKvdOwyiyd/7sMt9yFOenN004Y6O4lCcCUoKVej48FS5cvTw9geRaEcB9TsDZaJKAxPTVvi8tFsVuXA=="],
+
+ "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
+
+ "client-renderer": ["client-renderer@workspace:examples/client-renderer"],
+
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
+
+ "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
+
+ "didact": ["didact@workspace:examples/didact"],
+
+ "fast-web": ["fast-web@workspace:examples/blog"],
+
+ "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
+
+ "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
+
+ "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
+
+ "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="],
+
+ "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="],
+
+ "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="],
+
+ "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="],
+
+ "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="],
+
+ "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="],
+
+ "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="],
+
+ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
+
+ "lucide": ["lucide@0.525.0", "", {}, "sha512-sfehWlaE/7NVkcEQ4T9JD3eID8RNMIGJBBUq9wF3UFiJIrcMKRbU3g1KGfDk4svcW7yw8BtDLXaXo02scDtUYQ=="],
+
+ "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
+
+ "serve-static-bun": ["serve-static-bun@0.5.3", "", {}, "sha512-QlfA/Z30MwZl4XXWM9KevfinJRJjzJMRK8sXABbaY06Y7KTuXtbT1n0e8qdf1PgM59mpgSh/JTUM9Jjsh0E58Q=="],
+
+ "signal-polyfill": ["signal-polyfill@0.2.2", "", {}, "sha512-p63Y4Er5/eMQ9RHg0M0Y64NlsQKpiu6MDdhBXpyywRuWiPywhJTpKJ1iB5K2hJEbFZ0BnDS7ZkJ+0AfTuL37Rg=="],
+
+ "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
+
+ "undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
+
+ "fast-web/@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
+
+ "fast-web/@types/bun/bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
+ }
+}
diff --git a/README.md b/examples/blog/README.md
similarity index 100%
rename from README.md
rename to examples/blog/README.md
diff --git a/bun.lockb b/examples/blog/bun.lockb
similarity index 100%
rename from bun.lockb
rename to examples/blog/bun.lockb
diff --git a/bunfig.toml b/examples/blog/bunfig.toml
similarity index 100%
rename from bunfig.toml
rename to examples/blog/bunfig.toml
diff --git a/examples/blog/package.json b/examples/blog/package.json
new file mode 100644
index 0000000..8c4d121
--- /dev/null
+++ b/examples/blog/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "fast-web",
+ "module": "index.ts",
+ "type": "module",
+ "scripts": {
+ "start": "bun run src/index.tsx",
+ "dev": "bun run --watch --hot src/index.tsx"
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ "csstype": "^3.1.3",
+ "typescript": "^5.8.3"
+ },
+ "dependencies": {
+ "chokidar": "^4.0.3",
+ "lightningcss": "^1.30.1",
+ "lucide": "^0.525.0",
+ "serve-static-bun": "^0.5.3"
+ },
+ "private": true
+}
diff --git a/public/assets/fonts/CascadiaCode-VariableFont_wght-subset.woff2 b/examples/blog/public/assets/fonts/CascadiaCode-VariableFont_wght-subset.woff2
similarity index 100%
rename from public/assets/fonts/CascadiaCode-VariableFont_wght-subset.woff2
rename to examples/blog/public/assets/fonts/CascadiaCode-VariableFont_wght-subset.woff2
diff --git a/public/assets/fonts/CascadiaCode-VariableFont_wght.ttf b/examples/blog/public/assets/fonts/CascadiaCode-VariableFont_wght.ttf
similarity index 100%
rename from public/assets/fonts/CascadiaCode-VariableFont_wght.ttf
rename to examples/blog/public/assets/fonts/CascadiaCode-VariableFont_wght.ttf
diff --git a/public/assets/fonts/OFL.txt b/examples/blog/public/assets/fonts/OFL.txt
similarity index 100%
rename from public/assets/fonts/OFL.txt
rename to examples/blog/public/assets/fonts/OFL.txt
diff --git a/public/assets/fonts/make-font.sh b/examples/blog/public/assets/fonts/make-font.sh
similarity index 100%
rename from public/assets/fonts/make-font.sh
rename to examples/blog/public/assets/fonts/make-font.sh
diff --git a/src/components/Button.tsx b/examples/blog/src/components/Button.tsx
similarity index 100%
rename from src/components/Button.tsx
rename to examples/blog/src/components/Button.tsx
diff --git a/src/components/Counter.state.ts b/examples/blog/src/components/Counter.state.ts
similarity index 100%
rename from src/components/Counter.state.ts
rename to examples/blog/src/components/Counter.state.ts
diff --git a/src/components/Counter.tsx b/examples/blog/src/components/Counter.tsx
similarity index 100%
rename from src/components/Counter.tsx
rename to examples/blog/src/components/Counter.tsx
diff --git a/src/components/Heading.tsx b/examples/blog/src/components/Heading.tsx
similarity index 100%
rename from src/components/Heading.tsx
rename to examples/blog/src/components/Heading.tsx
diff --git a/src/components/Icon.tsx b/examples/blog/src/components/Icon.tsx
similarity index 100%
rename from src/components/Icon.tsx
rename to examples/blog/src/components/Icon.tsx
diff --git a/src/components/Layout.tsx b/examples/blog/src/components/Layout.tsx
similarity index 100%
rename from src/components/Layout.tsx
rename to examples/blog/src/components/Layout.tsx
diff --git a/src/components/Link.tsx b/examples/blog/src/components/Link.tsx
similarity index 100%
rename from src/components/Link.tsx
rename to examples/blog/src/components/Link.tsx
diff --git a/src/components/Navigation.tsx b/examples/blog/src/components/Navigation.tsx
similarity index 100%
rename from src/components/Navigation.tsx
rename to examples/blog/src/components/Navigation.tsx
diff --git a/src/css.d.ts b/examples/blog/src/css.d.ts
similarity index 100%
rename from src/css.d.ts
rename to examples/blog/src/css.d.ts
diff --git a/src/index.tsx b/examples/blog/src/index.tsx
similarity index 100%
rename from src/index.tsx
rename to examples/blog/src/index.tsx
diff --git a/src/jsx.d.ts b/examples/blog/src/jsx.d.ts
similarity index 100%
rename from src/jsx.d.ts
rename to examples/blog/src/jsx.d.ts
diff --git a/src/lib/Global.tsx b/examples/blog/src/lib/Global.tsx
similarity index 100%
rename from src/lib/Global.tsx
rename to examples/blog/src/lib/Global.tsx
diff --git a/src/lib/fast-web.tsx b/examples/blog/src/lib/fast-web.tsx
similarity index 100%
rename from src/lib/fast-web.tsx
rename to examples/blog/src/lib/fast-web.tsx
diff --git a/src/lib/hooks.ts b/examples/blog/src/lib/hooks.ts
similarity index 100%
rename from src/lib/hooks.ts
rename to examples/blog/src/lib/hooks.ts
diff --git a/src/lib/pokeapi.ts b/examples/blog/src/lib/pokeapi.ts
similarity index 100%
rename from src/lib/pokeapi.ts
rename to examples/blog/src/lib/pokeapi.ts
diff --git a/src/lib/reset.css b/examples/blog/src/lib/reset.css
similarity index 100%
rename from src/lib/reset.css
rename to examples/blog/src/lib/reset.css
diff --git a/src/lib/state.ts b/examples/blog/src/lib/state.ts
similarity index 100%
rename from src/lib/state.ts
rename to examples/blog/src/lib/state.ts
diff --git a/src/middlewares/gzip.ts b/examples/blog/src/middlewares/gzip.ts
similarity index 100%
rename from src/middlewares/gzip.ts
rename to examples/blog/src/middlewares/gzip.ts
diff --git a/src/middlewares/request-log.ts b/examples/blog/src/middlewares/request-log.ts
similarity index 100%
rename from src/middlewares/request-log.ts
rename to examples/blog/src/middlewares/request-log.ts
diff --git a/src/middlewares/with-middlewares.ts b/examples/blog/src/middlewares/with-middlewares.ts
similarity index 100%
rename from src/middlewares/with-middlewares.ts
rename to examples/blog/src/middlewares/with-middlewares.ts
diff --git a/src/myPlugin.ts b/examples/blog/src/myPlugin.ts
similarity index 100%
rename from src/myPlugin.ts
rename to examples/blog/src/myPlugin.ts
diff --git a/src/pages/_layout.tsx b/examples/blog/src/pages/_layout.tsx
similarity index 100%
rename from src/pages/_layout.tsx
rename to examples/blog/src/pages/_layout.tsx
diff --git a/src/pages/index.tsx b/examples/blog/src/pages/index.tsx
similarity index 100%
rename from src/pages/index.tsx
rename to examples/blog/src/pages/index.tsx
diff --git a/src/runtime/jsx-dev-runtime.ts b/examples/blog/src/runtime/jsx-dev-runtime.ts
similarity index 100%
rename from src/runtime/jsx-dev-runtime.ts
rename to examples/blog/src/runtime/jsx-dev-runtime.ts
diff --git a/src/runtime/jsx-runtime.ts b/examples/blog/src/runtime/jsx-runtime.ts
similarity index 100%
rename from src/runtime/jsx-runtime.ts
rename to examples/blog/src/runtime/jsx-runtime.ts
diff --git a/src/scripts/hmr.dev.ts b/examples/blog/src/scripts/hmr.dev.ts
similarity index 100%
rename from src/scripts/hmr.dev.ts
rename to examples/blog/src/scripts/hmr.dev.ts
diff --git a/src/scripts/navigation.ts b/examples/blog/src/scripts/navigation.ts
similarity index 100%
rename from src/scripts/navigation.ts
rename to examples/blog/src/scripts/navigation.ts
diff --git a/src/system/crazyHash.ts b/examples/blog/src/system/crazyHash.ts
similarity index 100%
rename from src/system/crazyHash.ts
rename to examples/blog/src/system/crazyHash.ts
diff --git a/src/system/css-utils.ts b/examples/blog/src/system/css-utils.ts
similarity index 100%
rename from src/system/css-utils.ts
rename to examples/blog/src/system/css-utils.ts
diff --git a/src/system/style-rules.ts b/examples/blog/src/system/style-rules.ts
similarity index 100%
rename from src/system/style-rules.ts
rename to examples/blog/src/system/style-rules.ts
diff --git a/src/system/token-categories.ts b/examples/blog/src/system/token-categories.ts
similarity index 100%
rename from src/system/token-categories.ts
rename to examples/blog/src/system/token-categories.ts
diff --git a/src/template.html b/examples/blog/src/template.html
similarity index 100%
rename from src/template.html
rename to examples/blog/src/template.html
diff --git a/src/theme.css b/examples/blog/src/theme.css
similarity index 100%
rename from src/theme.css
rename to examples/blog/src/theme.css
diff --git a/src/types.ts b/examples/blog/src/types.ts
similarity index 100%
rename from src/types.ts
rename to examples/blog/src/types.ts
diff --git a/tsconfig.json b/examples/blog/tsconfig.json
similarity index 100%
rename from tsconfig.json
rename to examples/blog/tsconfig.json
diff --git a/watch.ts b/examples/blog/watch.ts
similarity index 100%
rename from watch.ts
rename to examples/blog/watch.ts
diff --git a/examples/client-renderer/.gitignore b/examples/client-renderer/.gitignore
new file mode 100644
index 0000000..a14702c
--- /dev/null
+++ b/examples/client-renderer/.gitignore
@@ -0,0 +1,34 @@
+# dependencies (bun install)
+node_modules
+
+# output
+out
+dist
+*.tgz
+
+# code coverage
+coverage
+*.lcov
+
+# logs
+logs
+_.log
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# caches
+.eslintcache
+.cache
+*.tsbuildinfo
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
diff --git a/examples/client-renderer/README.md b/examples/client-renderer/README.md
new file mode 100644
index 0000000..a4b54db
--- /dev/null
+++ b/examples/client-renderer/README.md
@@ -0,0 +1,15 @@
+# client-renderer
+
+To install dependencies:
+
+```bash
+bun install
+```
+
+To run:
+
+```bash
+bun run index.ts
+```
+
+This project was created using `bun init` in bun v1.2.19. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
diff --git a/examples/client-renderer/index.html b/examples/client-renderer/index.html
new file mode 100644
index 0000000..2f1349e
--- /dev/null
+++ b/examples/client-renderer/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ Client Renderer
+
+
+
+
+
+
+
+
diff --git a/examples/client-renderer/package.json b/examples/client-renderer/package.json
new file mode 100644
index 0000000..b433871
--- /dev/null
+++ b/examples/client-renderer/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "client-renderer",
+ "module": "index.ts",
+ "type": "module",
+ "private": true,
+ "scripts": {
+ "dev": "bun run index.html"
+ },
+ "devDependencies": {
+ "@types/bun": "latest"
+ },
+ "peerDependencies": {
+ "typescript": "^5"
+ },
+ "dependencies": {
+ "signal-polyfill": "^0.2.2"
+ }
+}
diff --git a/examples/client-renderer/src/cr.ts b/examples/client-renderer/src/cr.ts
new file mode 100644
index 0000000..e69de29
diff --git a/examples/client-renderer/src/main.tsx b/examples/client-renderer/src/main.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/examples/client-renderer/tsconfig.json b/examples/client-renderer/tsconfig.json
new file mode 100644
index 0000000..bfa0fea
--- /dev/null
+++ b/examples/client-renderer/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ // Environment setup & latest features
+ "lib": ["ESNext"],
+ "target": "ESNext",
+ "module": "Preserve",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ // Bundler mode
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ // Best practices
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+
+ // Some stricter flags (disabled by default)
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false
+ }
+}
diff --git a/examples/didact/.gitignore b/examples/didact/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/examples/didact/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/examples/didact/README.md b/examples/didact/README.md
new file mode 100644
index 0000000..8ad87c2
--- /dev/null
+++ b/examples/didact/README.md
@@ -0,0 +1,15 @@
+# didact
+
+To install dependencies:
+
+```bash
+bun install
+```
+
+To run:
+
+```bash
+bun run index.ts
+```
+
+This project was created using `bun init` in bun v1.2.19. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
diff --git a/examples/didact/bun.lockb b/examples/didact/bun.lockb
new file mode 100755
index 0000000..7d22bac
Binary files /dev/null and b/examples/didact/bun.lockb differ
diff --git a/examples/didact/index.html b/examples/didact/index.html
new file mode 100644
index 0000000..391d302
--- /dev/null
+++ b/examples/didact/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+ Vite + TS
+
+
+
+
+
+
+
+
diff --git a/examples/didact/package.json b/examples/didact/package.json
new file mode 100644
index 0000000..6d96166
--- /dev/null
+++ b/examples/didact/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "didact",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {},
+ "devDependencies": {
+ "typescript": "^5.9.2",
+ "@types/bun": "latest"
+ },
+ "module": "index.ts",
+ "dependencies": {
+ "signal-polyfill": "^0.2.2"
+ }
+}
diff --git a/examples/didact/public/vite.svg b/examples/didact/public/vite.svg
new file mode 100644
index 0000000..e7b8dfb
--- /dev/null
+++ b/examples/didact/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/didact/src/components/Log.tsx b/examples/didact/src/components/Log.tsx
new file mode 100644
index 0000000..0306e40
--- /dev/null
+++ b/examples/didact/src/components/Log.tsx
@@ -0,0 +1,9 @@
+import { Didact } from "../lib/Didact";
+/** @jsx Didact.createElement */
+
+const Log = ({ message, children }) => {
+ console.log(message);
+ return Test
;
+};
+
+export default Log;
diff --git a/examples/didact/src/jsx.d.ts b/examples/didact/src/jsx.d.ts
new file mode 100644
index 0000000..72543a3
--- /dev/null
+++ b/examples/didact/src/jsx.d.ts
@@ -0,0 +1,165 @@
+namespace JSX {
+ interface FC {
+ (
+ props: any,
+ ):
+ | JSX.Element
+ | string
+ | null
+ | Promise
+ | Promise
+ | Promsie;
+ }
+ type Children =
+ | JSX.Element
+ | (string | JSX.Element | JSX.Element[])[]
+ | string
+ | null
+ | undefined;
+ type BaseElementPropsWithoutChildren = {
+ className?: string;
+ id?: string;
+ tabindex?: number | string;
+ style?: import("./types").Styles;
+ [`data-${string}`]?: string | boolean;
+ onClick?: (event: MouseEvent) => void;
+ } & Partial;
+ type BaseElementProps = BaseElementPropsWithoutChildren & {
+ children?: JSX.Children;
+ };
+ type SvgProps = BaseElementProps & {
+ ["xml:lang"]?: string;
+ ["xml:space"]?: string;
+ xmlns?: string;
+ // XLink attributes
+ ["xlink:hrefDeprecated"]?: string;
+ ["xlink:type"]?: string;
+ ["xlink:role"]?: string;
+ ["xlink:arcrole"]?: string;
+ ["xlink:title"]?: string;
+ ["xlink:show"]?: string;
+ ["xlink:actuate"]?: string;
+ // Presentation attributes
+ ["alignment-baseline"]?: string;
+ ["baseline-shift"]?: string;
+ ["clip"]?: string;
+ ["clipPath"]?: string;
+ ["clipRule"]?: string;
+ ["color"]?: string;
+ ["colorInterpolation"]?: string;
+ ["colorInterpolationFilters"]?: string;
+ ["cursor"]?: string;
+ ["cx"]?: string;
+ ["cy"]?: string;
+ ["d"]?: string;
+ ["direction"]?: string;
+ ["display"]?: string;
+ ["dominantBaseline"]?: string;
+ ["fill"]?: string;
+ ["fillOpacity"]?: string;
+ ["fillRule"]?: string;
+ ["filter"]?: string;
+ ["floodColor"]?: string;
+ ["floodOpacity"]?: string;
+ ["fontFamily"]?: string;
+ ["fontSize"]?: string;
+ ["fontSize-adjust"]?: string;
+ ["fontStretch"]?: string;
+ ["fontStyle"]?: string;
+ ["fontVariant"]?: string;
+ ["fontWeight"]?: string;
+ ["glyphOrientation-horizontal"]?: string;
+ ["glyphOrientation-vertical"]?: string;
+ ["height"]?: string;
+ ["imageRendering"]?: string;
+ ["letterSpacing"]?: string;
+ ["lightingColor"]?: string;
+ ["markerEnd"]?: string;
+ ["markerMid"]?: string;
+ ["markerStart"]?: string;
+ ["mask"]?: string;
+ ["maskType"]?: string;
+ ["opacity"]?: string;
+ ["overflow"]?: string;
+ ["pointerEvents"]?: string;
+ ["r"]?: string;
+ ["rx"]?: string;
+ ["ry"]?: string;
+ ["shapeRendering"]?: string;
+ ["stopColor"]?: string;
+ ["stopOpacity"]?: string;
+ ["stroke"]?: string;
+ ["strokeDasharray"]?: string;
+ ["strokeDashoffset"]?: string;
+ ["strokeLinecap"]?: string;
+ ["strokeLinejoin"]?: string;
+ ["strokeMiterlimit"]?: string;
+ ["strokeOpacity"]?: string;
+ ["strokeWidth"]?: string;
+ ["textAnchor"]?: string;
+ ["textDecoration"]?: string;
+ ["textOverflow"]?: string;
+ ["textRendering"]?: string;
+ ["transform"]?: string;
+ ["transformOrigin"]?: string;
+ ["unicodeBidi"]?: string;
+ ["vectorEffect"]?: string;
+ ["visibility"]?: string;
+ ["whiteSpace"]?: string;
+ ["width"]?: string;
+ ["wordSpacing"]?: string;
+ ["writingMode"]?: string;
+ ["x"]?: string;
+ ["y"]?: string;
+ viewbox?: string;
+ };
+ interface IntrinsicElements {
+ a: BaseElementProps & {
+ href?: string;
+ target?: HTMLAnchorElement["target"];
+ };
+ img: BaseElementProps & {
+ src: string;
+ alt?: string;
+ width?: number;
+ height?: number;
+ };
+
+ // Void IntrinsicElements
+ // https://github.com/wooorm/html-void-elements
+ area: BaseElementPropsWithoutChildren;
+ base: BaseElementPropsWithoutChildren;
+ basefont: BaseElementPropsWithoutChildren;
+ bgsound: BaseElementPropsWithoutChildren;
+ br: BaseElementPropsWithoutChildren;
+ col: BaseElementPropsWithoutChildren;
+ command: BaseElementPropsWithoutChildren;
+ embed: BaseElementPropsWithoutChildren;
+ frame: BaseElementPropsWithoutChildren;
+ hr: BaseElementPropsWithoutChildren;
+ image: BaseElementPropsWithoutChildren;
+ img: BaseElementPropsWithoutChildren;
+ input: BaseElementPropsWithoutChildren;
+ keygen: BaseElementPropsWithoutChildren;
+ link: BaseElementPropsWithoutChildren;
+ meta: BaseElementPropsWithoutChildren;
+ param: BaseElementPropsWithoutChildren;
+ source: BaseElementPropsWithoutChildren;
+ track: BaseElementPropsWithoutChildren;
+ wbr: BaseElementPropsWithoutChildren;
+ svg: SvgProps;
+ path: SvgProps;
+ g: SvgProps;
+ [tagName: string]: BaseElementProps;
+ }
+ interface Element {
+ type: string | FC | null;
+ key?: string;
+ ref?: string;
+ children?: JSX.Children;
+ props: any;
+ }
+ interface ElementChildrenAttribute {
+ children: {};
+ }
+}
diff --git a/examples/didact/src/lib/Didact.ts b/examples/didact/src/lib/Didact.ts
new file mode 100644
index 0000000..38af1e5
--- /dev/null
+++ b/examples/didact/src/lib/Didact.ts
@@ -0,0 +1,314 @@
+import {
+ Dom,
+ Fiber,
+ FunctionFiber,
+ IntrinsicFiber,
+ Props,
+ Root,
+ Element,
+} from "./Types";
+import { Signal } from "signal-polyfill";
+
+export const createElement = (
+ type: string,
+ props?: Props,
+ ...children: Element[] | string[]
+): Element => {
+ return {
+ type,
+ props: {
+ ...props,
+ children: children.map((child) =>
+ typeof child === "object" ? child : createTextElement(child),
+ ),
+ },
+ };
+};
+
+export const createTextElement = (text: string): Element => {
+ return {
+ type: "TEXT_ELEMENT",
+ props: {
+ nodeValue: text,
+ children: [],
+ },
+ };
+};
+
+export const render = (element: JSX.Element, container: HTMLElement) => {
+ wipRoot = {
+ dom: container,
+ props: {
+ children: [element],
+ },
+ };
+ deletions = [];
+ nextUnitOfWork = wipRoot;
+};
+
+type PrevProps = Partial;
+
+const isEvent = (key: string) => key.startsWith("on");
+const isProperty = (key: string) => key !== "children" && !isEvent(key);
+const isNew = (prev: PrevProps, next: Props) => (key: string) =>
+ prev[key] !== next[key];
+const isGone = (_prev: PrevProps, next: Props) => (key: string) =>
+ !(key in next);
+const updateDom = (dom: Dom, prevProps: PrevProps, nextProps: Props) => {
+ console.log("updateDom", dom, prevProps, nextProps);
+ // Remove old or changed event listeners
+ Object.keys(prevProps)
+ .filter(isEvent)
+ .filter((key: string) => !(key in nextProps) || isNew(prevProps, nextProps))
+ .forEach((name: string) => {
+ const eventType = name.toLowerCase().substring(2);
+ // @ts-ignore
+ dom.removeEventListener(eventType, prevProps[name]);
+ });
+
+ // Remove old properties
+ Object.keys(prevProps)
+ .filter(isProperty)
+ .filter(isGone(prevProps, nextProps))
+ .forEach((name: string) => {
+ // @ts-ignore
+ dom[name] = "";
+ });
+
+ // Set new or changed properties
+ Object.keys(nextProps)
+ .filter(isProperty)
+ .filter(isNew(prevProps, nextProps))
+ .forEach((name: string) => {
+ // @ts-ignore
+ dom[name] = nextProps[name];
+ });
+
+ // Add event listeners
+ Object.keys(nextProps)
+ .filter(isEvent)
+ .filter(isNew(prevProps, nextProps))
+ .forEach((name: string) => {
+ const eventType = name.toLowerCase().substring(2);
+ // @ts-ignore
+ dom.addEventListener(eventType, nextProps[name]);
+ });
+};
+
+const commitRoot = () => {
+ deletions.forEach(commitWork);
+ commitWork(wipRoot!.child);
+ currentRoot = wipRoot!;
+ wipRoot = undefined;
+};
+
+const commitWork = (fiber?: Fiber) => {
+ if (!fiber) {
+ return;
+ }
+ let domParentFiber = fiber.parent;
+ while (!domParentFiber.dom) {
+ domParentFiber = domParentFiber.parent;
+ }
+ const domParent = domParentFiber.dom;
+
+ if (fiber.effectTag === "PLACEMENT" && typeof fiber.dom !== "undefined") {
+ domParent.appendChild(fiber.dom);
+ } else if (fiber.effectTag === "UPDATE" && typeof fiber.dom !== "undefined") {
+ updateDom(fiber.dom, fiber.alternate!.props, fiber.props);
+ } else if (fiber.effectTag === "DELETION") {
+ commitDeletion(fiber, domParent);
+ }
+ commitWork(fiber.child);
+ commitWork(fiber.sibling);
+};
+
+const commitDeletion = (fiber: Fiber, domParent: Dom) => {
+ if (fiber.dom) {
+ domParent.removeChild(fiber.dom);
+ } else {
+ commitDeletion(fiber.child!, domParent);
+ }
+};
+
+export const createDom = (fiber: IntrinsicFiber) => {
+ const dom =
+ fiber.type === "TEXT_ELEMENT"
+ ? document.createTextNode("")
+ : document.createElement(fiber.type);
+
+ updateDom(dom, {}, fiber.props);
+
+ return dom;
+};
+
+let nextUnitOfWork: Fiber | Root | undefined = undefined;
+let wipRoot: Root | undefined = undefined;
+let currentRoot: Root;
+let deletions: Fiber[] = [];
+
+const workLoop: IdleRequestCallback = (deadline) => {
+ let shouldYield = false;
+ while (nextUnitOfWork && !shouldYield) {
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
+ // @ts-ignore
+ shouldYield = deadline.timeRemaining() < 1;
+ }
+
+ if (!nextUnitOfWork && wipRoot) {
+ commitRoot();
+ }
+
+ requestIdleCallback(workLoop);
+};
+
+requestIdleCallback(workLoop);
+
+const performUnitOfWork = (fiber: Fiber | Root) => {
+ if (isFunctionFiber(fiber)) {
+ updateFunctionComponent(fiber);
+ } else {
+ updateHostComponent(fiber);
+ }
+
+ if (fiber.child) {
+ return fiber.child;
+ }
+
+ let nextFiber: Fiber | Root | undefined = fiber;
+ while (nextFiber) {
+ if (nextFiber.sibling) {
+ return nextFiber.sibling;
+ }
+
+ nextFiber = nextFiber.parent;
+ }
+};
+
+let wipFiber: Fiber;
+let hookIndex: number;
+
+const updateFunctionComponent = (fiber: FunctionFiber) => {
+ wipFiber = fiber;
+ hookIndex = 0;
+ wipFiber.hooks = [];
+ const children = [fiber.type(fiber.props)];
+ reconcileChildren(fiber, children);
+};
+
+export const useState = (initial: T) => {
+ type Action = T | ((prev: T) => T);
+ const oldHook = (wipFiber as FunctionFiber).alternate?.hooks?.[hookIndex];
+ if (oldHook?.state) {
+ console.log("oldHook.state", oldHook.state);
+ }
+ const signal: Signal.State =
+ oldHook?.state ?? new Signal.State(initial);
+ const hook: {
+ state: Signal.State;
+ queue: Action[];
+ } = {
+ state: signal,
+ queue: [],
+ };
+ const actions: Action[] = oldHook ? oldHook.queue : [];
+ actions.forEach((action) => {
+ if (action instanceof Function) {
+ hook.state.set(action(hook.state.get()));
+ } else {
+ hook.state.set(action);
+ }
+ });
+
+ const setState = (action: Action) => {
+ hook.queue.push(action);
+ wipRoot = {
+ dom: currentRoot.dom,
+ props: currentRoot.props,
+ alternate: currentRoot,
+ };
+ nextUnitOfWork = wipRoot;
+ deletions = [];
+ };
+
+ (wipFiber as FunctionFiber).hooks.push(hook);
+ hookIndex++;
+ const getState = () => hook.state.get();
+ getState.toString = () => {
+ throw "please use getState()";
+ };
+ return [getState, setState] as const;
+};
+
+const updateHostComponent = (fiber: IntrinsicFiber | Root) => {
+ if (!fiber.dom) {
+ // Case because Root always has a dom
+ fiber.dom = createDom(fiber as IntrinsicFiber);
+ }
+ reconcileChildren(fiber, fiber.props.children);
+};
+
+const reconcileChildren = (wipFiber: Fiber | Root, elements: Fiber[]) => {
+ let index = 0;
+ let oldFiber = wipFiber.alternate?.child;
+ // let oldFiber = wipFiber.alternate && wipFiber.alternate.child;
+ let prevSibling = null;
+
+ while (index < elements.length || oldFiber) {
+ const element = elements[index];
+ let newFiber: Fiber | undefined;
+
+ const sameType = oldFiber && element && element.type === oldFiber.type;
+
+ if (sameType && oldFiber) {
+ newFiber = {
+ type: oldFiber.type,
+ props: element.props,
+ dom: oldFiber.dom,
+ parent: wipFiber,
+ alternate: oldFiber,
+ effectTag: "UPDATE",
+ } as Fiber;
+ }
+
+ if (element && !sameType) {
+ newFiber = {
+ type: element.type,
+ props: element.props,
+ dom: undefined,
+ parent: wipFiber,
+ alternate: undefined,
+ effectTag: "PLACEMENT",
+ } as Fiber;
+ }
+
+ if (oldFiber && !sameType) {
+ oldFiber.effectTag = "DELETION";
+ deletions.push(oldFiber);
+ }
+
+ if (oldFiber) {
+ oldFiber = oldFiber.sibling;
+ }
+
+ if (index === 0) {
+ wipFiber.child = newFiber;
+ } else if (element) {
+ // @ts-ignore
+ prevSibling.sibling = newFiber;
+ }
+
+ prevSibling = newFiber;
+ index++;
+ }
+};
+
+const isFunctionFiber = (fiber: Fiber | Root): fiber is FunctionFiber => {
+ return fiber.type instanceof Function;
+};
+
+export const Didact = {
+ createElement,
+ render,
+ useState,
+};
diff --git a/examples/didact/src/lib/Types.ts b/examples/didact/src/lib/Types.ts
new file mode 100644
index 0000000..44d1849
--- /dev/null
+++ b/examples/didact/src/lib/Types.ts
@@ -0,0 +1,51 @@
+export interface Props {
+ children: Fiber[];
+ nodeValue?: string;
+ [key: string]: any;
+}
+
+export interface Element {
+ type: string;
+ props: {
+ children: Element[];
+ nodeValue?: string;
+ [key: string]: any;
+ };
+}
+
+export type Dom = HTMLElement | Text;
+
+type Hook = any;
+
+export interface IntrinsicFiber extends BaseFiber {
+ dom?: Dom;
+ type: string;
+ alternate?: IntrinsicFiber;
+}
+
+export interface FunctionFiber extends BaseFiber {
+ dom?: undefined;
+ type: JSX.FC;
+ hooks: Hook[];
+ alternate?: FunctionFiber;
+}
+
+export type Fiber = IntrinsicFiber | FunctionFiber;
+
+export interface BaseFiber extends JSX.Element {
+ parent: Fiber | Root;
+ alternate?: Fiber;
+ child?: Fiber;
+ sibling?: Fiber;
+ props: Props;
+ effectTag: "PLACEMENT" | "UPDATE" | "DELETION";
+}
+
+export interface Root
+ extends Omit {
+ dom: Dom;
+ child?: Fiber;
+ alternate?: Root;
+ type?: undefined;
+ parent?: undefined;
+}
diff --git a/examples/didact/src/lib/jsx-dev-runtime.ts b/examples/didact/src/lib/jsx-dev-runtime.ts
new file mode 100644
index 0000000..1e39ca3
--- /dev/null
+++ b/examples/didact/src/lib/jsx-dev-runtime.ts
@@ -0,0 +1,3 @@
+import { jsx, Fragment } from "./jsx-runtime";
+export const jsxDEV = jsx;
+export { jsx, Fragment };
diff --git a/examples/didact/src/lib/jsx-runtime.ts b/examples/didact/src/lib/jsx-runtime.ts
new file mode 100644
index 0000000..7f13723
--- /dev/null
+++ b/examples/didact/src/lib/jsx-runtime.ts
@@ -0,0 +1,14 @@
+import { createElement } from "./Didact";
+
+export type FC = (props: any) => JSX.Element | null | string;
+
+export const jsx = function (type: string | FC, fullProps: any): JSX.Element {
+ const { children, ...props } = fullProps;
+ return createElement(
+ type,
+ props,
+ ...(Array.isArray(children) ? children : [children]),
+ );
+};
+
+export const Fragment = createElement;
diff --git a/examples/didact/src/main.tsx b/examples/didact/src/main.tsx
new file mode 100644
index 0000000..82d512e
--- /dev/null
+++ b/examples/didact/src/main.tsx
@@ -0,0 +1,35 @@
+import Log from "./components/Log";
+import { Didact } from "./lib/Didact";
+import logo from "./typescript.svg";
+/** @jsx Didact.createElement */
+
+const e = (
+
+ Hello
+
+
alert("Test")} />
+
+
+);
+
+const App = (props) => {
+ const [count, setCount] = Didact.useState(1);
+
+ return (
+
+ Hello {props.name}
+
+ Count: {count()}
+ Log1
+
+ Log2
+ Log3
+
+
+
+
+
+ );
+};
+
+Didact.render(, document.getElementById("app")!);
diff --git a/examples/didact/src/style.css b/examples/didact/src/style.css
new file mode 100644
index 0000000..b528b6c
--- /dev/null
+++ b/examples/didact/src/style.css
@@ -0,0 +1,97 @@
+:root {
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-text-size-adjust: 100%;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+a:hover {
+ color: #535bf2;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+#app {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.vanilla:hover {
+ filter: drop-shadow(0 0 2em #3178c6aa);
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+button:hover {
+ border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
+ a:hover {
+ color: #747bff;
+ }
+ button {
+ background-color: #f9f9f9;
+ }
+}
diff --git a/examples/didact/src/typescript.svg b/examples/didact/src/typescript.svg
new file mode 100644
index 0000000..d91c910
--- /dev/null
+++ b/examples/didact/src/typescript.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/didact/src/vite-env.d.ts b/examples/didact/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/examples/didact/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/examples/didact/tsconfig.json b/examples/didact/tsconfig.json
new file mode 100644
index 0000000..a68f53a
--- /dev/null
+++ b/examples/didact/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+
+ "jsx": "react-jsx",
+ "jsxImportSource": "~/lib",
+ "paths": {
+ "~/*": ["./src/*"]
+ }
+ },
+ "include": ["src"]
+}
diff --git a/package.json b/package.json
index 8c4d121..a8c27c8 100644
--- a/package.json
+++ b/package.json
@@ -1,21 +1,8 @@
{
"name": "fast-web",
- "module": "index.ts",
- "type": "module",
- "scripts": {
- "start": "bun run src/index.tsx",
- "dev": "bun run --watch --hot src/index.tsx"
- },
- "devDependencies": {
- "@types/bun": "latest",
- "csstype": "^3.1.3",
- "typescript": "^5.8.3"
- },
- "dependencies": {
- "chokidar": "^4.0.3",
- "lightningcss": "^1.30.1",
- "lucide": "^0.525.0",
- "serve-static-bun": "^0.5.3"
- },
- "private": true
+ "private": true,
+ "workspaces": [
+ "packages/*",
+ "examples/*"
+ ]
}