First Commit
This commit is contained in:
commit
8177df7dd3
176
.gitignore
vendored
Normal file
176
.gitignore
vendored
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
npm-debug.log_
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Caches
|
||||||
|
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
|
||||||
|
pids
|
||||||
|
_.pid
|
||||||
|
_.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
|
||||||
|
.temp
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
||||||
|
/test
|
||||||
23
README.md
Normal file
23
README.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Bunext
|
||||||
|
|
||||||
|
A Next JS replacement built with bun JS and docker
|
||||||
|
|
||||||
|
## Running this application
|
||||||
|
|
||||||
|
To run development:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
To run production:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
You need `docker` installed to run this project
|
||||||
92
bun.lock
Normal file
92
bun.lock
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "bun-next",
|
||||||
|
"dependencies": {
|
||||||
|
"commander": "^14.0.2",
|
||||||
|
"ora": "^9.0.0",
|
||||||
|
"react": "^19.2.0",
|
||||||
|
"react-dom": "^19.2.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
"@types/node": "^24.10.0",
|
||||||
|
"@types/react": "^19.2.2",
|
||||||
|
"@types/react-dom": "^19.2.2",
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.0.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@types/bun": ["@types/bun@1.2.3", "", { "dependencies": { "bun-types": "1.2.3" } }, "sha512-054h79ipETRfjtsCW9qJK8Ipof67Pw9bodFWmkfkaUaRiIQ1dIV2VTlheshlBx3mpKr0KeK8VqnMMCtgN9rQtw=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="],
|
||||||
|
|
||||||
|
"@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
|
||||||
|
|
||||||
|
"@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="],
|
||||||
|
|
||||||
|
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
|
||||||
|
|
||||||
|
"ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.2.3", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-P7AeyTseLKAvgaZqQrvp3RqFM3yN9PlcLuSTe7SoJOfZkER73mLdT2vEQi8U64S1YvM/ldcNiQjn0Sn7H9lGgg=="],
|
||||||
|
|
||||||
|
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||||
|
|
||||||
|
"cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
|
||||||
|
|
||||||
|
"cli-spinners": ["cli-spinners@3.3.0", "", {}, "sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ=="],
|
||||||
|
|
||||||
|
"commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="],
|
||||||
|
|
||||||
|
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||||
|
|
||||||
|
"get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||||
|
|
||||||
|
"is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="],
|
||||||
|
|
||||||
|
"is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
|
||||||
|
|
||||||
|
"log-symbols": ["log-symbols@7.0.1", "", { "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" } }, "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg=="],
|
||||||
|
|
||||||
|
"mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
|
||||||
|
|
||||||
|
"onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
|
||||||
|
|
||||||
|
"ora": ["ora@9.0.0", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.2.2", "string-width": "^8.1.0", "strip-ansi": "^7.1.2" } }, "sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A=="],
|
||||||
|
|
||||||
|
"react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
|
||||||
|
|
||||||
|
"react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="],
|
||||||
|
|
||||||
|
"restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
|
||||||
|
|
||||||
|
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||||
|
|
||||||
|
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||||
|
|
||||||
|
"stdin-discarder": ["stdin-discarder@0.2.2", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="],
|
||||||
|
|
||||||
|
"string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="],
|
||||||
|
|
||||||
|
"strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||||
|
|
||||||
|
"yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="],
|
||||||
|
|
||||||
|
"@types/ws/@types/node": ["@types/node@22.13.5", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg=="],
|
||||||
|
|
||||||
|
"bun-types/@types/node": ["@types/node@22.13.5", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg=="],
|
||||||
|
|
||||||
|
"@types/ws/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||||
|
|
||||||
|
"bun-types/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
13
commands/build/index.ts
Normal file
13
commands/build/index.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Command } from "commander";
|
||||||
|
import grabConfig from "../../functions/grab-config";
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return new Command("build")
|
||||||
|
.description("Build project")
|
||||||
|
.action(async () => {
|
||||||
|
console.log(`Building project ...`);
|
||||||
|
|
||||||
|
const config = await grabConfig();
|
||||||
|
global.CONFIG = config;
|
||||||
|
});
|
||||||
|
}
|
||||||
42
commands/dev/index.ts
Normal file
42
commands/dev/index.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { Command } from "commander";
|
||||||
|
import grabConfig from "../../functions/grab-config";
|
||||||
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
|
import AppNames from "../../utils/grab-app-names";
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return new Command("dev")
|
||||||
|
.description("Run development server")
|
||||||
|
.action(async () => {
|
||||||
|
console.log(`Running development server ...`);
|
||||||
|
|
||||||
|
const config = await grabConfig();
|
||||||
|
global.CONFIG = config;
|
||||||
|
|
||||||
|
const { entrypoint } = grabDirNames();
|
||||||
|
const { defaultDistDir } = AppNames;
|
||||||
|
|
||||||
|
let buildCmd = ["bun"];
|
||||||
|
|
||||||
|
buildCmd.push(
|
||||||
|
"build",
|
||||||
|
entrypoint,
|
||||||
|
"--outdir",
|
||||||
|
config.distDir || defaultDistDir,
|
||||||
|
"--watch"
|
||||||
|
);
|
||||||
|
|
||||||
|
const spawnedProcess = Bun.spawn({
|
||||||
|
cmd: buildCmd,
|
||||||
|
});
|
||||||
|
|
||||||
|
const exitCode = await spawnedProcess.exited;
|
||||||
|
|
||||||
|
// Bun.build({
|
||||||
|
// entrypoints: [entrypoint],
|
||||||
|
// outdir: config.distDir || defaultDistDir,
|
||||||
|
// minify: true,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// await startServer();
|
||||||
|
});
|
||||||
|
}
|
||||||
10
commands/init/index.ts
Normal file
10
commands/init/index.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Command } from "commander";
|
||||||
|
import AppNames from "../../utils/grab-app-names";
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return new Command("init")
|
||||||
|
.description("Initialize project")
|
||||||
|
.action(async () => {
|
||||||
|
console.log(`Initializing ${AppNames.name} ...`);
|
||||||
|
});
|
||||||
|
}
|
||||||
13
commands/start/index.ts
Normal file
13
commands/start/index.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Command } from "commander";
|
||||||
|
import grabConfig from "../../functions/grab-config";
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return new Command("start")
|
||||||
|
.description("Start production server")
|
||||||
|
.action(async () => {
|
||||||
|
console.log(`Starting production server ...`);
|
||||||
|
|
||||||
|
const config = await grabConfig();
|
||||||
|
global.CONFIG = config;
|
||||||
|
});
|
||||||
|
}
|
||||||
22
functions/grab-config.ts
Normal file
22
functions/grab-config.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { existsSync } from "fs";
|
||||||
|
import type { BunextConfig } from "../types";
|
||||||
|
import grabDirNames from "../utils/grab-dir-names";
|
||||||
|
import exitWithError from "../utils/exit-with-error";
|
||||||
|
|
||||||
|
export default async function grabConfig(): Promise<BunextConfig> {
|
||||||
|
const { configFile } = grabDirNames();
|
||||||
|
|
||||||
|
if (!existsSync(configFile)) {
|
||||||
|
exitWithError(`Config file \`${configFile}\` doesn't exist!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = (await import(configFile)).default as BunextConfig;
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
exitWithError(
|
||||||
|
`Config file \`${configFile}\` is invalid! Please provide a valid default export in your config file.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
47
functions/router/get-route.ts
Normal file
47
functions/router/get-route.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
|
import type { GetRouteReturn } from "../../types";
|
||||||
|
import grabAssetsPrefix from "../../utils/grab-assets-prefix";
|
||||||
|
import grabOrigin from "../../utils/grab-origin";
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
route: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function getRoute({
|
||||||
|
route,
|
||||||
|
}: Params): Promise<GetRouteReturn | null> {
|
||||||
|
const { pagesDir } = grabDirNames();
|
||||||
|
|
||||||
|
if (route.match(/\(/)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const assetPrefix = grabAssetsPrefix();
|
||||||
|
const origin = grabOrigin();
|
||||||
|
|
||||||
|
const router = new Bun.FileSystemRouter({
|
||||||
|
style: "nextjs",
|
||||||
|
dir: pagesDir,
|
||||||
|
origin,
|
||||||
|
assetPrefix,
|
||||||
|
});
|
||||||
|
|
||||||
|
const match = router.match(route);
|
||||||
|
|
||||||
|
if (!match?.filePath) {
|
||||||
|
console.error(`Route ${route} not found`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const module = await import(match.filePath);
|
||||||
|
|
||||||
|
return {
|
||||||
|
match,
|
||||||
|
module,
|
||||||
|
component: module.default,
|
||||||
|
serverProps: module.serverProps,
|
||||||
|
staticProps: module.staticProps,
|
||||||
|
staticPaths: module.staticPaths,
|
||||||
|
staticParams: module.staticParams,
|
||||||
|
};
|
||||||
|
}
|
||||||
31
functions/server/grab-route-content.tsx
Normal file
31
functions/server/grab-route-content.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import type { GetRouteReturn } from "../../types";
|
||||||
|
import AppNames from "../../utils/grab-app-names";
|
||||||
|
import ReactDOMServer from "react-dom/server";
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
url: URL;
|
||||||
|
route: GetRouteReturn;
|
||||||
|
req: Request;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function grabRouteContent({
|
||||||
|
url,
|
||||||
|
route,
|
||||||
|
req,
|
||||||
|
}: Params): Promise<Response> {
|
||||||
|
const config = global.CONFIG;
|
||||||
|
const { name } = AppNames;
|
||||||
|
|
||||||
|
let html = `Welcome to ${name} ...`;
|
||||||
|
|
||||||
|
if (route.component) {
|
||||||
|
html = ReactDOMServer.renderToString(<route.component />);
|
||||||
|
return new Response(html, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "text/html; charset=utf-8",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(html);
|
||||||
|
}
|
||||||
47
functions/server/start-server.ts
Normal file
47
functions/server/start-server.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import AppNames from "../../utils/grab-app-names";
|
||||||
|
import grabAppPort from "../../utils/grab-app-port";
|
||||||
|
import getRoute from "../router/get-route";
|
||||||
|
import grabRouteContent from "./grab-route-content";
|
||||||
|
|
||||||
|
export default async function startServer() {
|
||||||
|
const config = global.CONFIG;
|
||||||
|
const port = grabAppPort();
|
||||||
|
const { name } = AppNames;
|
||||||
|
|
||||||
|
const server = Bun.serve({
|
||||||
|
async fetch(req) {
|
||||||
|
try {
|
||||||
|
const url = new URL(req.url);
|
||||||
|
|
||||||
|
const route = await getRoute({ route: url.pathname });
|
||||||
|
|
||||||
|
if (!route) {
|
||||||
|
return new Response(`Route ${url.pathname} not Found!`, {
|
||||||
|
status: 404,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await grabRouteContent({ req, route, url });
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(`No Response!`, {
|
||||||
|
status: 404,
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
return new Response(`Server Error: ${error.message}`, {
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
port,
|
||||||
|
development: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
global.SERVER = server;
|
||||||
|
console.log(`${name} Server Running on Port ${server.port} ...`);
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
54
index.ts
Normal file
54
index.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import { program } from "commander";
|
||||||
|
import start from "./commands/start";
|
||||||
|
import dev from "./commands/dev";
|
||||||
|
import build from "./commands/build";
|
||||||
|
import init from "./commands/init";
|
||||||
|
import ora, { type Ora } from "ora";
|
||||||
|
import type { BunextConfig } from "./types";
|
||||||
|
import type { Server } from "bun";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # Declare Global Variables
|
||||||
|
*/
|
||||||
|
declare global {
|
||||||
|
var ORA_SPINNER: Ora;
|
||||||
|
var CONFIG: BunextConfig;
|
||||||
|
var SERVER: Server | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
global.ORA_SPINNER = ora();
|
||||||
|
global.ORA_SPINNER.clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # Describe Program
|
||||||
|
*/
|
||||||
|
program
|
||||||
|
.name(`bunext`)
|
||||||
|
.description(`A React Next JS replacement built with bun JS`)
|
||||||
|
.version(`1.0.0`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # Declare Commands
|
||||||
|
*/
|
||||||
|
program.addCommand(start());
|
||||||
|
program.addCommand(dev());
|
||||||
|
program.addCommand(build());
|
||||||
|
program.addCommand(init());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # Handle Unavailable Commands
|
||||||
|
*/
|
||||||
|
program.on("command:*", () => {
|
||||||
|
console.error(
|
||||||
|
"Invalid command: %s\nSee --help for a list of available commands.",
|
||||||
|
program.args.join(" ")
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # Parse Arguments
|
||||||
|
*/
|
||||||
|
program.parse(Bun.argv);
|
||||||
31
package.json
Normal file
31
package.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "bunext",
|
||||||
|
"module": "index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"bin": {
|
||||||
|
"bunext": "index.ts"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"index.ts"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"dev": "cd envs/development && docker compose down && docker compose up --build",
|
||||||
|
"start": "cd envs/production && docker compose down && docker compose up -d --build",
|
||||||
|
"preview": "cd envs/preview && docker compose down && docker compose up -d --build"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
"@types/node": "^24.10.0",
|
||||||
|
"@types/react": "^19.2.2",
|
||||||
|
"@types/react-dom": "^19.2.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"commander": "^14.0.2",
|
||||||
|
"ora": "^9.0.0",
|
||||||
|
"react": "^19.2.0",
|
||||||
|
"react-dom": "^19.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Enable latest features
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"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,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
||||||
59
types/index.ts
Normal file
59
types/index.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import type { MatchedRoute } from "bun";
|
||||||
|
|
||||||
|
export type ServerProps = {
|
||||||
|
params: Record<string, string>;
|
||||||
|
searchParams: Record<string, string>;
|
||||||
|
headers: Headers;
|
||||||
|
cookies: Record<string, string>;
|
||||||
|
body: any;
|
||||||
|
method: string;
|
||||||
|
url: string;
|
||||||
|
pathname: string;
|
||||||
|
query: Record<string, string>;
|
||||||
|
search: string;
|
||||||
|
hash: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StaticProps = {
|
||||||
|
params: Record<string, string>;
|
||||||
|
searchParams: Record<string, string>;
|
||||||
|
headers: Headers;
|
||||||
|
cookies: Record<string, string>;
|
||||||
|
body: any;
|
||||||
|
method: string;
|
||||||
|
url: string;
|
||||||
|
pathname: string;
|
||||||
|
query: Record<string, string>;
|
||||||
|
search: string;
|
||||||
|
hash: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StaticPaths = string[];
|
||||||
|
|
||||||
|
export type StaticParams = Record<string, string>;
|
||||||
|
|
||||||
|
export type PageModule = {
|
||||||
|
component: React.ComponentType<any>;
|
||||||
|
serverProps: ServerProps;
|
||||||
|
staticProps: StaticProps;
|
||||||
|
staticPaths: StaticPaths;
|
||||||
|
staticParams: StaticParams;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BunextConfig = {
|
||||||
|
distDir?: string;
|
||||||
|
assetsPrefix?: string;
|
||||||
|
origin?: string;
|
||||||
|
globalVars?: { [k: string]: any };
|
||||||
|
port?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetRouteReturn = {
|
||||||
|
match: MatchedRoute;
|
||||||
|
module: PageModule;
|
||||||
|
component: React.ComponentType<any>;
|
||||||
|
serverProps: ServerProps;
|
||||||
|
staticProps: StaticProps;
|
||||||
|
staticPaths: StaticPaths;
|
||||||
|
staticParams: StaticParams;
|
||||||
|
};
|
||||||
4
utils/exit-with-error.ts
Normal file
4
utils/exit-with-error.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default function exitWithError(msg: string, code?: number) {
|
||||||
|
console.error(msg);
|
||||||
|
process.exit(code || 1);
|
||||||
|
}
|
||||||
8
utils/grab-app-names.ts
Normal file
8
utils/grab-app-names.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
const AppNames = {
|
||||||
|
defaultPort: 7000,
|
||||||
|
defaultAssetPrefix: "_bunext/static",
|
||||||
|
name: "Bunext",
|
||||||
|
defaultDistDir: ".bunext",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export default AppNames;
|
||||||
16
utils/grab-app-port.ts
Normal file
16
utils/grab-app-port.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import AppNames from "./grab-app-names";
|
||||||
|
import numberfy from "./numberfy";
|
||||||
|
|
||||||
|
export default function grabAppPort() {
|
||||||
|
if (process.env.PORT) {
|
||||||
|
return numberfy(process.env.PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global.CONFIG.port) {
|
||||||
|
return global.CONFIG.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { defaultPort } = AppNames;
|
||||||
|
|
||||||
|
return numberfy(defaultPort);
|
||||||
|
}
|
||||||
11
utils/grab-assets-prefix.ts
Normal file
11
utils/grab-assets-prefix.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import AppNames from "./grab-app-names";
|
||||||
|
|
||||||
|
export default function grabAssetsPrefix() {
|
||||||
|
if (global.CONFIG.assetsPrefix) {
|
||||||
|
return global.CONFIG.assetsPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { defaultAssetPrefix } = AppNames;
|
||||||
|
|
||||||
|
return defaultAssetPrefix;
|
||||||
|
}
|
||||||
37
utils/grab-dir-names.ts
Normal file
37
utils/grab-dir-names.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import path from "path";
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
dir?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function grabDirNames(params?: Params) {
|
||||||
|
const rootDir = params?.dir || process.cwd();
|
||||||
|
const appDir = path.resolve(__dirname, "..");
|
||||||
|
const entrypoint = path.join(
|
||||||
|
appDir,
|
||||||
|
"functions",
|
||||||
|
"server",
|
||||||
|
"start-server.ts"
|
||||||
|
);
|
||||||
|
|
||||||
|
const pagesDir = path.join(rootDir, "pages");
|
||||||
|
const componentsDir = path.join(rootDir, "components");
|
||||||
|
const publicDir = path.join(rootDir, "public");
|
||||||
|
const stylesDir = path.join(rootDir, "styles");
|
||||||
|
const utilsDir = path.join(rootDir, "utils");
|
||||||
|
const typesDir = path.join(rootDir, "types");
|
||||||
|
const configFile = path.join(rootDir, "bunext.config.ts");
|
||||||
|
|
||||||
|
return {
|
||||||
|
rootDir,
|
||||||
|
pagesDir,
|
||||||
|
componentsDir,
|
||||||
|
publicDir,
|
||||||
|
stylesDir,
|
||||||
|
utilsDir,
|
||||||
|
typesDir,
|
||||||
|
configFile,
|
||||||
|
appDir,
|
||||||
|
entrypoint,
|
||||||
|
};
|
||||||
|
}
|
||||||
11
utils/grab-origin.ts
Normal file
11
utils/grab-origin.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import grabAppPort from "./grab-app-port";
|
||||||
|
|
||||||
|
export default function grabOrigin() {
|
||||||
|
if (global.CONFIG.origin) {
|
||||||
|
return global.CONFIG.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
const port = grabAppPort();
|
||||||
|
|
||||||
|
return `http://localhost:${port}`;
|
||||||
|
}
|
||||||
33
utils/numberfy.ts
Normal file
33
utils/numberfy.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
export default function numberfy(num: any, decimals?: number): number {
|
||||||
|
try {
|
||||||
|
const numberString = String(num)
|
||||||
|
.replace(/[^0-9\.]/g, "")
|
||||||
|
.replace(/\.$/, "");
|
||||||
|
|
||||||
|
if (!numberString.match(/./)) return 0;
|
||||||
|
|
||||||
|
const existingDecimals = numberString.match(/\./)
|
||||||
|
? numberString.split(".").pop()?.length
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const numberfiedNum = Number(numberString);
|
||||||
|
|
||||||
|
if (typeof numberfiedNum !== "number") return 0;
|
||||||
|
if (isNaN(numberfiedNum)) return 0;
|
||||||
|
|
||||||
|
if (decimals == 0) {
|
||||||
|
return Math.round(Number(numberfiedNum));
|
||||||
|
} else if (decimals) {
|
||||||
|
return Number(numberfiedNum.toFixed(decimals));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingDecimals)
|
||||||
|
return Number(numberfiedNum.toFixed(existingDecimals));
|
||||||
|
return Math.round(numberfiedNum);
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log(`Numberfy ERROR: ${error.message}`);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const _n = numberfy;
|
||||||
Loading…
Reference in New Issue
Block a user