First Commit

This commit is contained in:
Benjamin Toby 2025-11-05 07:12:15 +01:00
commit 8177df7dd3
22 changed files with 807 additions and 0 deletions

176
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

View 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,
};
}

View 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);
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}

View 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
View 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
View 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
View 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;