Compare commits
30
Commits
cb5126c947
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bf1b651db | ||
|
|
247a64c873 | ||
|
|
f590deb11b | ||
|
|
3426c7b53b | ||
|
|
afd1af1827 | ||
|
|
22d3dedab2 | ||
|
|
8d12329f01 | ||
|
|
78e86b3999 | ||
|
|
87948340b0 | ||
|
|
86ea86e7bd | ||
|
|
596b9de047 | ||
|
|
9da1e16318 | ||
|
|
823c5bb1ca | ||
|
|
88ead3b3d6 | ||
|
|
45509deff8 | ||
|
|
a19863b3e9 | ||
|
|
a9cd51d71c | ||
|
|
e3a0f5fbeb | ||
|
|
1d0ac4aa80 | ||
|
|
817beacc7a | ||
|
|
61a9d8d612 | ||
|
|
cd9ac833dc | ||
|
|
e2b8b95a4b | ||
|
|
c06cb73181 | ||
|
|
f3bb972a20 | ||
|
|
40a987b983 | ||
|
|
ceeb6fbdaf | ||
|
|
4f5445e3df | ||
|
|
b702e26bf6 | ||
|
|
3b26292124 |
@@ -182,3 +182,4 @@ __fixtures__
|
|||||||
/.dump
|
/.dump
|
||||||
/.vscode
|
/.vscode
|
||||||
/source.md
|
/source.md
|
||||||
|
SECURITY.md
|
||||||
@@ -742,6 +742,9 @@ const config: BunextConfig = {
|
|||||||
globalVars: {
|
globalVars: {
|
||||||
MY_API_URL: "https://api.example.com",
|
MY_API_URL: "https://api.example.com",
|
||||||
},
|
},
|
||||||
|
public_envs: {
|
||||||
|
BUNEXT_PUBLIC_APP_NAME: "My App",
|
||||||
|
},
|
||||||
development: false, // forced by the CLI; set manually if needed
|
development: false, // forced by the CLI; set manually if needed
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -755,6 +758,7 @@ export default config;
|
|||||||
| `distDir` | `string` | `.bunext` | Internal artifact directory |
|
| `distDir` | `string` | `.bunext` | Internal artifact directory |
|
||||||
| `assetsPrefix` | `string` | `_bunext/static` | URL prefix for static assets |
|
| `assetsPrefix` | `string` | `_bunext/static` | URL prefix for static assets |
|
||||||
| `globalVars` | `{ [k: string]: any }` | — | Variables injected globally at build time |
|
| `globalVars` | `{ [k: string]: any }` | — | Variables injected globally at build time |
|
||||||
|
| `public_envs` | `Record<string, string>` | — | Public env vars exposed to the client via `window.process.env` (see [Environment Variables](#environment-variables)) |
|
||||||
| `development` | `boolean` | — | Overridden to `true` by `bunext dev` automatically |
|
| `development` | `boolean` | — | Overridden to `true` by `bunext dev` automatically |
|
||||||
| `defaultCacheExpiry` | `number` | `3600` | Global page cache expiry in seconds |
|
| `defaultCacheExpiry` | `number` | `3600` | Global page cache expiry in seconds |
|
||||||
| `middleware` | `(params: BunextConfigMiddlewareParams) => Response \| undefined \| Promise<...>` | — | Global middleware — see [Middleware](#middleware) |
|
| `middleware` | `(params: BunextConfigMiddlewareParams) => Response \| undefined \| Promise<...>` | — | Global middleware — see [Middleware](#middleware) |
|
||||||
@@ -910,8 +914,34 @@ bun run server.ts
|
|||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
| -------- | ------------------------------------------------------- |
|
| ------------------ | ------------------------------------------------------------------------------------------------ |
|
||||||
| `PORT` | Override the server port (takes precedence over config) |
|
| `PORT` | Override the server port (takes precedence over config) |
|
||||||
|
| `BUNEXT_PUBLIC_*` | Any env var prefixed with `BUNEXT_PUBLIC_` is exposed to the client via `window.process.env` |
|
||||||
|
|
||||||
|
### Public Environment Variables
|
||||||
|
|
||||||
|
Variables prefixed with `BUNEXT_PUBLIC_` are automatically injected into every page as `window.process.env`. You can also define public envs in config via `public_envs` (config values override env vars of the same name):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# .env
|
||||||
|
BUNEXT_PUBLIC_API_URL=https://api.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// bunext.config.ts
|
||||||
|
const config: BunextConfig = {
|
||||||
|
public_envs: {
|
||||||
|
BUNEXT_PUBLIC_APP_NAME: "My App",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Client component — available after hydration
|
||||||
|
const apiUrl = window.process.env.BUNEXT_PUBLIC_API_URL;
|
||||||
|
```
|
||||||
|
|
||||||
|
`window.process.env` always includes `NODE_ENV` (`"development"` or `"production"`).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"@types/react-dom": "^19.2.2",
|
"@types/react-dom": "^19.2.2",
|
||||||
"bun-plugin-tailwind": "^0.1.2",
|
"bun-plugin-tailwind": "^0.1.2",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
|
"chokidar": "^5.0.0",
|
||||||
"commander": "^14.0.2",
|
"commander": "^14.0.2",
|
||||||
"esbuild": "^0.27.4",
|
"esbuild": "^0.27.4",
|
||||||
"lightningcss-wasm": "^1.32.0",
|
"lightningcss-wasm": "^1.32.0",
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/dom": "^10.4.1",
|
"@testing-library/dom": "^10.4.1",
|
||||||
|
"@types/chokidar": "^2.1.7",
|
||||||
"@types/lodash": "^4.17.24",
|
"@types/lodash": "^4.17.24",
|
||||||
"@types/micromatch": "^4.0.10",
|
"@types/micromatch": "^4.0.10",
|
||||||
"happy-dom": "^20.8.4",
|
"happy-dom": "^20.8.4",
|
||||||
@@ -165,6 +167,8 @@
|
|||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
||||||
|
|
||||||
|
"@types/chokidar": ["@types/chokidar@2.1.7", "", { "dependencies": { "chokidar": "*" } }, "sha512-A7/MFHf6KF7peCzjEC1BBTF8jpmZTokb3vr/A0NxRGfwRLK3Ws+Hq6ugVn6cJIMfM6wkCak/aplWrxbTcu8oig=="],
|
||||||
|
|
||||||
"@types/lodash": ["@types/lodash@4.17.24", "", {}, "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ=="],
|
"@types/lodash": ["@types/lodash@4.17.24", "", {}, "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ=="],
|
||||||
|
|
||||||
"@types/micromatch": ["@types/micromatch@4.0.10", "", { "dependencies": { "@types/braces": "*" } }, "sha512-5jOhFDElqr4DKTrTEbnW8DZ4Hz5LRUEmyrGpCMrD/NphYv3nUnaF08xmSLx1rGGnyEs/kFnhiw6dCgcDqMr5PQ=="],
|
"@types/micromatch": ["@types/micromatch@4.0.10", "", { "dependencies": { "@types/braces": "*" } }, "sha512-5jOhFDElqr4DKTrTEbnW8DZ4Hz5LRUEmyrGpCMrD/NphYv3nUnaF08xmSLx1rGGnyEs/kFnhiw6dCgcDqMr5PQ=="],
|
||||||
@@ -195,6 +199,8 @@
|
|||||||
|
|
||||||
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||||
|
|
||||||
|
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||||
|
|
||||||
"cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
|
"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=="],
|
"cli-spinners": ["cli-spinners@3.3.0", "", {}, "sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ=="],
|
||||||
@@ -291,6 +297,8 @@
|
|||||||
|
|
||||||
"react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="],
|
"react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="],
|
||||||
|
|
||||||
|
"readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||||
|
|
||||||
"restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
|
"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=="],
|
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||||
|
|||||||
Vendored
+4
-10
@@ -1,9 +1,8 @@
|
|||||||
import { Command } from "commander";
|
import { Command } from "commander";
|
||||||
import { log } from "../../utils/log";
|
import { log } from "../../utils/log";
|
||||||
import init from "../../functions/init";
|
|
||||||
import grabDirNames from "../../utils/grab-dir-names";
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
import { rmSync } from "fs";
|
import { rmSync } from "fs";
|
||||||
import allPagesESBuildContextBundler from "../../functions/bundler/all-pages-esbuild-context-bundler";
|
import bunextInit from "../../functions/bunext-init";
|
||||||
const { HYDRATION_DST_DIR, BUNX_CWD_PAGES_REWRITE_DIR } = grabDirNames();
|
const { HYDRATION_DST_DIR, BUNX_CWD_PAGES_REWRITE_DIR } = grabDirNames();
|
||||||
export default function () {
|
export default function () {
|
||||||
return new Command("build")
|
return new Command("build")
|
||||||
@@ -14,14 +13,9 @@ export default function () {
|
|||||||
rmSync(BUNX_CWD_PAGES_REWRITE_DIR, { recursive: true });
|
rmSync(BUNX_CWD_PAGES_REWRITE_DIR, { recursive: true });
|
||||||
}
|
}
|
||||||
catch (error) { }
|
catch (error) { }
|
||||||
global.SKIPPED_BROWSER_MODULES = new Set();
|
global.BUNEXT_SKIPPED_BROWSER_MODULES = new Set();
|
||||||
// await rewritePagesModule();
|
await bunextInit({ build_only: true });
|
||||||
await init();
|
log.success("Modules Built Successfully!");
|
||||||
log.banner();
|
|
||||||
log.build("Building Project ...");
|
|
||||||
// await allPagesBunBundler();
|
|
||||||
// await allPagesBundler();
|
|
||||||
await allPagesESBuildContextBundler();
|
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+11
@@ -4,11 +4,22 @@ import bunextInit from "../../functions/bunext-init";
|
|||||||
import grabDirNames from "../../utils/grab-dir-names";
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
import { rmSync } from "fs";
|
import { rmSync } from "fs";
|
||||||
const { HYDRATION_DST_DIR, BUNX_CWD_PAGES_REWRITE_DIR } = grabDirNames();
|
const { HYDRATION_DST_DIR, BUNX_CWD_PAGES_REWRITE_DIR } = grabDirNames();
|
||||||
|
process.on("uncaughtException", (error) => {
|
||||||
|
log.error(`Uncaught exception: ${error}`);
|
||||||
|
});
|
||||||
|
process.on("unhandledRejection", (reason) => {
|
||||||
|
log.error(`Unhandled rejection: ${reason}`);
|
||||||
|
});
|
||||||
log.info("Running development server ...");
|
log.info("Running development server ...");
|
||||||
try {
|
try {
|
||||||
rmSync(HYDRATION_DST_DIR, { recursive: true });
|
rmSync(HYDRATION_DST_DIR, { recursive: true });
|
||||||
rmSync(BUNX_CWD_PAGES_REWRITE_DIR, { recursive: true });
|
rmSync(BUNX_CWD_PAGES_REWRITE_DIR, { recursive: true });
|
||||||
}
|
}
|
||||||
catch (error) { }
|
catch (error) { }
|
||||||
|
try {
|
||||||
await bunextInit();
|
await bunextInit();
|
||||||
await startServer();
|
await startServer();
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
log.error(`Failed to start development server: ${error}`);
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+23
-4
@@ -2,6 +2,7 @@ import { Command } from "commander";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import grabDirNames from "../../utils/grab-dir-names";
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
import writeErrorFile from "../../functions/write-error-file";
|
import writeErrorFile from "../../functions/write-error-file";
|
||||||
|
import { existsSync } from "fs";
|
||||||
let retries = 0;
|
let retries = 0;
|
||||||
let timeout;
|
let timeout;
|
||||||
const MAX_RETRIES = 5;
|
const MAX_RETRIES = 5;
|
||||||
@@ -19,8 +20,12 @@ async function dev() {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
const dev_spawn_file = path.resolve(__dirname, "dev-spawn.ts");
|
const dev_spawn_file = path.resolve(__dirname, "dev-spawn.ts");
|
||||||
|
const dev_spawn_js_file = path.resolve(__dirname, "dev-spawn.js");
|
||||||
|
const final_spawn_file = existsSync(dev_spawn_js_file)
|
||||||
|
? dev_spawn_js_file
|
||||||
|
: dev_spawn_file;
|
||||||
const spawn_options = {
|
const spawn_options = {
|
||||||
cmd: ["bun", dev_spawn_file],
|
cmd: ["bun", final_spawn_file],
|
||||||
stdio: ["inherit", "inherit", "inherit"],
|
stdio: ["inherit", "inherit", "inherit"],
|
||||||
async onExit(subprocess, exitCode, signalCode, error) {
|
async onExit(subprocess, exitCode, signalCode, error) {
|
||||||
writeErrorFile({ exitCode, error });
|
writeErrorFile({ exitCode, error });
|
||||||
@@ -30,13 +35,27 @@ async function dev() {
|
|||||||
NODE_ENV: "development",
|
NODE_ENV: "development",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let dev_process = Bun.spawn(spawn_options);
|
let dev_process;
|
||||||
|
try {
|
||||||
|
dev_process = Bun.spawn(spawn_options);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(`Failed to start dev process:`, error);
|
||||||
retries++;
|
retries++;
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
retries = 0;
|
retries = 0;
|
||||||
}, 10000);
|
}, 10000);
|
||||||
const exited = await dev_process.exited;
|
|
||||||
if (exited) {
|
|
||||||
return await dev();
|
return await dev();
|
||||||
}
|
}
|
||||||
|
const exited = await dev_process.exited;
|
||||||
|
if (exited) {
|
||||||
|
retries++;
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
retries = 0;
|
||||||
|
}, 10000);
|
||||||
|
return await dev();
|
||||||
|
}
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
retries = 0;
|
||||||
|
}, 10000);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+36
-5
@@ -1,6 +1,10 @@
|
|||||||
import { Command } from "commander";
|
import { Command } from "commander";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import writeErrorFile from "../../functions/write-error-file";
|
import writeErrorFile from "../../functions/write-error-file";
|
||||||
|
import { existsSync } from "fs";
|
||||||
|
let retries = 0;
|
||||||
|
let timeout;
|
||||||
|
const MAX_RETRIES = 5;
|
||||||
export default function () {
|
export default function () {
|
||||||
return new Command("start")
|
return new Command("start")
|
||||||
.description("Start production server")
|
.description("Start production server")
|
||||||
@@ -9,9 +13,18 @@ export default function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
async function start() {
|
async function start() {
|
||||||
const dev_spawn_file = path.resolve(__dirname, "prod-spawn.ts");
|
clearTimeout(timeout);
|
||||||
|
if (retries >= MAX_RETRIES) {
|
||||||
|
console.error(`Production server crashed ${MAX_RETRIES} times. Exiting.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const prod_spawn_file = path.resolve(__dirname, "prod-spawn.ts");
|
||||||
|
const prod_spawn_js_file = path.resolve(__dirname, "prod-spawn.js");
|
||||||
|
const final_spawn_file = existsSync(prod_spawn_js_file)
|
||||||
|
? prod_spawn_js_file
|
||||||
|
: prod_spawn_file;
|
||||||
const spawn_options = {
|
const spawn_options = {
|
||||||
cmd: ["bun", dev_spawn_file],
|
cmd: ["bun", final_spawn_file],
|
||||||
stdio: ["inherit", "inherit", "inherit"],
|
stdio: ["inherit", "inherit", "inherit"],
|
||||||
onExit(subprocess, exitCode, signalCode, error) {
|
onExit(subprocess, exitCode, signalCode, error) {
|
||||||
writeErrorFile({ exitCode, error });
|
writeErrorFile({ exitCode, error });
|
||||||
@@ -21,9 +34,27 @@ async function start() {
|
|||||||
NODE_ENV: "production",
|
NODE_ENV: "production",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let dev_process = Bun.spawn(spawn_options);
|
let dev_process;
|
||||||
const exited = await dev_process.exited;
|
try {
|
||||||
if (exited) {
|
dev_process = Bun.spawn(spawn_options);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(`Failed to start production process:`, error);
|
||||||
|
retries++;
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
retries = 0;
|
||||||
|
}, 10000);
|
||||||
return await start();
|
return await start();
|
||||||
}
|
}
|
||||||
|
const exited = await dev_process.exited;
|
||||||
|
if (exited) {
|
||||||
|
retries++;
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
retries = 0;
|
||||||
|
}, 10000);
|
||||||
|
return await start();
|
||||||
|
}
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
retries = 0;
|
||||||
|
}, 10000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ type Params = {
|
|||||||
post_build_fn?: (params: {
|
post_build_fn?: (params: {
|
||||||
artifacts: BundlerCTXMap[];
|
artifacts: BundlerCTXMap[];
|
||||||
}) => Promise<void> | void;
|
}) => Promise<void> | void;
|
||||||
|
build_only?: boolean;
|
||||||
|
start?: boolean;
|
||||||
};
|
};
|
||||||
export default function allPagesESBuildContextBundler(params?: Params): Promise<void>;
|
export default function allPagesESBuildContextBundler(params?: Params): Promise<void>;
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export default async function allPagesESBuildContextBundler(params) {
|
|||||||
try {
|
try {
|
||||||
const did_process_exit_because_of_bundler_error = existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE);
|
const did_process_exit_because_of_bundler_error = existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE);
|
||||||
const pages = grabAllPages({ exclude_api: true });
|
const pages = grabAllPages({ exclude_api: true });
|
||||||
global.PAGE_FILES = pages;
|
global.BUNEXT_PAGE_FILES = pages;
|
||||||
const dev = isDevelopment();
|
const dev = isDevelopment();
|
||||||
const entryToPage = new Map();
|
const entryToPage = new Map();
|
||||||
for (const page of pages) {
|
for (const page of pages) {
|
||||||
@@ -28,7 +28,7 @@ export default async function allPagesESBuildContextBundler(params) {
|
|||||||
entryToPage.set(entryFile, { ...page, tsx });
|
entryToPage.set(entryFile, { ...page, tsx });
|
||||||
}
|
}
|
||||||
const entryPoints = [...entryToPage.keys()].map((e) => `hydration-virtual:${e}`);
|
const entryPoints = [...entryToPage.keys()].map((e) => `hydration-virtual:${e}`);
|
||||||
global.BUNDLER_CTX = await esbuild.context({
|
global.BUNEXT_BUNDLER_CTX = await esbuild.context({
|
||||||
entryPoints,
|
entryPoints,
|
||||||
outdir: HYDRATION_DST_DIR,
|
outdir: HYDRATION_DST_DIR,
|
||||||
bundle: true,
|
bundle: true,
|
||||||
@@ -50,6 +50,7 @@ export default async function allPagesESBuildContextBundler(params) {
|
|||||||
esbuildCTXArtifactTracker({
|
esbuildCTXArtifactTracker({
|
||||||
entryToPage,
|
entryToPage,
|
||||||
post_build_fn: params?.post_build_fn,
|
post_build_fn: params?.post_build_fn,
|
||||||
|
build_only: params?.build_only || params?.start,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
jsx: "automatic",
|
jsx: "automatic",
|
||||||
@@ -61,13 +62,13 @@ export default async function allPagesESBuildContextBundler(params) {
|
|||||||
"react-dom/client",
|
"react-dom/client",
|
||||||
"react/jsx-runtime",
|
"react/jsx-runtime",
|
||||||
"react/jsx-dev-runtime",
|
"react/jsx-dev-runtime",
|
||||||
...(global.CONFIG.page_compiler_excludes || []),
|
...(global.BUNEXT_CONFIG.page_compiler_excludes || []),
|
||||||
],
|
],
|
||||||
logLevel: did_process_exit_because_of_bundler_error
|
logLevel: did_process_exit_because_of_bundler_error
|
||||||
? "silent"
|
? "silent"
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
await global.BUNDLER_CTX.rebuild();
|
await global.BUNEXT_BUNDLER_CTX.rebuild();
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.log(`ESBUILD Error =>`, error);
|
console.log(`ESBUILD Error =>`, error);
|
||||||
|
|||||||
+13
-8
@@ -1,13 +1,18 @@
|
|||||||
export default async function buildOnstartErrorHandler(params) {
|
export default async function buildOnstartErrorHandler(params) {
|
||||||
// const error_msg = `Build Failed. Please check all your components and imports.`;
|
// const error_msg = `Build Failed. Please check all your components and imports.`;
|
||||||
// log.error(error_msg);
|
// log.error(error_msg);
|
||||||
global.BUNDLER_CTX_DISPOSED = true;
|
if (global.BUNEXT_BUNDLER_CTX_DISPOSED) {
|
||||||
global.RECOMPILING = false;
|
return;
|
||||||
global.IS_SERVER_COMPONENT = false;
|
}
|
||||||
Promise.all([
|
// console.log(`Killing Bundler ...`);
|
||||||
global.SSR_BUNDLER_CTX?.dispose(),
|
// console.log(`global.BUNEXT_BUNDLER_CTX_DISPOSED`, global.BUNEXT_BUNDLER_CTX_DISPOSED);
|
||||||
global.BUNDLER_CTX?.dispose(),
|
global.BUNEXT_BUNDLER_CTX_DISPOSED = true;
|
||||||
|
global.BUNEXT_RECOMPILING = false;
|
||||||
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||||
|
await Promise.all([
|
||||||
|
global.BUNEXT_SSR_BUNDLER_CTX?.dispose(),
|
||||||
|
global.BUNEXT_BUNDLER_CTX?.dispose(),
|
||||||
]);
|
]);
|
||||||
global.SSR_BUNDLER_CTX = undefined;
|
global.BUNEXT_SSR_BUNDLER_CTX = undefined;
|
||||||
global.BUNDLER_CTX = undefined;
|
global.BUNEXT_BUNDLER_CTX = undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -62,7 +62,7 @@ export default async function bunReactModulesBundler() {
|
|||||||
});
|
});
|
||||||
rmSync(tmpDir, { force: true, recursive: true });
|
rmSync(tmpDir, { force: true, recursive: true });
|
||||||
const PUBLIC_ROOT = BUNEXT_VENDOR_DIR.replace(BUNX_CWD_DIR, "/.bunext");
|
const PUBLIC_ROOT = BUNEXT_VENDOR_DIR.replace(BUNX_CWD_DIR, "/.bunext");
|
||||||
global.REACT_IMPORTS_MAP = {
|
global.BUNEXT_REACT_IMPORTS_MAP = {
|
||||||
imports: {
|
imports: {
|
||||||
react: `${PUBLIC_ROOT}/react.js`,
|
react: `${PUBLIC_ROOT}/react.js`,
|
||||||
"react-dom": `${PUBLIC_ROOT}/react-dom.js`,
|
"react-dom": `${PUBLIC_ROOT}/react-dom.js`,
|
||||||
|
|||||||
+8
-2
@@ -16,7 +16,7 @@ export default async function pagesSSRBundler(params) {
|
|||||||
include_server: true,
|
include_server: true,
|
||||||
});
|
});
|
||||||
const dev = isDevelopment();
|
const dev = isDevelopment();
|
||||||
const config = global.CONFIG;
|
const config = global.BUNEXT_CONFIG;
|
||||||
try {
|
try {
|
||||||
writeFileSync(path.join(BUNX_TMP_DIR, "ssr-pages.json"), JSON.stringify(pages, null, 4));
|
writeFileSync(path.join(BUNX_TMP_DIR, "ssr-pages.json"), JSON.stringify(pages, null, 4));
|
||||||
}
|
}
|
||||||
@@ -48,6 +48,7 @@ export default async function pagesSSRBundler(params) {
|
|||||||
writeFileSync(path.join(BUNX_TMP_DIR, "ssr-entrypoints.json"), JSON.stringify(entryPoints, null, 4));
|
writeFileSync(path.join(BUNX_TMP_DIR, "ssr-entrypoints.json"), JSON.stringify(entryPoints, null, 4));
|
||||||
}
|
}
|
||||||
catch (error) { }
|
catch (error) { }
|
||||||
|
try {
|
||||||
await esbuild.build({
|
await esbuild.build({
|
||||||
entryPoints,
|
entryPoints,
|
||||||
outdir: BUNX_CWD_MODULE_CACHE_DIR,
|
outdir: BUNX_CWD_MODULE_CACHE_DIR,
|
||||||
@@ -78,11 +79,16 @@ export default async function pagesSSRBundler(params) {
|
|||||||
"react/jsx-runtime",
|
"react/jsx-runtime",
|
||||||
"react/jsx-dev-runtime",
|
"react/jsx-dev-runtime",
|
||||||
"bun:*",
|
"bun:*",
|
||||||
|
"bun",
|
||||||
"sqlite-vec",
|
"sqlite-vec",
|
||||||
"better-sqlite3",
|
"better-sqlite3",
|
||||||
...(config.ssr_compiler_excludes || []),
|
...(config.ssr_compiler_excludes || []),
|
||||||
],
|
],
|
||||||
splitting: true,
|
splitting: true,
|
||||||
// logLevel: "silent",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
catch (error) {
|
||||||
|
global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED = true;
|
||||||
|
log.error(`SSR Bundler Error: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+5
-5
@@ -11,9 +11,9 @@ const { BUNX_CWD_MODULE_CACHE_DIR } = grabDirNames();
|
|||||||
export default async function pagesSSRContextBundler(params) {
|
export default async function pagesSSRContextBundler(params) {
|
||||||
const pages = grabAllPages();
|
const pages = grabAllPages();
|
||||||
const dev = isDevelopment();
|
const dev = isDevelopment();
|
||||||
if (global.SSR_BUNDLER_CTX) {
|
if (global.BUNEXT_SSR_BUNDLER_CTX) {
|
||||||
await global.SSR_BUNDLER_CTX.dispose();
|
await global.BUNEXT_SSR_BUNDLER_CTX.dispose();
|
||||||
global.SSR_BUNDLER_CTX = undefined;
|
global.BUNEXT_SSR_BUNDLER_CTX = undefined;
|
||||||
}
|
}
|
||||||
const entryToPage = new Map();
|
const entryToPage = new Map();
|
||||||
const { root_file_path } = grabRootFilePath();
|
const { root_file_path } = grabRootFilePath();
|
||||||
@@ -32,7 +32,7 @@ export default async function pagesSSRContextBundler(params) {
|
|||||||
entryToPage.set(page.local_path, { ...page, tsx });
|
entryToPage.set(page.local_path, { ...page, tsx });
|
||||||
}
|
}
|
||||||
const entryPoints = [...entryToPage.keys()].map((e) => `ssr-virtual:${e}`);
|
const entryPoints = [...entryToPage.keys()].map((e) => `ssr-virtual:${e}`);
|
||||||
global.SSR_BUNDLER_CTX = await esbuild.context({
|
global.BUNEXT_SSR_BUNDLER_CTX = await esbuild.context({
|
||||||
entryPoints,
|
entryPoints,
|
||||||
outdir: BUNX_CWD_MODULE_CACHE_DIR,
|
outdir: BUNX_CWD_MODULE_CACHE_DIR,
|
||||||
bundle: true,
|
bundle: true,
|
||||||
@@ -65,5 +65,5 @@ export default async function pagesSSRContextBundler(params) {
|
|||||||
],
|
],
|
||||||
// logLevel: "silent",
|
// logLevel: "silent",
|
||||||
});
|
});
|
||||||
await global.SSR_BUNDLER_CTX.rebuild();
|
await global.BUNEXT_SSR_BUNDLER_CTX.rebuild();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const BunSkipNonBrowserPlugin = {
|
|||||||
const skipFilter = /^(bun:|node:|fs$|path$|os$|crypto$|net$|events$|util$|tls$|url$|process$)/;
|
const skipFilter = /^(bun:|node:|fs$|path$|os$|crypto$|net$|events$|util$|tls$|url$|process$)/;
|
||||||
// const skipped_modules = new Set<string>();
|
// const skipped_modules = new Set<string>();
|
||||||
build.onResolve({ filter: skipFilter }, (args) => {
|
build.onResolve({ filter: skipFilter }, (args) => {
|
||||||
global.SKIPPED_BROWSER_MODULES.add(args.path);
|
global.BUNEXT_SKIPPED_BROWSER_MODULES.add(args.path);
|
||||||
return {
|
return {
|
||||||
path: args.path,
|
path: args.path,
|
||||||
namespace: "skipped",
|
namespace: "skipped",
|
||||||
@@ -13,8 +13,8 @@ const BunSkipNonBrowserPlugin = {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
// build.onEnd(() => {
|
// build.onEnd(() => {
|
||||||
// log.warn(`global.SKIPPED_BROWSER_MODULES`, [
|
// log.warn(`global.BUNEXT_SKIPPED_BROWSER_MODULES`, [
|
||||||
// ...global.SKIPPED_BROWSER_MODULES,
|
// ...global.BUNEXT_SKIPPED_BROWSER_MODULES,
|
||||||
// ]);
|
// ]);
|
||||||
// });
|
// });
|
||||||
// build.onResolve({ filter: /^[^./]/ }, (args) => {
|
// build.onResolve({ filter: /^[^./]/ }, (args) => {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ type Params = {
|
|||||||
post_build_fn?: (params: {
|
post_build_fn?: (params: {
|
||||||
artifacts: any[];
|
artifacts: any[];
|
||||||
}) => Promise<void> | void;
|
}) => Promise<void> | void;
|
||||||
|
build_only?: boolean;
|
||||||
};
|
};
|
||||||
export default function esbuildCTXArtifactTracker({ entryToPage, post_build_fn, }: Params): Plugin;
|
export default function esbuildCTXArtifactTracker({ entryToPage, post_build_fn, build_only, }: Params): Plugin;
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
+43
-32
@@ -11,37 +11,35 @@ import path from "path";
|
|||||||
import cleanupLogsDirs from "../../cleanup-logs-dir";
|
import cleanupLogsDirs from "../../cleanup-logs-dir";
|
||||||
const { BUNX_BUNDLER_ERROR_EXIT_FILE, BUNX_ERROR_LOGS_DIR } = grabDirNames();
|
const { BUNX_BUNDLER_ERROR_EXIT_FILE, BUNX_ERROR_LOGS_DIR } = grabDirNames();
|
||||||
let build_start = 0;
|
let build_start = 0;
|
||||||
let build_starts = 0;
|
|
||||||
const MAX_BUILD_STARTS = 2;
|
const MAX_BUILD_STARTS = 2;
|
||||||
export default function esbuildCTXArtifactTracker({ entryToPage, post_build_fn, }) {
|
export default function esbuildCTXArtifactTracker({ entryToPage, post_build_fn, build_only, }) {
|
||||||
const artifactTracker = {
|
const artifactTracker = {
|
||||||
name: "artifact-tracker",
|
name: "artifact-tracker",
|
||||||
setup(build) {
|
setup(build) {
|
||||||
build.onStart(async () => {
|
build.onStart(async () => {
|
||||||
build_starts++;
|
global.BUNEXT_MAIN_CTX_BUILD_STARTS++;
|
||||||
build_start = performance.now();
|
build_start = performance.now();
|
||||||
const does_error_file_exist = existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE);
|
const does_error_file_exist = existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE);
|
||||||
if (build_starts >= MAX_BUILD_STARTS &&
|
if (global.BUNEXT_MAIN_CTX_BUILD_STARTS >= MAX_BUILD_STARTS &&
|
||||||
!does_error_file_exist) {
|
!does_error_file_exist) {
|
||||||
await buildOnstartErrorHandler();
|
await buildOnstartErrorHandler();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
build.onEnd((result) => {
|
build.onEnd(async (result) => {
|
||||||
if (result.errors.length > 0) {
|
if (result.errors.length > 0) {
|
||||||
global.RECOMPILING = false;
|
global.BUNEXT_RECOMPILING = false;
|
||||||
global.IS_SERVER_COMPONENT = false;
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||||
build_starts = 0;
|
|
||||||
log.error(`Build errors:`);
|
log.error(`Build errors:`);
|
||||||
for (const err of result.errors) {
|
for (const err of result.errors) {
|
||||||
log.error(` ${err.text}${err.location ? ` (${err.location.file}:${err.location.line}:${err.location.column})` : ""}`);
|
log.error(` ${err.text}${err.location ? ` (${err.location.file}:${err.location.line}:${err.location.column})` : ""}`);
|
||||||
}
|
}
|
||||||
for (let i = global.HMR_CONTROLLERS.length - 1; i >= 0; i--) {
|
for (let i = global.BUNEXT_HMR_CONTROLLERS.length - 1; i >= 0; i--) {
|
||||||
const controller = global.HMR_CONTROLLERS[i];
|
const controller = global.BUNEXT_HMR_CONTROLLERS[i];
|
||||||
try {
|
try {
|
||||||
controller?.controller?.enqueue(`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`);
|
controller?.controller?.enqueue(`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`);
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
global.HMR_CONTROLLERS.splice(i, 1);
|
global.BUNEXT_HMR_CONTROLLERS.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -53,39 +51,52 @@ export default function esbuildCTXArtifactTracker({ entryToPage, post_build_fn,
|
|||||||
if (artifacts?.[0] && artifacts.length > 0) {
|
if (artifacts?.[0] && artifacts.length > 0) {
|
||||||
for (let i = 0; i < artifacts.length; i++) {
|
for (let i = 0; i < artifacts.length; i++) {
|
||||||
const artifact = artifacts[i];
|
const artifact = artifacts[i];
|
||||||
if (artifact?.local_path && global.BUNDLER_CTX_MAP) {
|
if (artifact?.local_path &&
|
||||||
global.BUNDLER_CTX_MAP[artifact.local_path] =
|
global.BUNEXT_BUNDLER_CTX_MAP) {
|
||||||
_.merge(global.BUNDLER_CTX_MAP[artifact.local_path], artifact);
|
global.BUNEXT_BUNDLER_CTX_MAP[artifact.local_path] =
|
||||||
|
_.merge(global.BUNEXT_BUNDLER_CTX_MAP[artifact.local_path], artifact);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post_build_fn?.({ artifacts });
|
|
||||||
}
|
}
|
||||||
const elapsed = (performance.now() - build_start).toFixed(0);
|
const elapsed = (performance.now() - build_start).toFixed(0);
|
||||||
log.success(`[Built] in ${elapsed}ms`);
|
log.success(`[Built] in ${elapsed}ms`);
|
||||||
global.RECOMPILING = false;
|
global.BUNEXT_MAIN_CTX_BUILD_STARTS = 0;
|
||||||
global.IS_SERVER_COMPONENT = false;
|
global.BUNEXT_BUNDLER_CTX_DISPOSED = false;
|
||||||
build_starts = 0;
|
|
||||||
const does_error_file_exist = existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE);
|
const does_error_file_exist = existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE);
|
||||||
if (does_error_file_exist) {
|
// SSR must finish before HMR so server props are fresh
|
||||||
mkdirSync(BUNX_ERROR_LOGS_DIR, { recursive: true });
|
if (build_only) {
|
||||||
cpSync(BUNX_BUNDLER_ERROR_EXIT_FILE, path.join(BUNX_ERROR_LOGS_DIR, `${Date.now()}.log`));
|
|
||||||
rmSync(BUNX_BUNDLER_ERROR_EXIT_FILE, { force: true });
|
|
||||||
cleanupLogsDirs();
|
|
||||||
fullRebuild();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
try {
|
try {
|
||||||
pagesSSRBundler();
|
await pagesSSRBundler();
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
log.error(`SSR Bundler Error: ${error}`);
|
log.error(`SSR Bundler Error: ${error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if (global.SSR_BUNDLER_CTX) {
|
else if (does_error_file_exist) {
|
||||||
// global.SSR_BUNDLER_CTX.rebuild();
|
mkdirSync(BUNX_ERROR_LOGS_DIR, { recursive: true });
|
||||||
// } else {
|
cpSync(BUNX_BUNDLER_ERROR_EXIT_FILE, path.join(BUNX_ERROR_LOGS_DIR, `${Date.now()}.log`));
|
||||||
// pagesSSRContextBundler();
|
rmSync(BUNX_BUNDLER_ERROR_EXIT_FILE, { force: true });
|
||||||
// }
|
cleanupLogsDirs();
|
||||||
|
await fullRebuild();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
await pagesSSRBundler();
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
log.error(`SSR Bundler Error: ${error}`);
|
||||||
|
}
|
||||||
|
if (artifacts?.[0] && artifacts.length > 0) {
|
||||||
|
try {
|
||||||
|
await post_build_fn?.({ artifacts });
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
log.error(`Post-build Error: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
global.BUNEXT_RECOMPILING = false;
|
||||||
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+17
-11
@@ -15,16 +15,23 @@ export default function ssrCTXArtifactTracker({ entryToPage, post_build_fn, }) {
|
|||||||
build_starts++;
|
build_starts++;
|
||||||
build_start = performance.now();
|
build_start = performance.now();
|
||||||
if (build_starts == MAX_BUILD_STARTS) {
|
if (build_starts == MAX_BUILD_STARTS) {
|
||||||
global.SSR_BUNDLER_CTX_DISPOSED = true;
|
global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED = true;
|
||||||
await global.SSR_BUNDLER_CTX?.dispose();
|
await global.BUNEXT_SSR_BUNDLER_CTX?.dispose();
|
||||||
global.SSR_BUNDLER_CTX = undefined;
|
global.BUNEXT_SSR_BUNDLER_CTX = undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
build.onEnd((result) => {
|
build.onEnd(async (result) => {
|
||||||
if (result.errors.length > 0) {
|
if (result.errors.length > 0) {
|
||||||
global.SSR_BUNDLER_CTX_DISPOSED = false;
|
global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED = true;
|
||||||
|
try {
|
||||||
|
await global.BUNEXT_SSR_BUNDLER_CTX?.dispose();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
global.BUNEXT_SSR_BUNDLER_CTX = undefined;
|
||||||
build_starts = 0;
|
build_starts = 0;
|
||||||
console.log("SSR Build errors:", result.errors);
|
for (const err of result.errors) {
|
||||||
|
console.error(`SSR Build error: ${err.text}${err.location ? ` (${err.location.file}:${err.location.line}:${err.location.column})` : ""}`);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const artifacts = grabArtifactsFromBundledResults({
|
const artifacts = grabArtifactsFromBundledResults({
|
||||||
@@ -36,9 +43,8 @@ export default function ssrCTXArtifactTracker({ entryToPage, post_build_fn, }) {
|
|||||||
for (let i = 0; i < artifacts.length; i++) {
|
for (let i = 0; i < artifacts.length; i++) {
|
||||||
const artifact = artifacts[i];
|
const artifact = artifacts[i];
|
||||||
if (artifact?.local_path &&
|
if (artifact?.local_path &&
|
||||||
global.SSR_BUNDLER_CTX_MAP) {
|
global.BUNEXT_SSR_BUNDLER_CTX_MAP) {
|
||||||
global.SSR_BUNDLER_CTX_MAP[artifact.local_path] =
|
global.BUNEXT_SSR_BUNDLER_CTX_MAP[artifact.local_path] = artifact;
|
||||||
artifact;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// post_build_fn?.({ artifacts });
|
// post_build_fn?.({ artifacts });
|
||||||
@@ -48,10 +54,10 @@ export default function ssrCTXArtifactTracker({ entryToPage, post_build_fn, }) {
|
|||||||
// log.success(`SSR [Built] in ${elapsed}ms`);
|
// log.success(`SSR [Built] in ${elapsed}ms`);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
writeFileSync(path.join(BUNX_TMP_DIR, "ctx-map.json"), JSON.stringify(global.SSR_BUNDLER_CTX_MAP, null, 4));
|
writeFileSync(path.join(BUNX_TMP_DIR, "ctx-map.json"), JSON.stringify(global.BUNEXT_SSR_BUNDLER_CTX_MAP, null, 4));
|
||||||
}
|
}
|
||||||
catch (error) { }
|
catch (error) { }
|
||||||
global.SSR_BUNDLER_CTX_DISPOSED = false;
|
global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import { log } from "../../../utils/log";
|
|
||||||
export default function virtualFilesPlugin({ entryToPage }) {
|
export default function virtualFilesPlugin({ entryToPage }) {
|
||||||
const virtualPlugin = {
|
const virtualPlugin = {
|
||||||
name: "virtual-hydration",
|
name: "virtual-hydration",
|
||||||
|
|||||||
+1
-1
@@ -65,7 +65,7 @@ export default async function reactModulesBundler() {
|
|||||||
});
|
});
|
||||||
rmSync(tmpDir, { force: true, recursive: true });
|
rmSync(tmpDir, { force: true, recursive: true });
|
||||||
const PUBLIC_ROOT = BUNEXT_VENDOR_DIR.replace(BUNX_CWD_DIR, "/.bunext");
|
const PUBLIC_ROOT = BUNEXT_VENDOR_DIR.replace(BUNX_CWD_DIR, "/.bunext");
|
||||||
global.REACT_IMPORTS_MAP = {
|
global.BUNEXT_REACT_IMPORTS_MAP = {
|
||||||
imports: {
|
imports: {
|
||||||
react: `${PUBLIC_ROOT}/react.js`,
|
react: `${PUBLIC_ROOT}/react.js`,
|
||||||
"react-dom": `${PUBLIC_ROOT}/react-dom.js`,
|
"react-dom": `${PUBLIC_ROOT}/react-dom.js`,
|
||||||
|
|||||||
+2
-2
@@ -8,8 +8,8 @@ export default async function recordArtifacts({ artifacts, page_file_paths, }) {
|
|||||||
artifacts_map[artifact.local_path] = artifact;
|
artifacts_map[artifact.local_path] = artifact;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (global.BUNDLER_CTX_MAP) {
|
if (global.BUNEXT_BUNDLER_CTX_MAP) {
|
||||||
global.BUNDLER_CTX_MAP = _.merge(global.BUNDLER_CTX_MAP, artifacts_map);
|
global.BUNEXT_BUNDLER_CTX_MAP = _.merge(global.BUNEXT_BUNDLER_CTX_MAP, artifacts_map);
|
||||||
}
|
}
|
||||||
// await Bun.write(
|
// await Bun.write(
|
||||||
// HYDRATION_DST_DIR_MAP_JSON_FILE,
|
// HYDRATION_DST_DIR_MAP_JSON_FILE,
|
||||||
|
|||||||
Vendored
+36
-31
@@ -1,49 +1,54 @@
|
|||||||
import type { BundlerCTXMap, BunextConfig, GlobalHMRControllerObject, PageFiles } from "../types";
|
import type { BundlerCTXMap, BunextConfig, GlobalHMRControllerObject, PageFiles } from "../types";
|
||||||
import type { FileSystemRouter, Server } from "bun";
|
import type { FileSystemRouter, Server } from "bun";
|
||||||
import grabDirNames from "../utils/grab-dir-names";
|
import { type DirNames } from "../utils/grab-dir-names";
|
||||||
import { type FSWatcher } from "fs";
|
|
||||||
import type { BuildContext } from "esbuild";
|
import type { BuildContext } from "esbuild";
|
||||||
import grabConstants from "../utils/grab-constants";
|
import grabConstants from "../utils/grab-constants";
|
||||||
|
import type { FSWatcher } from "fs";
|
||||||
/**
|
/**
|
||||||
* # Declare Global Variables
|
* # Declare Global Variables
|
||||||
*/
|
*/
|
||||||
declare global {
|
declare global {
|
||||||
var CONFIG: BunextConfig;
|
var BUNEXT_CONFIG: BunextConfig;
|
||||||
var SERVER: Server<any> | undefined;
|
var BUNEXT_SERVER: Server<any> | undefined;
|
||||||
var RECOMPILING: boolean;
|
var BUNEXT_RECOMPILING: boolean;
|
||||||
var BUILDING_SSR: boolean;
|
var BUNEXT_BUILDING_SSR: boolean;
|
||||||
var IS_SERVER_COMPONENT: boolean;
|
var BUNEXT_IS_SERVER_COMPONENT: boolean;
|
||||||
var WATCHER_TIMEOUT: any;
|
var BUNEXT_WATCHER_TIMEOUT: any;
|
||||||
var ROUTER: FileSystemRouter;
|
var BUNEXT_ROUTER: FileSystemRouter;
|
||||||
var HMR_CONTROLLERS: GlobalHMRControllerObject[];
|
var BUNEXT_HMR_CONTROLLERS: GlobalHMRControllerObject[];
|
||||||
var LAST_BUILD_TIME: number;
|
var BUNEXT_LAST_BUILD_TIME: number;
|
||||||
var BUNDLER_CTX_MAP: {
|
var BUNEXT_BUNDLER_CTX_MAP: {
|
||||||
[k: string]: BundlerCTXMap;
|
[k: string]: BundlerCTXMap;
|
||||||
};
|
};
|
||||||
var SSR_BUNDLER_CTX_MAP: {
|
var BUNEXT_SSR_BUNDLER_CTX_MAP: {
|
||||||
[k: string]: BundlerCTXMap;
|
[k: string]: BundlerCTXMap;
|
||||||
};
|
};
|
||||||
var BUNDLER_REBUILDS: 0;
|
var BUNEXT_BUNDLER_REBUILDS: 0;
|
||||||
var PAGES_SRC_WATCHER: FSWatcher | undefined;
|
var BUNEXT_PAGES_SRC_WATCHER: FSWatcher | undefined;
|
||||||
var CURRENT_VERSION: string | undefined;
|
var BUNEXT_CURRENT_VERSION: string | undefined;
|
||||||
var PAGE_FILES: PageFiles[];
|
var BUNEXT_PAGE_FILES: PageFiles[];
|
||||||
var ROOT_FILE_UPDATED: boolean;
|
var BUNEXT_ROOT_FILE_UPDATED: boolean;
|
||||||
var SKIPPED_BROWSER_MODULES: Set<string>;
|
var BUNEXT_SKIPPED_BROWSER_MODULES: Set<string>;
|
||||||
var BUNDLER_CTX: BuildContext | undefined;
|
var BUNEXT_BUNDLER_CTX: BuildContext | undefined;
|
||||||
var SSR_BUNDLER_CTX: BuildContext | undefined;
|
var BUNEXT_SSR_BUNDLER_CTX: BuildContext | undefined;
|
||||||
var DIR_NAMES: ReturnType<typeof grabDirNames>;
|
var BUNEXT_DIR_NAMES: DirNames;
|
||||||
var REACT_IMPORTS_MAP: {
|
var BUNEXT_REACT_IMPORTS_MAP: {
|
||||||
imports: Record<string, string>;
|
imports: Record<string, string>;
|
||||||
};
|
};
|
||||||
var REACT_DOM_SERVER: any;
|
var BUNEXT_REACT_DOM_SERVER: any;
|
||||||
var REACT_DOM_MODULE_CACHE: Map<string, {
|
var BUNEXT_REACT_DOM_MODULE_CACHE: Map<string, {
|
||||||
main: any;
|
main: any;
|
||||||
css: string;
|
css: string;
|
||||||
}>;
|
}>;
|
||||||
var BUNDLER_CTX_DISPOSED: boolean | undefined;
|
var BUNEXT_BUNDLER_CTX_DISPOSED: boolean | undefined;
|
||||||
var SSR_BUNDLER_CTX_DISPOSED: boolean | undefined;
|
var BUNEXT_SSR_BUNDLER_CTX_DISPOSED: boolean | undefined;
|
||||||
var REBUILD_RETRIES: number;
|
var BUNEXT_REBUILD_RETRIES: number;
|
||||||
var IS_404_PAGE: boolean;
|
var BUNEXT_IS_404_PAGE: boolean;
|
||||||
var CONSTANTS: ReturnType<typeof grabConstants>;
|
var BUNEXT_CONSTANTS: ReturnType<typeof grabConstants>;
|
||||||
|
var BUNEXT_MAIN_CTX_BUILD_STARTS: number;
|
||||||
}
|
}
|
||||||
export default function bunextInit(): Promise<void>;
|
type Params = {
|
||||||
|
build_only?: boolean;
|
||||||
|
};
|
||||||
|
export default function bunextInit(params?: Params): Promise<void>;
|
||||||
|
export {};
|
||||||
|
|||||||
Vendored
+26
-22
@@ -1,50 +1,54 @@
|
|||||||
import grabDirNames from "../utils/grab-dir-names";
|
import grabDirNames, {} from "../utils/grab-dir-names";
|
||||||
import {} from "fs";
|
|
||||||
import init from "./init";
|
import init from "./init";
|
||||||
import isDevelopment from "../utils/is-development";
|
import isDevelopment from "../utils/is-development";
|
||||||
import { log } from "../utils/log";
|
import { log } from "../utils/log";
|
||||||
import cron from "./server/cron";
|
import cron from "./server/cron";
|
||||||
import watcherEsbuildCTX from "./server/watcher-esbuild-ctx";
|
|
||||||
import allPagesESBuildContextBundler from "./bundler/all-pages-esbuild-context-bundler";
|
import allPagesESBuildContextBundler from "./bundler/all-pages-esbuild-context-bundler";
|
||||||
import serverPostBuildFn from "./server/server-post-build-fn";
|
import serverPostBuildFn from "./server/server-post-build-fn";
|
||||||
import reactModulesBundler from "./bundler/react-modules-bundler";
|
import reactModulesBundler from "./bundler/react-modules-bundler";
|
||||||
import grabConstants from "../utils/grab-constants";
|
import grabConstants from "../utils/grab-constants";
|
||||||
|
import watcherEsbuildCTX from "./server/watcher-esbuild-ctx";
|
||||||
const dirNames = grabDirNames();
|
const dirNames = grabDirNames();
|
||||||
const { PAGES_DIR } = dirNames;
|
const { PAGES_DIR } = dirNames;
|
||||||
export default async function bunextInit() {
|
export default async function bunextInit(params) {
|
||||||
global.HMR_CONTROLLERS = [];
|
global.BUNEXT_HMR_CONTROLLERS = [];
|
||||||
global.BUNDLER_CTX_MAP = {};
|
global.BUNEXT_BUNDLER_CTX_MAP = {};
|
||||||
global.SSR_BUNDLER_CTX_MAP = {};
|
global.BUNEXT_SSR_BUNDLER_CTX_MAP = {};
|
||||||
// global.API_ROUTES_BUNDLER_CTX_MAP = {};
|
// global.BUNEXT_API_ROUTES_BUNDLER_CTX_MAP = {};
|
||||||
global.BUNDLER_REBUILDS = 0;
|
global.BUNEXT_BUNDLER_REBUILDS = 0;
|
||||||
global.REBUILD_RETRIES = 0;
|
global.BUNEXT_REBUILD_RETRIES = 0;
|
||||||
global.PAGE_FILES = [];
|
global.BUNEXT_PAGE_FILES = [];
|
||||||
global.SKIPPED_BROWSER_MODULES = new Set();
|
global.BUNEXT_SKIPPED_BROWSER_MODULES = new Set();
|
||||||
global.DIR_NAMES = dirNames;
|
global.BUNEXT_DIR_NAMES = dirNames;
|
||||||
global.REACT_IMPORTS_MAP = { imports: {} };
|
global.BUNEXT_REACT_IMPORTS_MAP = { imports: {} };
|
||||||
global.REACT_DOM_MODULE_CACHE = new Map();
|
global.BUNEXT_REACT_DOM_MODULE_CACHE = new Map();
|
||||||
log.banner();
|
global.BUNEXT_MAIN_CTX_BUILD_STARTS = 0;
|
||||||
await init();
|
await init();
|
||||||
global.CONSTANTS = grabConstants();
|
log.banner();
|
||||||
|
global.BUNEXT_CONSTANTS = grabConstants();
|
||||||
await reactModulesBundler();
|
await reactModulesBundler();
|
||||||
const router = new Bun.FileSystemRouter({
|
const router = new Bun.FileSystemRouter({
|
||||||
style: "nextjs",
|
style: "nextjs",
|
||||||
dir: PAGES_DIR,
|
dir: PAGES_DIR,
|
||||||
});
|
});
|
||||||
global.ROUTER = router;
|
global.BUNEXT_ROUTER = router;
|
||||||
const is_dev = isDevelopment();
|
const is_dev = isDevelopment();
|
||||||
if (is_dev) {
|
if (params?.build_only) {
|
||||||
|
log.build(`Building Modules ...`);
|
||||||
|
await allPagesESBuildContextBundler();
|
||||||
|
}
|
||||||
|
else if (is_dev) {
|
||||||
log.build(`Building Modules ...`);
|
log.build(`Building Modules ...`);
|
||||||
await allPagesESBuildContextBundler({
|
await allPagesESBuildContextBundler({
|
||||||
post_build_fn: () => {
|
post_build_fn: async () => {
|
||||||
serverPostBuildFn();
|
await serverPostBuildFn();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
watcherEsbuildCTX();
|
watcherEsbuildCTX();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.build(`Building Modules ...`);
|
log.build(`Building Modules ...`);
|
||||||
await allPagesESBuildContextBundler();
|
await allPagesESBuildContextBundler({ start: true });
|
||||||
cron();
|
cron();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -13,6 +13,10 @@ export default async function trimAllCache() {
|
|||||||
const trim_key = await trimCacheKey({
|
const trim_key = await trimCacheKey({
|
||||||
key: cache_key,
|
key: cache_key,
|
||||||
});
|
});
|
||||||
|
if (trim_key.success) {
|
||||||
|
cached_items.splice(i, 1);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ export default async function trimCacheKey({ key, }) {
|
|||||||
const { cache_name, cache_meta_name } = grabCacheNames({
|
const { cache_name, cache_meta_name } = grabCacheNames({
|
||||||
key,
|
key,
|
||||||
});
|
});
|
||||||
const config = global.CONFIG;
|
const config = global.BUNEXT_CONFIG;
|
||||||
const default_expiry_time_seconds = config.default_cache_expiry ||
|
const default_expiry_time_seconds = config.default_cache_expiry ||
|
||||||
AppData["DefaultCacheExpiryTimeSeconds"];
|
AppData["DefaultCacheExpiryTimeSeconds"];
|
||||||
const default_expiry_time_milliseconds = default_expiry_time_seconds * 1000;
|
const default_expiry_time_milliseconds = default_expiry_time_seconds * 1000;
|
||||||
|
|||||||
Vendored
+2
-2
@@ -24,7 +24,7 @@ export default async function () {
|
|||||||
try {
|
try {
|
||||||
const package_json = await Bun.file(path.resolve(__dirname, "../../package.json")).json();
|
const package_json = await Bun.file(path.resolve(__dirname, "../../package.json")).json();
|
||||||
const current_version = package_json.version;
|
const current_version = package_json.version;
|
||||||
global.CURRENT_VERSION = current_version;
|
global.BUNEXT_CURRENT_VERSION = current_version;
|
||||||
}
|
}
|
||||||
catch (error) { }
|
catch (error) { }
|
||||||
const keys = Object.keys(dirNames);
|
const keys = Object.keys(dirNames);
|
||||||
@@ -45,7 +45,7 @@ export default async function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const config = (await grabConfig()) || {};
|
const config = (await grabConfig()) || {};
|
||||||
global.CONFIG = {
|
global.BUNEXT_CONFIG = {
|
||||||
...config,
|
...config,
|
||||||
development: is_dev,
|
development: is_dev,
|
||||||
};
|
};
|
||||||
|
|||||||
+13
-2
@@ -8,6 +8,8 @@ import handleBunextPublicAssets from "./handle-bunext-public-assets";
|
|||||||
import checkExcludedPatterns from "../../utils/check-excluded-patterns";
|
import checkExcludedPatterns from "../../utils/check-excluded-patterns";
|
||||||
import { AppData } from "../../data/app-data";
|
import { AppData } from "../../data/app-data";
|
||||||
import fullRebuild from "./full-rebuild";
|
import fullRebuild from "./full-rebuild";
|
||||||
|
const HMR_RETRY_COOLDOWN_MS = 5000;
|
||||||
|
let lastHmrRetryTime = 0;
|
||||||
export default async function bunextRequestHandler({ req: initial_req, server, }) {
|
export default async function bunextRequestHandler({ req: initial_req, server, }) {
|
||||||
const is_dev = isDevelopment();
|
const is_dev = isDevelopment();
|
||||||
let req = initial_req.clone();
|
let req = initial_req.clone();
|
||||||
@@ -17,8 +19,8 @@ export default async function bunextRequestHandler({ req: initial_req, server, }
|
|||||||
return Response.json({ success: false, msg: `Invalid Path` });
|
return Response.json({ success: false, msg: `Invalid Path` });
|
||||||
}
|
}
|
||||||
let response = undefined;
|
let response = undefined;
|
||||||
if (global.CONSTANTS.config?.middleware) {
|
if (global.BUNEXT_CONSTANTS.config?.middleware) {
|
||||||
const middleware_res = await global.CONSTANTS.config.middleware({
|
const middleware_res = await global.BUNEXT_CONSTANTS.config.middleware({
|
||||||
req: initial_req,
|
req: initial_req,
|
||||||
url,
|
url,
|
||||||
});
|
});
|
||||||
@@ -30,6 +32,11 @@ export default async function bunextRequestHandler({ req: initial_req, server, }
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is_dev && url.pathname == AppData["BunextHMRRetryRoute"]) {
|
if (is_dev && url.pathname == AppData["BunextHMRRetryRoute"]) {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastHmrRetryTime < HMR_RETRY_COOLDOWN_MS) {
|
||||||
|
return new Response("Too Many Requests", { status: 429 });
|
||||||
|
}
|
||||||
|
lastHmrRetryTime = now;
|
||||||
await fullRebuild({ msg: `HMR Retry Rebuild ...` });
|
await fullRebuild({ msg: `HMR Retry Rebuild ...` });
|
||||||
return new Response("Modules Rebuilt");
|
return new Response("Modules Rebuilt");
|
||||||
}
|
}
|
||||||
@@ -60,8 +67,12 @@ export default async function bunextRequestHandler({ req: initial_req, server, }
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
|
if (is_dev) {
|
||||||
return new Response(`Server Error: ${error.message}`, {
|
return new Response(`Server Error: ${error.message}`, {
|
||||||
status: 500,
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
console.error(`Server Error: ${error.message}`, error);
|
||||||
|
return new Response("Internal Server Error", { status: 500 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export default function chokadirWatcherEsbuildCTX(): Promise<void>;
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import chokidar from "chokidar";
|
||||||
|
import path from "path";
|
||||||
|
import { existsSync } from "fs";
|
||||||
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
|
import fullRebuild from "./full-rebuild";
|
||||||
|
import { AppData } from "../../data/app-data";
|
||||||
|
import checkExcludedPatterns from "../../utils/check-excluded-patterns";
|
||||||
|
import pagesSSRBundler from "../bundler/pages-ssr-bundler";
|
||||||
|
import { log } from "../../utils/log";
|
||||||
|
const { ROOT_DIR, BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames();
|
||||||
|
export default async function chokadirWatcherEsbuildCTX() {
|
||||||
|
const watcher = chokidar.watch(ROOT_DIR, {
|
||||||
|
ignored: [
|
||||||
|
/(^|[\/\\])\../,
|
||||||
|
/node_modules/,
|
||||||
|
/public/,
|
||||||
|
/\.bunext/,
|
||||||
|
/\.git/,
|
||||||
|
/dist/,
|
||||||
|
/bun\.lockb/,
|
||||||
|
(path) => path.endsWith(AppData["BunextTmpFileExt"]),
|
||||||
|
],
|
||||||
|
persistent: true,
|
||||||
|
ignoreInitial: true,
|
||||||
|
depth: 99,
|
||||||
|
});
|
||||||
|
const handleEvent = async (event, filePath) => {
|
||||||
|
let owns_recompile = false;
|
||||||
|
try {
|
||||||
|
const filename = path.relative(ROOT_DIR, filePath);
|
||||||
|
if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) {
|
||||||
|
await fullRebuild();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (global.BUNEXT_BUNDLER_CTX_DISPOSED) {
|
||||||
|
await fullRebuild({ msg: `Restarting Bundler ...` });
|
||||||
|
}
|
||||||
|
if (global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED) {
|
||||||
|
await pagesSSRBundler().catch((error) => {
|
||||||
|
log.error(`SSR Bundler Error: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (filename.match(/\/styles$/) || filename === "styles") {
|
||||||
|
owns_recompile = true;
|
||||||
|
global.BUNEXT_RECOMPILING = true;
|
||||||
|
await Bun.sleep(1000);
|
||||||
|
await fullRebuild({
|
||||||
|
msg: `Detected new \`styles\` directory. Rebuilding ...`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (filename.match(/bunext.config\.ts/)) {
|
||||||
|
await fullRebuild({
|
||||||
|
msg: `bunext.config.ts file changed. Rebuilding server ...`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target_files_match = /\.(tsx?|jsx?|css)$/;
|
||||||
|
if (event === "change") {
|
||||||
|
if (filename.match(target_files_match)) {
|
||||||
|
if (global.BUNEXT_RECOMPILING)
|
||||||
|
return;
|
||||||
|
owns_recompile = true;
|
||||||
|
global.BUNEXT_RECOMPILING = true;
|
||||||
|
if (filename.match(/.*\.server\.tsx?/)) {
|
||||||
|
global.BUNEXT_IS_SERVER_COMPONENT = true;
|
||||||
|
}
|
||||||
|
if (global.BUNEXT_BUNDLER_CTX) {
|
||||||
|
await global.BUNEXT_BUNDLER_CTX.rebuild();
|
||||||
|
}
|
||||||
|
if (filename.match(/(404|500)\.tsx?/)) {
|
||||||
|
for (let i = global.BUNEXT_HMR_CONTROLLERS.length - 1; i >= 0; i--) {
|
||||||
|
const controller = global.BUNEXT_HMR_CONTROLLERS[i];
|
||||||
|
try {
|
||||||
|
controller?.controller?.enqueue(`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (["add", "unlink", "addDir", "unlinkDir"].includes(event)) {
|
||||||
|
const is_file_of_interest = !!filename.match(target_files_match) ||
|
||||||
|
event.includes("Dir");
|
||||||
|
if (!is_file_of_interest)
|
||||||
|
return;
|
||||||
|
if (!filename.match(/^src\/pages\/|\.css$/) ||
|
||||||
|
checkExcludedPatterns({ path: filename }) ||
|
||||||
|
filename.includes(" ")) {
|
||||||
|
return reloadWatcher();
|
||||||
|
}
|
||||||
|
if (global.BUNEXT_RECOMPILING)
|
||||||
|
return;
|
||||||
|
owns_recompile = true;
|
||||||
|
const action = event.startsWith("add") ? "created" : "deleted";
|
||||||
|
const type = filename.match(/\.css$/)
|
||||||
|
? "Stylesheet"
|
||||||
|
: event.includes("Dir")
|
||||||
|
? "Directory"
|
||||||
|
: filename.match(/\/pages\/api\//)
|
||||||
|
? "API Route"
|
||||||
|
: "Page";
|
||||||
|
await fullRebuild({
|
||||||
|
msg: `${type} ${action}: ${filename}. Rebuilding ...`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
log.error(`Watcher rebuild failed: ${error}`);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (owns_recompile) {
|
||||||
|
global.BUNEXT_RECOMPILING = false;
|
||||||
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
watcher
|
||||||
|
.on("add", (path) => handleEvent("add", path))
|
||||||
|
.on("change", (path) => handleEvent("change", path))
|
||||||
|
.on("unlink", (path) => handleEvent("unlink", path))
|
||||||
|
.on("addDir", (path) => handleEvent("addDir", path))
|
||||||
|
.on("unlinkDir", (path) => handleEvent("unlinkDir", path));
|
||||||
|
}
|
||||||
|
function reloadWatcher() {
|
||||||
|
if (global.BUNEXT_PAGES_SRC_WATCHER) {
|
||||||
|
global.BUNEXT_PAGES_SRC_WATCHER.close();
|
||||||
|
chokadirWatcherEsbuildCTX();
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-15
@@ -1,34 +1,37 @@
|
|||||||
import { log } from "../../utils/log";
|
import { log } from "../../utils/log";
|
||||||
import allPagesESBuildContextBundler from "../bundler/all-pages-esbuild-context-bundler";
|
import allPagesESBuildContextBundler from "../bundler/all-pages-esbuild-context-bundler";
|
||||||
import pagesSSRBundler from "../bundler/pages-ssr-bundler";
|
|
||||||
import serverPostBuildFn from "./server-post-build-fn";
|
import serverPostBuildFn from "./server-post-build-fn";
|
||||||
import watcherEsbuildCTX from "./watcher-esbuild-ctx";
|
import watcherEsbuildCTX from "./watcher-esbuild-ctx";
|
||||||
export default async function fullRebuild(params) {
|
export default async function fullRebuild(params) {
|
||||||
try {
|
try {
|
||||||
const { msg } = params || {};
|
const { msg } = params || {};
|
||||||
global.RECOMPILING = true;
|
global.BUNEXT_RECOMPILING = true;
|
||||||
if (msg) {
|
if (msg) {
|
||||||
log.watch(msg);
|
log.watch(msg);
|
||||||
}
|
}
|
||||||
global.ROUTER.reload();
|
global.BUNEXT_ROUTER.reload();
|
||||||
await global.BUNDLER_CTX?.dispose();
|
try {
|
||||||
global.BUNDLER_CTX = undefined;
|
await global.BUNEXT_BUNDLER_CTX?.dispose();
|
||||||
await global.SSR_BUNDLER_CTX?.dispose();
|
global.BUNEXT_BUNDLER_CTX = undefined;
|
||||||
global.SSR_BUNDLER_CTX = undefined;
|
await global.BUNEXT_SSR_BUNDLER_CTX?.dispose();
|
||||||
await pagesSSRBundler();
|
global.BUNEXT_SSR_BUNDLER_CTX = undefined;
|
||||||
allPagesESBuildContextBundler({
|
}
|
||||||
post_build_fn: () => {
|
catch (error) { }
|
||||||
serverPostBuildFn();
|
await allPagesESBuildContextBundler({
|
||||||
|
post_build_fn: async () => {
|
||||||
|
await serverPostBuildFn();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
global.RECOMPILING = false;
|
|
||||||
global.IS_SERVER_COMPONENT = false;
|
|
||||||
log.error(error);
|
log.error(error);
|
||||||
}
|
}
|
||||||
if (global.PAGES_SRC_WATCHER) {
|
finally {
|
||||||
global.PAGES_SRC_WATCHER.close();
|
global.BUNEXT_RECOMPILING = false;
|
||||||
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||||
|
}
|
||||||
|
if (global.BUNEXT_PAGES_SRC_WATCHER) {
|
||||||
|
global.BUNEXT_PAGES_SRC_WATCHER.close();
|
||||||
watcherEsbuildCTX();
|
watcherEsbuildCTX();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -2,13 +2,14 @@ import grabDirNames from "../../utils/grab-dir-names";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import isDevelopment from "../../utils/is-development";
|
import isDevelopment from "../../utils/is-development";
|
||||||
import { readFileResponse } from "./handle-public";
|
import { readFileResponse } from "./handle-public";
|
||||||
|
import isSafePath from "../../utils/is-safe-path";
|
||||||
const { BUNEXT_PUBLIC_DIR } = grabDirNames();
|
const { BUNEXT_PUBLIC_DIR } = grabDirNames();
|
||||||
export default async function ({ req }) {
|
export default async function ({ req }) {
|
||||||
try {
|
try {
|
||||||
const is_dev = isDevelopment();
|
const is_dev = isDevelopment();
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
const file_path = path.join(BUNEXT_PUBLIC_DIR, url.pathname.replace(/\/\.bunext\/public\//, ""));
|
const file_path = path.join(BUNEXT_PUBLIC_DIR, url.pathname.replace(/\/\.bunext\/public\//, ""));
|
||||||
if (!file_path.startsWith(BUNEXT_PUBLIC_DIR + path.sep)) {
|
if (!isSafePath({ filePath: file_path, allowedDir: BUNEXT_PUBLIC_DIR })) {
|
||||||
return new Response("Forbidden", { status: 403 });
|
return new Response("Forbidden", { status: 403 });
|
||||||
}
|
}
|
||||||
return readFileResponse({
|
return readFileResponse({
|
||||||
|
|||||||
+7
-2
@@ -2,13 +2,14 @@ import grabDirNames from "../../utils/grab-dir-names";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import isDevelopment from "../../utils/is-development";
|
import isDevelopment from "../../utils/is-development";
|
||||||
import { existsSync } from "fs";
|
import { existsSync } from "fs";
|
||||||
|
import isSafePath from "../../utils/is-safe-path";
|
||||||
const { PUBLIC_DIR } = grabDirNames();
|
const { PUBLIC_DIR } = grabDirNames();
|
||||||
export default async function ({ req }) {
|
export default async function ({ req }) {
|
||||||
try {
|
try {
|
||||||
const is_dev = isDevelopment();
|
const is_dev = isDevelopment();
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
const file_path = path.join(PUBLIC_DIR, url.pathname);
|
const file_path = path.join(PUBLIC_DIR, url.pathname);
|
||||||
if (!file_path.startsWith(PUBLIC_DIR + path.sep)) {
|
if (!isSafePath({ filePath: file_path, allowedDir: PUBLIC_DIR })) {
|
||||||
return new Response("Forbidden", { status: 403 });
|
return new Response("Forbidden", { status: 403 });
|
||||||
}
|
}
|
||||||
if (!existsSync(file_path)) {
|
if (!existsSync(file_path)) {
|
||||||
@@ -17,7 +18,11 @@ export default async function ({ req }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const file = Bun.file(file_path);
|
const file = Bun.file(file_path);
|
||||||
return new Response(file);
|
const headers = new Headers();
|
||||||
|
if (!is_dev) {
|
||||||
|
headers.set("Cache-Control", "public, max-age=3600");
|
||||||
|
}
|
||||||
|
return new Response(file, { headers });
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
return new Response(`File Not Found`, {
|
return new Response(`File Not Found`, {
|
||||||
|
|||||||
Vendored
+24
-9
@@ -1,18 +1,36 @@
|
|||||||
|
function removeController(controller) {
|
||||||
|
const idx = global.BUNEXT_HMR_CONTROLLERS.findIndex((c) => c.controller == controller);
|
||||||
|
if (typeof idx == "number" && idx >= 0) {
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS.splice(idx, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
export default async function ({ req }) {
|
export default async function ({ req }) {
|
||||||
const referer_url = new URL(req.headers.get("referer") || "");
|
const referer = req.headers.get("referer");
|
||||||
const match = global.ROUTER.match(referer_url.pathname);
|
const page_cookie = req.headers.get("cookie");
|
||||||
|
if (!referer) {
|
||||||
|
return new Response("Missing Referer Header", { status: 400 });
|
||||||
|
}
|
||||||
|
let referer_url;
|
||||||
|
try {
|
||||||
|
referer_url = new URL(referer);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return new Response("Invalid Referer Header", { status: 400 });
|
||||||
|
}
|
||||||
|
const match = global.BUNEXT_ROUTER.match(referer_url.pathname);
|
||||||
const target_map = match?.filePath
|
const target_map = match?.filePath
|
||||||
? global.BUNDLER_CTX_MAP?.[match.filePath]
|
? global.BUNEXT_BUNDLER_CTX_MAP?.[match.filePath]
|
||||||
: undefined;
|
: undefined;
|
||||||
let controller;
|
let controller;
|
||||||
let heartbeat;
|
let heartbeat;
|
||||||
const stream = new ReadableStream({
|
const stream = new ReadableStream({
|
||||||
start(c) {
|
start(c) {
|
||||||
controller = c;
|
controller = c;
|
||||||
global.HMR_CONTROLLERS.push({
|
global.BUNEXT_HMR_CONTROLLERS.push({
|
||||||
controller: c,
|
controller: c,
|
||||||
page_url: referer_url.href,
|
page_url: referer_url.href,
|
||||||
target_map,
|
target_map,
|
||||||
|
page_cookie,
|
||||||
});
|
});
|
||||||
heartbeat = setInterval(() => {
|
heartbeat = setInterval(() => {
|
||||||
try {
|
try {
|
||||||
@@ -20,16 +38,13 @@ export default async function ({ req }) {
|
|||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
clearInterval(heartbeat);
|
clearInterval(heartbeat);
|
||||||
|
removeController(controller);
|
||||||
}
|
}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
},
|
},
|
||||||
cancel() {
|
cancel() {
|
||||||
clearInterval(heartbeat);
|
clearInterval(heartbeat);
|
||||||
const targetControllerIndex = global.HMR_CONTROLLERS.findIndex((c) => c.controller == controller);
|
removeController(controller);
|
||||||
if (typeof targetControllerIndex == "number" &&
|
|
||||||
targetControllerIndex >= 0) {
|
|
||||||
global.HMR_CONTROLLERS.splice(targetControllerIndex, 1);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return new Response(stream, {
|
return new Response(stream, {
|
||||||
|
|||||||
+5
-1
@@ -2,13 +2,14 @@ import grabDirNames from "../../utils/grab-dir-names";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import isDevelopment from "../../utils/is-development";
|
import isDevelopment from "../../utils/is-development";
|
||||||
import { existsSync } from "fs";
|
import { existsSync } from "fs";
|
||||||
|
import isSafePath from "../../utils/is-safe-path";
|
||||||
const { PUBLIC_DIR } = grabDirNames();
|
const { PUBLIC_DIR } = grabDirNames();
|
||||||
export default async function ({ req }) {
|
export default async function ({ req }) {
|
||||||
try {
|
try {
|
||||||
const is_dev = isDevelopment();
|
const is_dev = isDevelopment();
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
const file_path = path.join(PUBLIC_DIR, url.pathname.replace(/^\/public/, ""));
|
const file_path = path.join(PUBLIC_DIR, url.pathname.replace(/^\/public/, ""));
|
||||||
if (!file_path.startsWith(PUBLIC_DIR + path.sep)) {
|
if (!isSafePath({ filePath: file_path, allowedDir: PUBLIC_DIR })) {
|
||||||
return new Response("Forbidden", { status: 403 });
|
return new Response("Forbidden", { status: 403 });
|
||||||
}
|
}
|
||||||
return readFileResponse({ file_path });
|
return readFileResponse({ file_path });
|
||||||
@@ -33,6 +34,9 @@ export function readFileResponse({ file_path, cache }) {
|
|||||||
else if (cache?.duration) {
|
else if (cache?.duration) {
|
||||||
headers.set("Cache-Control", `public, max-age=${cache.duration}`);
|
headers.set("Cache-Control", `public, max-age=${cache.duration}`);
|
||||||
}
|
}
|
||||||
|
else if (!isDevelopment()) {
|
||||||
|
headers.set("Cache-Control", "public, max-age=3600");
|
||||||
|
}
|
||||||
return new Response(file, {
|
return new Response(file, {
|
||||||
headers,
|
headers,
|
||||||
});
|
});
|
||||||
|
|||||||
+21
-5
@@ -30,8 +30,8 @@ export default async function ({ req }) {
|
|||||||
});
|
});
|
||||||
let module;
|
let module;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (is_dev && global.SSR_BUNDLER_CTX_MAP?.[match.filePath]?.path) {
|
if (is_dev && global.BUNEXT_SSR_BUNDLER_CTX_MAP?.[match.filePath]?.path) {
|
||||||
const target_import = path.join(ROOT_DIR, global.SSR_BUNDLER_CTX_MAP[match.filePath].path);
|
const target_import = path.join(ROOT_DIR, global.BUNEXT_SSR_BUNDLER_CTX_MAP[match.filePath].path);
|
||||||
module = await import(`${target_import}?t=${now}`);
|
module = await import(`${target_import}?t=${now}`);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -41,12 +41,13 @@ export default async function ({ req }) {
|
|||||||
module = await import(import_path);
|
module = await import(import_path);
|
||||||
}
|
}
|
||||||
const config = module.config;
|
const config = module.config;
|
||||||
|
const maxBodyBytes = config?.max_request_body_mb
|
||||||
|
? config.max_request_body_mb * MBInBytes
|
||||||
|
: ServerDefaultRequestBodyLimitBytes;
|
||||||
const contentLength = req.headers.get("content-length");
|
const contentLength = req.headers.get("content-length");
|
||||||
if (contentLength) {
|
if (contentLength) {
|
||||||
const size = parseInt(contentLength, 10);
|
const size = parseInt(contentLength, 10);
|
||||||
if ((config?.max_request_body_mb &&
|
if (size > maxBodyBytes) {
|
||||||
size > config.max_request_body_mb * MBInBytes) ||
|
|
||||||
size > ServerDefaultRequestBodyLimitBytes) {
|
|
||||||
return Response.json({
|
return Response.json({
|
||||||
success: false,
|
success: false,
|
||||||
msg: "Request Body Too Large!",
|
msg: "Request Body Too Large!",
|
||||||
@@ -58,6 +59,21 @@ export default async function ({ req }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (req.method !== "GET" && req.method !== "HEAD") {
|
||||||
|
const body = await req.arrayBuffer();
|
||||||
|
if (body.byteLength > maxBodyBytes) {
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
msg: "Request Body Too Large!",
|
||||||
|
}, {
|
||||||
|
status: 413,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
routeParams.body = JSON.parse(new TextDecoder().decode(body) || "{}");
|
||||||
|
}
|
||||||
const target_module = (module["default"] ||
|
const target_module = (module["default"] ||
|
||||||
module["handler"]);
|
module["handler"]);
|
||||||
const res = await target_module?.({
|
const res = await target_module?.({
|
||||||
|
|||||||
+28
-25
@@ -1,49 +1,53 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import grabPageComponent from "./web-pages/grab-page-component";
|
import grabPageComponent from "./web-pages/grab-page-component";
|
||||||
export default async function serverPostBuildFn(params) {
|
export default async function serverPostBuildFn(params) {
|
||||||
if (!global.HMR_CONTROLLERS?.[0] || !global.BUNDLER_CTX_MAP) {
|
if (!global.BUNEXT_HMR_CONTROLLERS?.[0] || !global.BUNEXT_BUNDLER_CTX_MAP) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const reload_payload = { reload: true };
|
const reload_payload = { reload: true };
|
||||||
const reload_enqueue = `event: update\ndata: ${JSON.stringify(reload_payload)}\n\n`;
|
const reload_enqueue = `event: update\ndata: ${JSON.stringify(reload_payload)}\n\n`;
|
||||||
for (let i = global.HMR_CONTROLLERS.length - 1; i >= 0; i--) {
|
for (let i = global.BUNEXT_HMR_CONTROLLERS.length - 1; i >= 0; i--) {
|
||||||
const controller = global.HMR_CONTROLLERS[i];
|
const controller = global.BUNEXT_HMR_CONTROLLERS[i];
|
||||||
if (!controller) {
|
if (!controller) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!controller.target_map?.local_path) {
|
if (!controller.target_map?.local_path) {
|
||||||
// if (global.IS_404_PAGE) {
|
|
||||||
// controller.controller.enqueue(reload_enqueue);
|
|
||||||
// }
|
|
||||||
// if (!global.HMR_CONTROLLERS[i].page_reloaded) {
|
|
||||||
// controller.controller.enqueue(reload_enqueue);
|
|
||||||
// global.HMR_CONTROLLERS[i].page_reloaded = true;
|
|
||||||
// }
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (params?.reload_all_controllers) {
|
if (params?.reload_all_controllers) {
|
||||||
|
try {
|
||||||
controller.controller.enqueue(reload_enqueue);
|
controller.controller.enqueue(reload_enqueue);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS.splice(i, 1);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const target_artifact = global.BUNDLER_CTX_MAP[controller.target_map.local_path];
|
const target_artifact = global.BUNEXT_BUNDLER_CTX_MAP[controller.target_map.local_path];
|
||||||
if (!target_artifact.local_path) {
|
if (!target_artifact?.local_path) {
|
||||||
|
try {
|
||||||
controller.controller.enqueue(reload_enqueue);
|
controller.controller.enqueue(reload_enqueue);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS.splice(i, 1);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const mock_req = target_artifact.req
|
const mock_req = target_artifact.req_url
|
||||||
? target_artifact.req.clone()
|
? new Request(target_artifact.req_url, {})
|
||||||
: new Request(controller.page_url);
|
: new Request(controller.page_url);
|
||||||
const page_component = global.IS_SERVER_COMPONENT
|
if (controller.page_cookie) {
|
||||||
? await grabPageComponent({
|
mock_req.headers.set("cookie", controller.page_cookie);
|
||||||
|
}
|
||||||
|
const page_component = await grabPageComponent({
|
||||||
req: mock_req,
|
req: mock_req,
|
||||||
return_server_res_only: true,
|
return_server_res_only: true,
|
||||||
is_hydration: true,
|
is_hydration: true,
|
||||||
})
|
});
|
||||||
: {};
|
|
||||||
if (page_component instanceof Response) {
|
if (page_component instanceof Response) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const { serverRes } = page_component;
|
const { serverRes } = page_component || {};
|
||||||
const final_artifact = {
|
const final_artifact = {
|
||||||
..._.omit(controller, ["controller"]),
|
..._.omit(controller, ["controller"]),
|
||||||
target_map: target_artifact,
|
target_map: target_artifact,
|
||||||
@@ -51,22 +55,21 @@ export default async function serverPostBuildFn(params) {
|
|||||||
if (!target_artifact) {
|
if (!target_artifact) {
|
||||||
delete final_artifact.target_map;
|
delete final_artifact.target_map;
|
||||||
}
|
}
|
||||||
if (serverRes) {
|
// Always replace so prior error props cannot linger
|
||||||
final_artifact.page_props = serverRes;
|
final_artifact.page_props = serverRes || {};
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
let final_data = {};
|
let final_data = {};
|
||||||
if (global.ROOT_FILE_UPDATED) {
|
if (global.BUNEXT_ROOT_FILE_UPDATED) {
|
||||||
final_data = reload_payload;
|
final_data = reload_payload;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final_data = final_artifact;
|
final_data = final_artifact;
|
||||||
}
|
}
|
||||||
controller.controller.enqueue(`event: update\ndata: ${JSON.stringify(final_data)}\n\n`);
|
controller.controller.enqueue(`event: update\ndata: ${JSON.stringify(final_data)}\n\n`);
|
||||||
global.ROOT_FILE_UPDATED = false;
|
global.BUNEXT_ROOT_FILE_UPDATED = false;
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
global.HMR_CONTROLLERS.splice(i, 1);
|
global.BUNEXT_HMR_CONTROLLERS.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-1
@@ -1,10 +1,21 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { log } from "../../utils/log";
|
import { log } from "../../utils/log";
|
||||||
import serverParamsGen from "./server-params-gen";
|
import serverParamsGen from "./server-params-gen";
|
||||||
|
import isDevelopment from "../../utils/is-development";
|
||||||
|
import watcherEsbuildCTX from "./watcher-esbuild-ctx";
|
||||||
export default async function startServer() {
|
export default async function startServer() {
|
||||||
const serverParams = await serverParamsGen();
|
const serverParams = await serverParamsGen();
|
||||||
const server = Bun.serve(serverParams);
|
const server = Bun.serve(serverParams);
|
||||||
global.SERVER = server;
|
const is_dev = isDevelopment();
|
||||||
|
global.BUNEXT_SERVER = server;
|
||||||
log.server(`http://${server.hostname}:${server.port}`);
|
log.server(`http://${server.hostname}:${server.port}`);
|
||||||
|
if (is_dev) {
|
||||||
|
setInterval(() => {
|
||||||
|
if (global.BUNEXT_PAGES_SRC_WATCHER) {
|
||||||
|
global.BUNEXT_PAGES_SRC_WATCHER.close();
|
||||||
|
watcherEsbuildCTX();
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-24
@@ -1,18 +1,39 @@
|
|||||||
import { watch, existsSync, statSync } from "fs";
|
import { watch, existsSync, statSync, glob } from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import grabDirNames from "../../utils/grab-dir-names";
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
import fullRebuild from "./full-rebuild";
|
import fullRebuild from "./full-rebuild";
|
||||||
import { AppData } from "../../data/app-data";
|
import { AppData } from "../../data/app-data";
|
||||||
import checkExcludedPatterns from "../../utils/check-excluded-patterns";
|
import checkExcludedPatterns from "../../utils/check-excluded-patterns";
|
||||||
import pagesSSRBundler from "../bundler/pages-ssr-bundler";
|
import pagesSSRBundler from "../bundler/pages-ssr-bundler";
|
||||||
|
import { log } from "../../utils/log";
|
||||||
const { ROOT_DIR, BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames();
|
const { ROOT_DIR, BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames();
|
||||||
export default async function watcherEsbuildCTX() {
|
export default async function watcherEsbuildCTX() {
|
||||||
const pages_src_watcher = watch(ROOT_DIR, {
|
const pages_src_watcher = watch(ROOT_DIR, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
persistent: true,
|
persistent: true,
|
||||||
}, async (event, filename) => {
|
}, async (event, filename) => {
|
||||||
|
let owns_recompile = false;
|
||||||
|
try {
|
||||||
if (!filename)
|
if (!filename)
|
||||||
return;
|
return;
|
||||||
|
const full_file_path = path.join(ROOT_DIR, filename);
|
||||||
|
if (global.BUNEXT_CONFIG.exclude_watch_patterns) {
|
||||||
|
for (let i = 0; i < global.BUNEXT_CONFIG.exclude_watch_patterns.length; i++) {
|
||||||
|
const watch_pattern = global.BUNEXT_CONFIG.exclude_watch_patterns[i];
|
||||||
|
if (watch_pattern instanceof RegExp) {
|
||||||
|
const is_path_excluded = watch_pattern.test(filename);
|
||||||
|
if (is_path_excluded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const excluded_path = path.resolve(ROOT_DIR, watch_pattern);
|
||||||
|
if (excluded_path == full_file_path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) {
|
if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) {
|
||||||
await fullRebuild();
|
await fullRebuild();
|
||||||
return;
|
return;
|
||||||
@@ -20,23 +41,25 @@ export default async function watcherEsbuildCTX() {
|
|||||||
if (filename.match(/^\.\w+/)) {
|
if (filename.match(/^\.\w+/)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (global.BUNDLER_CTX_DISPOSED) {
|
if (global.BUNEXT_BUNDLER_CTX_DISPOSED) {
|
||||||
await fullRebuild({ msg: `Restarting Bundler ...` });
|
await fullRebuild({ msg: `Restarting Bundler ...` });
|
||||||
global.BUNDLER_CTX_DISPOSED = false;
|
return;
|
||||||
}
|
}
|
||||||
if (global.SSR_BUNDLER_CTX_DISPOSED) {
|
if (global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED) {
|
||||||
pagesSSRBundler();
|
await pagesSSRBundler().catch((error) => {
|
||||||
|
log.error(`SSR Bundler Error: ${error}`);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (filename.endsWith(AppData["BunextTmpFileExt"])) {
|
if (filename.endsWith(AppData["BunextTmpFileExt"])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const full_file_path = path.join(ROOT_DIR, filename);
|
|
||||||
const does_file_exist = existsSync(full_file_path);
|
const does_file_exist = existsSync(full_file_path);
|
||||||
const file_stat = does_file_exist
|
const file_stat = does_file_exist
|
||||||
? statSync(full_file_path)
|
? statSync(full_file_path)
|
||||||
: undefined;
|
: undefined;
|
||||||
if (full_file_path.match(/\/styles$/)) {
|
if (full_file_path.match(/\/styles$/)) {
|
||||||
global.RECOMPILING = true;
|
owns_recompile = true;
|
||||||
|
global.BUNEXT_RECOMPILING = true;
|
||||||
await Bun.sleep(1000);
|
await Bun.sleep(1000);
|
||||||
await fullRebuild({
|
await fullRebuild({
|
||||||
msg: `Detected new \`styles\` directory. Rebuilding ...`,
|
msg: `Detected new \`styles\` directory. Rebuilding ...`,
|
||||||
@@ -53,28 +76,28 @@ export default async function watcherEsbuildCTX() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const target_files_match = /\.(tsx?|jsx?|css)$/;
|
const target_files_match = /\.(tsx?|jsx?|css)$/;
|
||||||
// const rebuild_skip_paths = /\/pages\/api\//;
|
|
||||||
if (event !== "rename") {
|
if (event !== "rename") {
|
||||||
if (filename.match(target_files_match)) {
|
if (filename.match(target_files_match)) {
|
||||||
if (global.RECOMPILING)
|
if (global.BUNEXT_RECOMPILING)
|
||||||
return;
|
return;
|
||||||
global.RECOMPILING = true;
|
owns_recompile = true;
|
||||||
|
global.BUNEXT_RECOMPILING = true;
|
||||||
if (filename.match(/.*\.server\.tsx?/)) {
|
if (filename.match(/.*\.server\.tsx?/)) {
|
||||||
global.IS_SERVER_COMPONENT = true;
|
global.BUNEXT_IS_SERVER_COMPONENT = true;
|
||||||
}
|
|
||||||
if (global.BUNDLER_CTX) {
|
|
||||||
try {
|
|
||||||
await global.BUNDLER_CTX.rebuild();
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log(`ESBUILD Rebuild Error =>`, error);
|
|
||||||
}
|
}
|
||||||
|
if (global.BUNEXT_BUNDLER_CTX) {
|
||||||
|
await global.BUNEXT_BUNDLER_CTX.rebuild();
|
||||||
}
|
}
|
||||||
if (filename.match(/(404|500)\.tsx?/)) {
|
if (filename.match(/(404|500)\.tsx?/)) {
|
||||||
for (let i = global.HMR_CONTROLLERS.length - 1; i >= 0; i--) {
|
for (let i = global.BUNEXT_HMR_CONTROLLERS.length - 1; i >= 0; i--) {
|
||||||
const controller = global.HMR_CONTROLLERS[i];
|
const controller = global.BUNEXT_HMR_CONTROLLERS[i];
|
||||||
|
try {
|
||||||
controller?.controller?.enqueue(`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`);
|
controller?.controller?.enqueue(`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`);
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -90,8 +113,9 @@ export default async function watcherEsbuildCTX() {
|
|||||||
return reloadWatcher();
|
return reloadWatcher();
|
||||||
if (filename.match(/ /))
|
if (filename.match(/ /))
|
||||||
return reloadWatcher();
|
return reloadWatcher();
|
||||||
if (global.RECOMPILING)
|
if (global.BUNEXT_RECOMPILING)
|
||||||
return;
|
return;
|
||||||
|
owns_recompile = true;
|
||||||
const action = does_file_exist ? "created" : "deleted";
|
const action = does_file_exist ? "created" : "deleted";
|
||||||
const type = filename.match(/\.css$/)
|
const type = filename.match(/\.css$/)
|
||||||
? "Sylesheet"
|
? "Sylesheet"
|
||||||
@@ -103,12 +127,22 @@ export default async function watcherEsbuildCTX() {
|
|||||||
await fullRebuild({
|
await fullRebuild({
|
||||||
msg: `${type} ${action}: ${filename}. Rebuilding ...`,
|
msg: `${type} ${action}: ${filename}. Rebuilding ...`,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
log.error(`Watcher rebuild failed: ${error}`);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (owns_recompile) {
|
||||||
|
global.BUNEXT_RECOMPILING = false;
|
||||||
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
global.PAGES_SRC_WATCHER = pages_src_watcher;
|
global.BUNEXT_PAGES_SRC_WATCHER = pages_src_watcher;
|
||||||
}
|
}
|
||||||
function reloadWatcher() {
|
function reloadWatcher() {
|
||||||
if (global.PAGES_SRC_WATCHER) {
|
if (global.BUNEXT_PAGES_SRC_WATCHER) {
|
||||||
global.PAGES_SRC_WATCHER.close();
|
global.BUNEXT_PAGES_SRC_WATCHER.close();
|
||||||
watcherEsbuildCTX();
|
watcherEsbuildCTX();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-6
@@ -43,16 +43,18 @@ export default async function genWebHTML({ component: Main, pageProps, bundledMa
|
|||||||
const RootHead = root_module?.Head;
|
const RootHead = root_module?.Head;
|
||||||
const dev = isDevelopment();
|
const dev = isDevelopment();
|
||||||
const final_meta = _.merge(root_meta, page_meta);
|
const final_meta = _.merge(root_meta, page_meta);
|
||||||
// const public_envs = Object.keys(process.env).filter((e) =>
|
const public_envs = Object.fromEntries(Object.entries(process.env).filter(([k]) => k.startsWith("BUNEXT_PUBLIC_")));
|
||||||
// e.startsWith(`NEXT_PUBLIC_`),
|
|
||||||
// );
|
|
||||||
const client_process = {
|
const client_process = {
|
||||||
env: {},
|
env: {
|
||||||
|
NODE_ENV: dev ? "development" : "production",
|
||||||
|
...public_envs,
|
||||||
|
...global.BUNEXT_CONFIG.public_envs,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
let final_component = (_jsxs("html", { ...html_props, children: [_jsxs("head", { children: [_jsx("meta", { charSet: "utf-8", "data-bunext-head": true }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0", "data-bunext-head": true }), final_meta ? grabWebMetaHTML({ meta: final_meta }) : null, bundledMap?.css_path ? (_jsx("link", { rel: "stylesheet", href: `/${bundledMap.css_path}`, "data-bunext-head": true })) : null, _jsx("script", { dangerouslySetInnerHTML: {
|
let final_component = (_jsxs("html", { ...html_props, children: [_jsxs("head", { children: [_jsx("meta", { charSet: "utf-8", "data-bunext-head": true }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0", "data-bunext-head": true }), final_meta ? grabWebMetaHTML({ meta: final_meta }) : null, bundledMap?.css_path ? (_jsx("link", { rel: "stylesheet", href: `/${bundledMap.css_path}`, "data-bunext-head": true })) : null, _jsx("script", { dangerouslySetInnerHTML: {
|
||||||
__html: `window.${ClientWindowPagePropsName} = ${serializedProps};\nwindow.process = ${JSON.stringify(client_process)}`,
|
__html: `window.${ClientWindowPagePropsName} = ${serializedProps};\nwindow.process = ${JSON.stringify(client_process)}`,
|
||||||
}, "data-bunext-head": true }), RootHead ? (_jsx(RootHead, { serverRes: pageProps, ctx: routeParams })) : null, Head ? _jsx(Head, { serverRes: pageProps, ctx: routeParams }) : null, bundledMap?.path ? (_jsxs(_Fragment, { children: [_jsx("script", { type: "importmap", dangerouslySetInnerHTML: {
|
}, "data-bunext-head": true }), RootHead ? (_jsx(RootHead, { serverRes: pageProps, ctx: routeParams })) : null, Head ? _jsx(Head, { serverRes: pageProps, ctx: routeParams }) : null, bundledMap?.path ? (_jsxs(_Fragment, { children: [_jsx("script", { type: "importmap", dangerouslySetInnerHTML: {
|
||||||
__html: JSON.stringify(global.REACT_IMPORTS_MAP),
|
__html: JSON.stringify(global.BUNEXT_REACT_IMPORTS_MAP),
|
||||||
}, defer: true, "data-bunext-head": true }), _jsx("script", { src: `/${bundledMap.path}`, type: "module", id: AppData["BunextClientHydrationScriptID"], defer: true, "data-bunext-head": true })] })) : null, is_dev ? (_jsx("script", { defer: true, dangerouslySetInnerHTML: {
|
}, defer: true, "data-bunext-head": true }), _jsx("script", { src: `/${bundledMap.path}`, type: "module", id: AppData["BunextClientHydrationScriptID"], defer: true, "data-bunext-head": true })] })) : null, is_dev ? (_jsx("script", { defer: true, dangerouslySetInnerHTML: {
|
||||||
__html: page_hydration_script,
|
__html: page_hydration_script,
|
||||||
}, "data-bunext-head": true })) : null] }), _jsx("body", { children: _jsx("div", { id: ClientRootElementIDName, suppressHydrationWarning: !dev, children: _jsx(Main, { ...pageProps }) }) })] }));
|
}, "data-bunext-head": true })) : null] }), _jsx("body", { children: _jsx("div", { id: ClientRootElementIDName, suppressHydrationWarning: !dev, children: _jsx(Main, { ...pageProps }) }) })] }));
|
||||||
@@ -76,6 +78,8 @@ export default async function genWebHTML({ component: Main, pageProps, bundledMa
|
|||||||
console.error = () => { };
|
console.error = () => { };
|
||||||
console.info = () => { };
|
console.info = () => { };
|
||||||
console.debug = () => { };
|
console.debug = () => { };
|
||||||
|
let htmlBody;
|
||||||
|
try {
|
||||||
const stream = await renderToReadableStream(final_component, {
|
const stream = await renderToReadableStream(final_component, {
|
||||||
onError(error) {
|
onError(error) {
|
||||||
if (error.message.includes('unique "key" prop'))
|
if (error.message.includes('unique "key" prop'))
|
||||||
@@ -83,8 +87,11 @@ export default async function genWebHTML({ component: Main, pageProps, bundledMa
|
|||||||
originalConsole.error(error);
|
originalConsole.error(error);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const htmlBody = await new Response(stream).text();
|
htmlBody = await new Response(stream).text();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
Object.assign(console, originalConsole);
|
Object.assign(console, originalConsole);
|
||||||
|
}
|
||||||
html += htmlBody;
|
html += htmlBody;
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default async function grabFilePathModule({ file_path, out_file, }) {
|
|||||||
jsx: "automatic",
|
jsx: "automatic",
|
||||||
outfile: target_cache_file_path,
|
outfile: target_cache_file_path,
|
||||||
});
|
});
|
||||||
Loader.registry.delete(target_cache_file_path);
|
// Loader.registry.delete(target_cache_file_path);
|
||||||
const module = await import(`${target_cache_file_path}?t=${Date.now()}`);
|
const module = await import(`${target_cache_file_path}?t=${Date.now()}`);
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import grabDirNames from "../../../utils/grab-dir-names";
|
|||||||
const { ROOT_DIR } = grabDirNames();
|
const { ROOT_DIR } = grabDirNames();
|
||||||
export default async function grabPageBundledReactComponent({ file_path, return_tsx_only, }) {
|
export default async function grabPageBundledReactComponent({ file_path, return_tsx_only, }) {
|
||||||
try {
|
try {
|
||||||
if (global.SSR_BUNDLER_CTX_MAP?.[file_path]) {
|
if (global.BUNEXT_SSR_BUNDLER_CTX_MAP?.[file_path]) {
|
||||||
const mod = await import(path.join(ROOT_DIR, global.SSR_BUNDLER_CTX_MAP[file_path].path));
|
const abs = path.join(ROOT_DIR, global.BUNEXT_SSR_BUNDLER_CTX_MAP[file_path].path);
|
||||||
|
// Loader.registry.delete(abs);
|
||||||
|
const mod = await import(`${abs}?t=${Date.now()}`);
|
||||||
const Main = mod.default;
|
const Main = mod.default;
|
||||||
return { component: Main };
|
return { component: Main };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,13 @@ export default async function grabPageCombinedServerRes({ file_path, debug, url,
|
|||||||
const { server_file_path: root_server_file_path } = root_file_path
|
const { server_file_path: root_server_file_path } = root_file_path
|
||||||
? grabPageServerPath({ file_path: root_file_path })
|
? grabPageServerPath({ file_path: root_file_path })
|
||||||
: {};
|
: {};
|
||||||
const root_server_ctx_map = global.SSR_BUNDLER_CTX_MAP[root_server_file_path || ""];
|
const root_server_ctx_map = global.BUNEXT_SSR_BUNDLER_CTX_MAP[root_server_file_path || ""];
|
||||||
const final_root_server_path = root_server_ctx_map?.local_path
|
const final_root_server_path = root_server_ctx_map?.local_path
|
||||||
? path.join(ROOT_DIR, root_server_ctx_map.path)
|
? path.join(ROOT_DIR, root_server_ctx_map.path)
|
||||||
: root_server_file_path;
|
: root_server_file_path;
|
||||||
|
if (final_root_server_path) {
|
||||||
|
// Loader.registry.delete(final_root_server_path);
|
||||||
|
}
|
||||||
const root_server_module = final_root_server_path
|
const root_server_module = final_root_server_path
|
||||||
? await import(`${final_root_server_path}?t=${now}`)
|
? await import(`${final_root_server_path}?t=${now}`)
|
||||||
: undefined;
|
: undefined;
|
||||||
@@ -30,10 +33,13 @@ export default async function grabPageCombinedServerRes({ file_path, debug, url,
|
|||||||
log.info(`rootServerRes:`, rootServerRes);
|
log.info(`rootServerRes:`, rootServerRes);
|
||||||
}
|
}
|
||||||
const { server_file_path } = grabPageServerPath({ file_path });
|
const { server_file_path } = grabPageServerPath({ file_path });
|
||||||
const page_server_ctx = global.SSR_BUNDLER_CTX_MAP[server_file_path || ""];
|
const page_server_ctx = global.BUNEXT_SSR_BUNDLER_CTX_MAP[server_file_path || ""];
|
||||||
const final_page_server_path = page_server_ctx?.local_path
|
const final_page_server_path = page_server_ctx?.local_path
|
||||||
? path.join(ROOT_DIR, page_server_ctx.path)
|
? path.join(ROOT_DIR, page_server_ctx.path)
|
||||||
: root_server_file_path;
|
: server_file_path;
|
||||||
|
if (final_page_server_path) {
|
||||||
|
// Loader.registry.delete(final_page_server_path);
|
||||||
|
}
|
||||||
const server_module = final_page_server_path
|
const server_module = final_page_server_path
|
||||||
? await import(`${final_page_server_path}?t=${now}`)
|
? await import(`${final_page_server_path}?t=${now}`)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|||||||
+15
-9
@@ -9,6 +9,7 @@ import serverPostBuildFn from "../server-post-build-fn";
|
|||||||
import isDevelopment from "../../../utils/is-development";
|
import isDevelopment from "../../../utils/is-development";
|
||||||
import { existsSync } from "fs";
|
import { existsSync } from "fs";
|
||||||
import grabDirNames from "../../../utils/grab-dir-names";
|
import grabDirNames from "../../../utils/grab-dir-names";
|
||||||
|
import watcherEsbuildCTX from "../watcher-esbuild-ctx";
|
||||||
const { BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames();
|
const { BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames();
|
||||||
class NotFoundError extends Error {
|
class NotFoundError extends Error {
|
||||||
status = 404;
|
status = 404;
|
||||||
@@ -20,7 +21,7 @@ class NotFoundError extends Error {
|
|||||||
export default async function grabPageComponent(params) {
|
export default async function grabPageComponent(params) {
|
||||||
const { req, file_path: passed_file_path, debug, return_server_res_only, skip_server_res, is_hydration, } = params;
|
const { req, file_path: passed_file_path, debug, return_server_res_only, skip_server_res, is_hydration, } = params;
|
||||||
const url = req?.url ? new URL(req.url) : undefined;
|
const url = req?.url ? new URL(req.url) : undefined;
|
||||||
const router = global.ROUTER;
|
const router = global.BUNEXT_ROUTER;
|
||||||
const is_dev = isDevelopment();
|
const is_dev = isDevelopment();
|
||||||
const forwarded_proto = req?.headers.get("x-forwarded-proto");
|
const forwarded_proto = req?.headers.get("x-forwarded-proto");
|
||||||
if (url && forwarded_proto) {
|
if (url && forwarded_proto) {
|
||||||
@@ -50,7 +51,7 @@ export default async function grabPageComponent(params) {
|
|||||||
// log.error(errMsg);
|
// log.error(errMsg);
|
||||||
throw new Error(errMsg);
|
throw new Error(errMsg);
|
||||||
}
|
}
|
||||||
let bundledMap = global.BUNDLER_CTX_MAP[file_path];
|
let bundledMap = global.BUNEXT_BUNDLER_CTX_MAP[file_path];
|
||||||
if (!bundledMap?.path) {
|
if (!bundledMap?.path) {
|
||||||
if (does_error_file_exist) {
|
if (does_error_file_exist) {
|
||||||
throw new Error(`Application Error. Please Check your components. ${match?.filePath} likely exists but has no exported module.`);
|
throw new Error(`Application Error. Please Check your components. ${match?.filePath} likely exists but has no exported module.`);
|
||||||
@@ -61,7 +62,8 @@ export default async function grabPageComponent(params) {
|
|||||||
await fullRebuild({
|
await fullRebuild({
|
||||||
msg: `Retrying Bundle map for file \`${file_path}\``,
|
msg: `Retrying Bundle map for file \`${file_path}\``,
|
||||||
});
|
});
|
||||||
bundledMap = global.BUNDLER_CTX_MAP[file_path];
|
await Bun.sleep(1000);
|
||||||
|
bundledMap = global.BUNEXT_BUNDLER_CTX_MAP[file_path];
|
||||||
if (bundledMap?.path)
|
if (bundledMap?.path)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -72,7 +74,7 @@ export default async function grabPageComponent(params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (req && !is_hydration) {
|
if (req && !is_hydration) {
|
||||||
global.BUNDLER_CTX_MAP[file_path].req = req;
|
global.BUNEXT_BUNDLER_CTX_MAP[file_path].req_url = req.url;
|
||||||
}
|
}
|
||||||
if (debug) {
|
if (debug) {
|
||||||
log.info(`bundledMap:`, bundledMap);
|
log.info(`bundledMap:`, bundledMap);
|
||||||
@@ -114,8 +116,9 @@ export default async function grabPageComponent(params) {
|
|||||||
error?.name === "NotFoundError" ||
|
error?.name === "NotFoundError" ||
|
||||||
error?.status === 404;
|
error?.status === 404;
|
||||||
if (!params.retry && is_dev) {
|
if (!params.retry && is_dev) {
|
||||||
while (global.REBUILD_RETRIES < 2) {
|
while (global.BUNEXT_REBUILD_RETRIES < 2) {
|
||||||
global.REBUILD_RETRIES = global.REBUILD_RETRIES + 1;
|
global.BUNEXT_REBUILD_RETRIES =
|
||||||
|
global.BUNEXT_REBUILD_RETRIES + 1;
|
||||||
await fullRebuild();
|
await fullRebuild();
|
||||||
await Bun.sleep(200);
|
await Bun.sleep(200);
|
||||||
const component_retried = await grabPageComponent({
|
const component_retried = await grabPageComponent({
|
||||||
@@ -124,19 +127,22 @@ export default async function grabPageComponent(params) {
|
|||||||
});
|
});
|
||||||
if (component_retried instanceof Response ||
|
if (component_retried instanceof Response ||
|
||||||
component_retried.success) {
|
component_retried.success) {
|
||||||
global.REBUILD_RETRIES = 0;
|
global.BUNEXT_REBUILD_RETRIES = 0;
|
||||||
await serverPostBuildFn();
|
await serverPostBuildFn();
|
||||||
return component_retried;
|
return component_retried;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
global.REBUILD_RETRIES = 0;
|
global.BUNEXT_REBUILD_RETRIES = 0;
|
||||||
}
|
}
|
||||||
if (is404) {
|
if (is404) {
|
||||||
global.IS_404_PAGE = true;
|
global.BUNEXT_IS_404_PAGE = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.error(`Error Grabbing Page Component: ${error.message}`);
|
log.error(`Error Grabbing Page Component: ${error.message}`);
|
||||||
log.error(`Page: ${passed_file_path || url?.pathname}`);
|
log.error(`Page: ${passed_file_path || url?.pathname}`);
|
||||||
|
if (is_dev) {
|
||||||
|
fullRebuild();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return await grabPageErrorComponent({
|
return await grabPageErrorComponent({
|
||||||
error,
|
error,
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|||||||
import grabDirNames from "../../../utils/grab-dir-names";
|
import grabDirNames from "../../../utils/grab-dir-names";
|
||||||
import grabPageModules from "./grab-page-modules";
|
import grabPageModules from "./grab-page-modules";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import fullRebuild from "../full-rebuild";
|
||||||
|
import isDevelopment from "../../../utils/is-development";
|
||||||
export default async function grabPageErrorComponent({ error, routeParams, is404, url, }) {
|
export default async function grabPageErrorComponent({ error, routeParams, is404, url, }) {
|
||||||
const router = global.ROUTER;
|
const router = global.BUNEXT_ROUTER;
|
||||||
|
const is_dev = isDevelopment();
|
||||||
const { BUNX_ROOT_500_PRESET_COMPONENT, BUNX_ROOT_404_PRESET_COMPONENT } = grabDirNames();
|
const { BUNX_ROOT_500_PRESET_COMPONENT, BUNX_ROOT_404_PRESET_COMPONENT } = grabDirNames();
|
||||||
const errorRoute = is404 ? "/404" : "/500";
|
const errorRoute = is404 ? "/404" : "/500";
|
||||||
const presetComponent = is404
|
const presetComponent = is404
|
||||||
@@ -30,7 +33,7 @@ export default async function grabPageErrorComponent({ error, routeParams, is404
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
const file_path = match.filePath;
|
const file_path = match.filePath;
|
||||||
const bundledMap = global.BUNDLER_CTX_MAP?.[file_path];
|
const bundledMap = global.BUNEXT_BUNDLER_CTX_MAP?.[file_path];
|
||||||
const page_component = await grabPageModules({
|
const page_component = await grabPageModules({
|
||||||
file_path: file_path,
|
file_path: file_path,
|
||||||
query: match?.query,
|
query: match?.query,
|
||||||
@@ -51,6 +54,9 @@ export default async function grabPageErrorComponent({ error, routeParams, is404
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
|
if (is_dev) {
|
||||||
|
fullRebuild();
|
||||||
|
}
|
||||||
const DefaultNotFound = () => (_jsxs("div", { style: {
|
const DefaultNotFound = () => (_jsxs("div", { style: {
|
||||||
width: "100vw",
|
width: "100vw",
|
||||||
height: "100vh",
|
height: "100vh",
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ export default async function grabPageServerRes({ url, query, routeParams, serve
|
|||||||
const serverData = await server_function({
|
const serverData = await server_function({
|
||||||
...routeParams,
|
...routeParams,
|
||||||
query: { ...routeParams.query, ...query },
|
query: { ...routeParams.query, ...query },
|
||||||
props: init_props,
|
props: init_props || undefined,
|
||||||
});
|
});
|
||||||
return _.merge(default_props, serverData);
|
return _.merge(default_props, serverData);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,11 +108,12 @@ async function loadEntry(page_file_path) {
|
|||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const mod_file_path = toModPath(page_file_path);
|
const mod_file_path = toModPath(page_file_path);
|
||||||
const mod_css_path = mod_file_path.replace(/\.js$/, ".css");
|
const mod_css_path = mod_file_path.replace(/\.js$/, ".css");
|
||||||
if (global.REACT_DOM_MODULE_CACHE.has(page_file_path)) {
|
if (global.BUNEXT_REACT_DOM_MODULE_CACHE.has(page_file_path)) {
|
||||||
return global.REACT_DOM_MODULE_CACHE.get(page_file_path)?.main;
|
return global.BUNEXT_REACT_DOM_MODULE_CACHE.get(page_file_path)
|
||||||
|
?.main;
|
||||||
}
|
}
|
||||||
const mod = await import(`${mod_file_path}?t=${now}`);
|
const mod = await import(`${mod_file_path}?t=${now}`);
|
||||||
global.REACT_DOM_MODULE_CACHE.set(page_file_path, {
|
global.BUNEXT_REACT_DOM_MODULE_CACHE.set(page_file_path, {
|
||||||
main: mod,
|
main: mod,
|
||||||
css: mod_css_path,
|
css: mod_css_path,
|
||||||
});
|
});
|
||||||
|
|||||||
Vendored
+13
-2
@@ -66,6 +66,16 @@ export type BunextConfig = {
|
|||||||
* bundler for the browser. Eg. `react/jsx-dev-runtime`
|
* bundler for the browser. Eg. `react/jsx-dev-runtime`
|
||||||
*/
|
*/
|
||||||
page_compiler_excludes?: string[];
|
page_compiler_excludes?: string[];
|
||||||
|
/**
|
||||||
|
* Patterns to exclude from the watcher. Eg. `./src/server.ts`
|
||||||
|
* or `\*.test.ts\`. It should either be a file path (relative or
|
||||||
|
* absolute), or a RegEx pattern.
|
||||||
|
*/
|
||||||
|
exclude_watch_patterns?: (string | RegExp)[];
|
||||||
|
/**
|
||||||
|
* Public environment variables to be passed to the client
|
||||||
|
*/
|
||||||
|
public_envs?: Record<string, string>;
|
||||||
};
|
};
|
||||||
export type BunextConfigMiddlewareParams = {
|
export type BunextConfigMiddlewareParams = {
|
||||||
req: Request;
|
req: Request;
|
||||||
@@ -203,7 +213,7 @@ export type BunextPageServerFn<T extends {
|
|||||||
} = {
|
} = {
|
||||||
[k: string]: any;
|
[k: string]: any;
|
||||||
}> = (ctx: Omit<BunxRouteParams, "body"> & {
|
}> = (ctx: Omit<BunxRouteParams, "body"> & {
|
||||||
props?: any;
|
props?: T;
|
||||||
}) => Promise<BunextPageModuleServerReturn<T>>;
|
}) => Promise<BunextPageModuleServerReturn<T>>;
|
||||||
export type BunextRouteConfig = {
|
export type BunextRouteConfig = {
|
||||||
/**
|
/**
|
||||||
@@ -306,7 +316,7 @@ export type BundlerCTXMap = {
|
|||||||
url_path: string;
|
url_path: string;
|
||||||
file_name: string;
|
file_name: string;
|
||||||
css_path?: string;
|
css_path?: string;
|
||||||
req?: Request;
|
req_url?: string;
|
||||||
};
|
};
|
||||||
export type GlobalHMRControllerObject = {
|
export type GlobalHMRControllerObject = {
|
||||||
controller: ReadableStreamDefaultController<string>;
|
controller: ReadableStreamDefaultController<string>;
|
||||||
@@ -314,6 +324,7 @@ export type GlobalHMRControllerObject = {
|
|||||||
target_map?: BundlerCTXMap;
|
target_map?: BundlerCTXMap;
|
||||||
page_props?: any;
|
page_props?: any;
|
||||||
page_reloaded?: boolean;
|
page_reloaded?: boolean;
|
||||||
|
page_cookie?: string | null;
|
||||||
};
|
};
|
||||||
export type BunextCacheFileMeta = {
|
export type BunextCacheFileMeta = {
|
||||||
date_created: number;
|
date_created: number;
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
export default function ({ path }) {
|
export default function ({ path }) {
|
||||||
for (let i = 0; i < global.CONSTANTS.RouteIgnorePatterns.length; i++) {
|
for (let i = 0; i < global.BUNEXT_CONSTANTS.RouteIgnorePatterns.length; i++) {
|
||||||
const regex = global.CONSTANTS.RouteIgnorePatterns[i];
|
const regex = global.BUNEXT_CONSTANTS.RouteIgnorePatterns[i];
|
||||||
if (path.match(regex))
|
if (path.match(regex))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
-3
@@ -1,6 +1,3 @@
|
|||||||
/**
|
|
||||||
* # Convert Serialized Query back to object
|
|
||||||
*/
|
|
||||||
export default function deserializeQuery(query: string | {
|
export default function deserializeQuery(query: string | {
|
||||||
[s: string]: any;
|
[s: string]: any;
|
||||||
}): {
|
}): {
|
||||||
|
|||||||
Vendored
+20
-5
@@ -1,18 +1,33 @@
|
|||||||
import EJSON from "./ejson";
|
import EJSON from "./ejson";
|
||||||
/**
|
const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
||||||
* # Convert Serialized Query back to object
|
function sanitize(value) {
|
||||||
*/
|
if (value === null || typeof value !== "object")
|
||||||
|
return value;
|
||||||
|
if (Array.isArray(value))
|
||||||
|
return value.map(sanitize);
|
||||||
|
const clean = Object.create(null);
|
||||||
|
for (const key of Object.keys(value)) {
|
||||||
|
if (DANGEROUS_KEYS.has(key))
|
||||||
|
continue;
|
||||||
|
clean[key] = sanitize(value[key]);
|
||||||
|
}
|
||||||
|
return clean;
|
||||||
|
}
|
||||||
export default function deserializeQuery(query) {
|
export default function deserializeQuery(query) {
|
||||||
let queryObject = typeof query == "object" ? query : Object(EJSON.parse(query));
|
let queryObject = typeof query == "object" ? query : Object(EJSON.parse(query));
|
||||||
const keys = Object.keys(queryObject);
|
const keys = Object.keys(queryObject);
|
||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
const key = keys[i];
|
const key = keys[i];
|
||||||
const value = queryObject[key];
|
const value = queryObject[key];
|
||||||
|
if (DANGEROUS_KEYS.has(key)) {
|
||||||
|
delete queryObject[key];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (typeof value == "string") {
|
if (typeof value == "string") {
|
||||||
if (value.match(/^\{|^\[/)) {
|
if (value.match(/^\{|^\[/)) {
|
||||||
queryObject[key] = EJSON.parse(value);
|
queryObject[key] = sanitize(EJSON.parse(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return queryObject;
|
return sanitize(queryObject);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+6
@@ -41,6 +41,12 @@ function grabPageDirRecursively({ page_dir, include_server, }) {
|
|||||||
if (is_page_excluded) {
|
if (is_page_excluded) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (full_page_path.match(/__tests__/)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (page_name.match(/\.test\.(t|j)sx?/)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (page_name.match(/\.server\.tsx?/) && !include_server) {
|
if (page_name.match(/\.server\.tsx?/) && !include_server) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+2
-2
@@ -6,8 +6,8 @@ export default function grabAppPort() {
|
|||||||
if (process.env.PORT) {
|
if (process.env.PORT) {
|
||||||
return numberfy(process.env.PORT);
|
return numberfy(process.env.PORT);
|
||||||
}
|
}
|
||||||
if (global.CONFIG.port) {
|
if (global.BUNEXT_CONFIG.port) {
|
||||||
return global.CONFIG.port;
|
return global.BUNEXT_CONFIG.port;
|
||||||
}
|
}
|
||||||
return numberfy(defaultPort);
|
return numberfy(defaultPort);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import AppNames from "./grab-app-names";
|
import AppNames from "./grab-app-names";
|
||||||
export default function grabAssetsPrefix() {
|
export default function grabAssetsPrefix() {
|
||||||
if (global.CONFIG.assets_prefix) {
|
if (global.BUNEXT_CONFIG.assets_prefix) {
|
||||||
return global.CONFIG.assets_prefix;
|
return global.BUNEXT_CONFIG.assets_prefix;
|
||||||
}
|
}
|
||||||
const { defaultAssetPrefix } = AppNames;
|
const { defaultAssetPrefix } = AppNames;
|
||||||
return defaultAssetPrefix;
|
return defaultAssetPrefix;
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
export default function grabConstants() {
|
export default function grabConstants() {
|
||||||
const config = global.CONFIG;
|
const config = global.BUNEXT_CONFIG;
|
||||||
const MB_IN_BYTES = 1024 * 1024;
|
const MB_IN_BYTES = 1024 * 1024;
|
||||||
const ClientWindowPagePropsName = "__PAGE_PROPS__";
|
const ClientWindowPagePropsName = "__PAGE_PROPS__";
|
||||||
const ClientRootElementIDName = "__bunext";
|
const ClientRootElementIDName = "__bunext";
|
||||||
|
|||||||
Vendored
+2
-1
@@ -1,4 +1,4 @@
|
|||||||
export default function grabDirNames(): {
|
export type DirNames = {
|
||||||
ROOT_DIR: string;
|
ROOT_DIR: string;
|
||||||
SRC_DIR: string;
|
SRC_DIR: string;
|
||||||
PAGES_DIR: string;
|
PAGES_DIR: string;
|
||||||
@@ -27,3 +27,4 @@ export default function grabDirNames(): {
|
|||||||
BUNX_ERROR_LOGS_DIR: string;
|
BUNX_ERROR_LOGS_DIR: string;
|
||||||
BUNX_LOGS_DIR: string;
|
BUNX_LOGS_DIR: string;
|
||||||
};
|
};
|
||||||
|
export default function grabDirNames(): DirNames;
|
||||||
|
|||||||
Vendored
+2
@@ -1,5 +1,7 @@
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
export default function grabDirNames() {
|
export default function grabDirNames() {
|
||||||
|
if (global.BUNEXT_DIR_NAMES)
|
||||||
|
return global.BUNEXT_DIR_NAMES;
|
||||||
const ROOT_DIR = process.cwd();
|
const ROOT_DIR = process.cwd();
|
||||||
const SRC_DIR = path.join(ROOT_DIR, "src");
|
const SRC_DIR = path.join(ROOT_DIR, "src");
|
||||||
const PAGES_DIR = path.join(SRC_DIR, "pages");
|
const PAGES_DIR = path.join(SRC_DIR, "pages");
|
||||||
|
|||||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import grabAppPort from "./grab-app-port";
|
import grabAppPort from "./grab-app-port";
|
||||||
export default function grabOrigin() {
|
export default function grabOrigin() {
|
||||||
if (global.CONFIG.origin) {
|
if (global.BUNEXT_CONFIG.origin) {
|
||||||
return global.CONFIG.origin;
|
return global.BUNEXT_CONFIG.origin;
|
||||||
}
|
}
|
||||||
const port = grabAppPort();
|
const port = grabAppPort();
|
||||||
return `http://localhost:${port}`;
|
return `http://localhost:${port}`;
|
||||||
|
|||||||
Vendored
+1
-1
@@ -16,7 +16,7 @@ export default async function grabRouteParams({ req, query: passed_query, }) {
|
|||||||
url,
|
url,
|
||||||
query: _.merge(query, passed_query),
|
query: _.merge(query, passed_query),
|
||||||
body,
|
body,
|
||||||
server: global.SERVER,
|
server: global.BUNEXT_SERVER,
|
||||||
};
|
};
|
||||||
return routeParams;
|
return routeParams;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
export default function grabRouter() {
|
export default function grabRouter() {
|
||||||
// if (process.env.NODE_ENV !== "production") {
|
// if (process.env.NODE_ENV !== "production") {
|
||||||
// global.ROUTER.reload();
|
// global.BUNEXT_ROUTER.reload();
|
||||||
// }
|
// }
|
||||||
return global.ROUTER;
|
return global.BUNEXT_ROUTER;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-5
@@ -1,10 +1,6 @@
|
|||||||
export default function isDevelopment() {
|
export default function isDevelopment() {
|
||||||
const config = global.CONFIG;
|
if (process.env.NODE_ENV === "production") {
|
||||||
if (process.env.NODE_ENV == "production") {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (config.development) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
export default function isSafePath({ filePath, allowedDir, }: {
|
||||||
|
filePath: string;
|
||||||
|
allowedDir: string;
|
||||||
|
}): boolean;
|
||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
import { realpathSync } from "fs";
|
||||||
|
import path from "path";
|
||||||
|
export default function isSafePath({ filePath, allowedDir, }) {
|
||||||
|
const resolved = path.resolve(filePath);
|
||||||
|
if (!resolved.startsWith(allowedDir + path.sep) && resolved !== allowedDir) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const real = realpathSync(resolved);
|
||||||
|
return (real.startsWith(allowedDir + path.sep) || real === allowedDir);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+1
-1
@@ -21,5 +21,5 @@ export const log = {
|
|||||||
build: (msg) => console.log(`${prefix.build} ${chalk.magenta(msg)}`),
|
build: (msg) => console.log(`${prefix.build} ${chalk.magenta(msg)}`),
|
||||||
watch: (msg) => console.log(`${prefix.watch} ${chalk.blue(msg)}`),
|
watch: (msg) => console.log(`${prefix.watch} ${chalk.blue(msg)}`),
|
||||||
server: (url) => console.log(`${prefix.success} ${chalk.white("Server running on")} ${chalk.cyan.underline(url)}`),
|
server: (url) => console.log(`${prefix.success} ${chalk.white("Server running on")} ${chalk.cyan.underline(url)}`),
|
||||||
banner: () => console.log(`\n ${chalk.cyan.bold(AppNames.name)} ${chalk.gray(`v${global.CURRENT_VERSION || AppNames["version"]}`)}\n`),
|
banner: () => console.log(`\n ${chalk.cyan.bold(AppNames.name)} ${chalk.gray(`v${global.BUNEXT_CURRENT_VERSION || AppNames["version"]}`)}\n`),
|
||||||
};
|
};
|
||||||
|
|||||||
Vendored
+1
-1
@@ -5,5 +5,5 @@ export default function refreshRouter() {
|
|||||||
style: "nextjs",
|
style: "nextjs",
|
||||||
dir: PAGES_DIR,
|
dir: PAGES_DIR,
|
||||||
});
|
});
|
||||||
global.ROUTER = router;
|
global.BUNEXT_ROUTER = router;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
-1
@@ -1 +0,0 @@
|
|||||||
export default function registerDevPlugin(): void;
|
|
||||||
Vendored
-69
@@ -1,69 +0,0 @@
|
|||||||
import { resolve, dirname, extname } from "path";
|
|
||||||
import { existsSync } from "fs";
|
|
||||||
const SOURCE_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js"];
|
|
||||||
function getLoader(filePath) {
|
|
||||||
const ext = extname(filePath).slice(1);
|
|
||||||
return SOURCE_EXTENSIONS.map((e) => e.slice(1)).includes(ext) ? ext : "js";
|
|
||||||
}
|
|
||||||
function tryResolveSync(absPath) {
|
|
||||||
if (existsSync(absPath))
|
|
||||||
return absPath;
|
|
||||||
for (const ext of SOURCE_EXTENSIONS) {
|
|
||||||
const p = absPath + ext;
|
|
||||||
if (existsSync(p))
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
for (const ext of SOURCE_EXTENSIONS) {
|
|
||||||
const p = resolve(absPath, "index" + ext);
|
|
||||||
if (existsSync(p))
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
export default function registerDevPlugin() {
|
|
||||||
Bun.plugin({
|
|
||||||
name: "bunext-dev-hmr",
|
|
||||||
setup(build) {
|
|
||||||
// Intercept absolute-path imports that already carry ?t= (our dynamic imports)
|
|
||||||
build.onResolve({ filter: /\?t=\d+$/ }, (args) => {
|
|
||||||
if (args.path.includes("node_modules"))
|
|
||||||
return undefined;
|
|
||||||
const cleanPath = args.path.replace(/\?t=\d+$/, "");
|
|
||||||
const resolved = tryResolveSync(cleanPath);
|
|
||||||
if (!resolved)
|
|
||||||
return undefined;
|
|
||||||
if (!SOURCE_EXTENSIONS.some((e) => resolved.endsWith(e)))
|
|
||||||
return undefined;
|
|
||||||
return {
|
|
||||||
path: `${resolved}?t=${global.LAST_BUILD_TIME ?? 0}`,
|
|
||||||
namespace: "bunext-dev",
|
|
||||||
};
|
|
||||||
});
|
|
||||||
// Intercept relative imports from within bunext-dev modules
|
|
||||||
build.onResolve({ filter: /^\./ }, (args) => {
|
|
||||||
if (!/\?t=\d+/.test(args.importer))
|
|
||||||
return undefined;
|
|
||||||
// Strip "namespace:" prefix (e.g. "bunext-dev:") Bun prepends to importer
|
|
||||||
const cleanImporter = args.importer
|
|
||||||
.replace(/^[^/]+:(?=\/)/, "")
|
|
||||||
.replace(/\?t=\d+$/, "");
|
|
||||||
const base = resolve(dirname(cleanImporter), args.path);
|
|
||||||
const resolved = tryResolveSync(base);
|
|
||||||
if (!resolved)
|
|
||||||
return undefined;
|
|
||||||
if (!SOURCE_EXTENSIONS.some((e) => resolved.endsWith(e)))
|
|
||||||
return undefined;
|
|
||||||
return {
|
|
||||||
path: `${resolved}?t=${global.LAST_BUILD_TIME ?? 0}`,
|
|
||||||
namespace: "bunext-dev",
|
|
||||||
};
|
|
||||||
});
|
|
||||||
// Load files in the bunext-dev namespace from disk (async is fine in onLoad)
|
|
||||||
build.onLoad({ filter: /.*/, namespace: "bunext-dev" }, async (args) => {
|
|
||||||
const realPath = args.path.replace(/\?t=\d+$/, "");
|
|
||||||
const source = await Bun.file(realPath).text();
|
|
||||||
return { contents: source, loader: getLoader(realPath) };
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
+3
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@moduletrace/bunext",
|
"name": "@moduletrace/bunext",
|
||||||
"version": "1.0.86",
|
"version": "1.1.7",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"module": "index.ts",
|
"module": "index.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
"@types/react-dom": "^19.2.2",
|
"@types/react-dom": "^19.2.2",
|
||||||
"bun-plugin-tailwind": "^0.1.2",
|
"bun-plugin-tailwind": "^0.1.2",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
|
"chokidar": "^5.0.0",
|
||||||
"commander": "^14.0.2",
|
"commander": "^14.0.2",
|
||||||
"esbuild": "^0.27.4",
|
"esbuild": "^0.27.4",
|
||||||
"lightningcss-wasm": "^1.32.0",
|
"lightningcss-wasm": "^1.32.0",
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/dom": "^10.4.1",
|
"@testing-library/dom": "^10.4.1",
|
||||||
|
"@types/chokidar": "^2.1.7",
|
||||||
"@types/lodash": "^4.17.24",
|
"@types/lodash": "^4.17.24",
|
||||||
"@types/micromatch": "^4.0.10",
|
"@types/micromatch": "^4.0.10",
|
||||||
"happy-dom": "^20.8.4"
|
"happy-dom": "^20.8.4"
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test";
|
||||||
|
|
||||||
|
const grabPageComponent = mock(async () => ({
|
||||||
|
serverRes: { props: { ok: true } },
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module(
|
||||||
|
"../../../functions/server/web-pages/grab-page-component",
|
||||||
|
() => ({
|
||||||
|
default: grabPageComponent,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { default: serverPostBuildFn } = await import(
|
||||||
|
"../../../functions/server/server-post-build-fn"
|
||||||
|
);
|
||||||
|
|
||||||
|
function makeController(page_url: string, target_map?: { local_path: string }) {
|
||||||
|
const enqueued: string[] = [];
|
||||||
|
return {
|
||||||
|
controller: {
|
||||||
|
enqueue: (chunk: string) => {
|
||||||
|
enqueued.push(chunk);
|
||||||
|
},
|
||||||
|
} as any,
|
||||||
|
page_url,
|
||||||
|
target_map: target_map as any,
|
||||||
|
enqueued,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("server-post-build-fn", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
grabPageComponent.mockClear();
|
||||||
|
global.ROUTER = {
|
||||||
|
match: (path: string) => {
|
||||||
|
if (path === "/about") return { filePath: "/pages/about.tsx" };
|
||||||
|
if (path === "/404") return { filePath: "/pages/404.tsx" };
|
||||||
|
if (path === "/new-page")
|
||||||
|
return { filePath: "/pages/new-page.tsx" };
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
} as any;
|
||||||
|
global.BUNDLER_CTX_MAP = {
|
||||||
|
"/pages/about.tsx": {
|
||||||
|
local_path: "/pages/about.tsx",
|
||||||
|
path: "pages/about.js",
|
||||||
|
} as any,
|
||||||
|
"/pages/404.tsx": {
|
||||||
|
local_path: "/pages/404.tsx",
|
||||||
|
path: "pages/404.js",
|
||||||
|
} as any,
|
||||||
|
"/pages/new-page.tsx": {
|
||||||
|
local_path: "/pages/new-page.tsx",
|
||||||
|
path: "pages/new-page.js",
|
||||||
|
} as any,
|
||||||
|
};
|
||||||
|
global.HMR_CONTROLLERS = [];
|
||||||
|
global.ROOT_FILE_UPDATED = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
global.ROUTER = undefined as any;
|
||||||
|
global.BUNDLER_CTX_MAP = undefined as any;
|
||||||
|
global.HMR_CONTROLLERS = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
test("soft-updates a normal page controller", async () => {
|
||||||
|
const c = makeController("http://localhost/about", {
|
||||||
|
local_path: "/pages/about.tsx",
|
||||||
|
});
|
||||||
|
global.HMR_CONTROLLERS = [c as any];
|
||||||
|
|
||||||
|
await serverPostBuildFn();
|
||||||
|
|
||||||
|
expect(c.enqueued.length).toBe(1);
|
||||||
|
expect(c.enqueued[0]).toContain("event: update");
|
||||||
|
expect(c.enqueued[0]).not.toContain('"reload":true');
|
||||||
|
expect(c.enqueued[0]).toContain("/pages/about.tsx");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("soft-updates unmatched URL via custom 404 artifact", async () => {
|
||||||
|
const c = makeController("http://localhost/missing", {
|
||||||
|
local_path: "/pages/404.tsx",
|
||||||
|
});
|
||||||
|
global.HMR_CONTROLLERS = [c as any];
|
||||||
|
|
||||||
|
await serverPostBuildFn();
|
||||||
|
|
||||||
|
expect(c.enqueued.length).toBe(1);
|
||||||
|
expect(c.enqueued[0]).not.toContain('"reload":true');
|
||||||
|
expect(c.enqueued[0]).toContain("/pages/404.tsx");
|
||||||
|
expect(grabPageComponent).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("full-reloads when a previously unmatched route now exists", async () => {
|
||||||
|
const c = makeController("http://localhost/new-page", {
|
||||||
|
local_path: "/pages/404.tsx",
|
||||||
|
});
|
||||||
|
global.HMR_CONTROLLERS = [c as any];
|
||||||
|
|
||||||
|
await serverPostBuildFn();
|
||||||
|
|
||||||
|
expect(c.enqueued.length).toBe(1);
|
||||||
|
expect(c.enqueued[0]).toContain('"reload":true');
|
||||||
|
expect(c.target_map?.local_path).toBe("/pages/new-page.tsx");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("full-reloads unmatched tab with no prior map when route appears", async () => {
|
||||||
|
const c = makeController("http://localhost/new-page");
|
||||||
|
global.HMR_CONTROLLERS = [c as any];
|
||||||
|
|
||||||
|
await serverPostBuildFn();
|
||||||
|
|
||||||
|
expect(c.enqueued.length).toBe(1);
|
||||||
|
expect(c.enqueued[0]).toContain('"reload":true');
|
||||||
|
});
|
||||||
|
|
||||||
|
test("full-reloads preset 404 tabs with no custom 404 page", async () => {
|
||||||
|
global.ROUTER = {
|
||||||
|
match: () => null,
|
||||||
|
} as any;
|
||||||
|
const c = makeController("http://localhost/missing");
|
||||||
|
global.HMR_CONTROLLERS = [c as any];
|
||||||
|
|
||||||
|
await serverPostBuildFn();
|
||||||
|
|
||||||
|
expect(c.enqueued.length).toBe(1);
|
||||||
|
expect(c.enqueued[0]).toContain('"reload":true');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
import { Command } from "commander";
|
import { Command } from "commander";
|
||||||
import { log } from "../../utils/log";
|
import { log } from "../../utils/log";
|
||||||
import init from "../../functions/init";
|
|
||||||
import grabDirNames from "../../utils/grab-dir-names";
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
import { rmSync } from "fs";
|
import { rmSync } from "fs";
|
||||||
import allPagesESBuildContextBundler from "../../functions/bundler/all-pages-esbuild-context-bundler";
|
import bunextInit from "../../functions/bunext-init";
|
||||||
|
|
||||||
const { HYDRATION_DST_DIR, BUNX_CWD_PAGES_REWRITE_DIR } = grabDirNames();
|
const { HYDRATION_DST_DIR, BUNX_CWD_PAGES_REWRITE_DIR } = grabDirNames();
|
||||||
|
|
||||||
@@ -16,18 +15,11 @@ export default function () {
|
|||||||
rmSync(BUNX_CWD_PAGES_REWRITE_DIR, { recursive: true });
|
rmSync(BUNX_CWD_PAGES_REWRITE_DIR, { recursive: true });
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
|
|
||||||
global.SKIPPED_BROWSER_MODULES = new Set<string>();
|
global.BUNEXT_SKIPPED_BROWSER_MODULES = new Set<string>();
|
||||||
|
|
||||||
// await rewritePagesModule();
|
await bunextInit({ build_only: true });
|
||||||
await init();
|
|
||||||
|
|
||||||
log.banner();
|
log.success("Modules Built Successfully!");
|
||||||
log.build("Building Project ...");
|
|
||||||
|
|
||||||
// await allPagesBunBundler();
|
|
||||||
|
|
||||||
// await allPagesBundler();
|
|
||||||
await allPagesESBuildContextBundler();
|
|
||||||
|
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,14 @@ import { rmSync } from "fs";
|
|||||||
|
|
||||||
const { HYDRATION_DST_DIR, BUNX_CWD_PAGES_REWRITE_DIR } = grabDirNames();
|
const { HYDRATION_DST_DIR, BUNX_CWD_PAGES_REWRITE_DIR } = grabDirNames();
|
||||||
|
|
||||||
|
process.on("uncaughtException", (error) => {
|
||||||
|
log.error(`Uncaught exception: ${error}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("unhandledRejection", (reason) => {
|
||||||
|
log.error(`Unhandled rejection: ${reason}`);
|
||||||
|
});
|
||||||
|
|
||||||
log.info("Running development server ...");
|
log.info("Running development server ...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -13,6 +21,9 @@ try {
|
|||||||
rmSync(BUNX_CWD_PAGES_REWRITE_DIR, { recursive: true });
|
rmSync(BUNX_CWD_PAGES_REWRITE_DIR, { recursive: true });
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
|
|
||||||
|
try {
|
||||||
await bunextInit();
|
await bunextInit();
|
||||||
|
|
||||||
await startServer();
|
await startServer();
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Failed to start development server: ${error}`);
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import path from "path";
|
|||||||
import type { BunSpawnOptions } from "../../types";
|
import type { BunSpawnOptions } from "../../types";
|
||||||
import grabDirNames from "../../utils/grab-dir-names";
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
import writeErrorFile from "../../functions/write-error-file";
|
import writeErrorFile from "../../functions/write-error-file";
|
||||||
|
import { existsSync } from "fs";
|
||||||
|
|
||||||
let retries = 0;
|
let retries = 0;
|
||||||
let timeout: any;
|
let timeout: any;
|
||||||
@@ -25,9 +26,14 @@ async function dev() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dev_spawn_file = path.resolve(__dirname, "dev-spawn.ts");
|
const dev_spawn_file = path.resolve(__dirname, "dev-spawn.ts");
|
||||||
|
const dev_spawn_js_file = path.resolve(__dirname, "dev-spawn.js");
|
||||||
|
|
||||||
|
const final_spawn_file = existsSync(dev_spawn_js_file)
|
||||||
|
? dev_spawn_js_file
|
||||||
|
: dev_spawn_file;
|
||||||
|
|
||||||
const spawn_options: BunSpawnOptions = {
|
const spawn_options: BunSpawnOptions = {
|
||||||
cmd: ["bun", dev_spawn_file],
|
cmd: ["bun", final_spawn_file],
|
||||||
stdio: ["inherit", "inherit", "inherit"],
|
stdio: ["inherit", "inherit", "inherit"],
|
||||||
async onExit(subprocess, exitCode, signalCode, error) {
|
async onExit(subprocess, exitCode, signalCode, error) {
|
||||||
writeErrorFile({ exitCode, error });
|
writeErrorFile({ exitCode, error });
|
||||||
@@ -38,16 +44,29 @@ async function dev() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let dev_process = Bun.spawn(spawn_options);
|
let dev_process;
|
||||||
|
try {
|
||||||
|
dev_process = Bun.spawn(spawn_options);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to start dev process:`, error);
|
||||||
retries++;
|
retries++;
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
retries = 0;
|
||||||
|
}, 10000);
|
||||||
|
return await dev();
|
||||||
|
}
|
||||||
|
|
||||||
|
const exited = await dev_process.exited;
|
||||||
|
|
||||||
|
if (exited) {
|
||||||
|
retries++;
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
retries = 0;
|
||||||
|
}, 10000);
|
||||||
|
return await dev();
|
||||||
|
}
|
||||||
|
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
retries = 0;
|
retries = 0;
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
||||||
const exited = await dev_process.exited;
|
|
||||||
if (exited) {
|
|
||||||
return await dev();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ import { Command } from "commander";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import type { BunSpawnOptions } from "../../types";
|
import type { BunSpawnOptions } from "../../types";
|
||||||
import writeErrorFile from "../../functions/write-error-file";
|
import writeErrorFile from "../../functions/write-error-file";
|
||||||
|
import { existsSync } from "fs";
|
||||||
|
|
||||||
|
let retries = 0;
|
||||||
|
let timeout: any;
|
||||||
|
const MAX_RETRIES = 5;
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
return new Command("start")
|
return new Command("start")
|
||||||
@@ -12,10 +17,24 @@ export default function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function start() {
|
async function start() {
|
||||||
const dev_spawn_file = path.resolve(__dirname, "prod-spawn.ts");
|
clearTimeout(timeout);
|
||||||
|
|
||||||
|
if (retries >= MAX_RETRIES) {
|
||||||
|
console.error(
|
||||||
|
`Production server crashed ${MAX_RETRIES} times. Exiting.`,
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const prod_spawn_file = path.resolve(__dirname, "prod-spawn.ts");
|
||||||
|
const prod_spawn_js_file = path.resolve(__dirname, "prod-spawn.js");
|
||||||
|
|
||||||
|
const final_spawn_file = existsSync(prod_spawn_js_file)
|
||||||
|
? prod_spawn_js_file
|
||||||
|
: prod_spawn_file;
|
||||||
|
|
||||||
const spawn_options: BunSpawnOptions = {
|
const spawn_options: BunSpawnOptions = {
|
||||||
cmd: ["bun", dev_spawn_file],
|
cmd: ["bun", final_spawn_file],
|
||||||
stdio: ["inherit", "inherit", "inherit"],
|
stdio: ["inherit", "inherit", "inherit"],
|
||||||
onExit(subprocess, exitCode, signalCode, error) {
|
onExit(subprocess, exitCode, signalCode, error) {
|
||||||
writeErrorFile({ exitCode, error });
|
writeErrorFile({ exitCode, error });
|
||||||
@@ -26,11 +45,29 @@ async function start() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let dev_process = Bun.spawn(spawn_options);
|
let dev_process;
|
||||||
|
try {
|
||||||
|
dev_process = Bun.spawn(spawn_options);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to start production process:`, error);
|
||||||
|
retries++;
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
retries = 0;
|
||||||
|
}, 10000);
|
||||||
|
return await start();
|
||||||
|
}
|
||||||
|
|
||||||
const exited = await dev_process.exited;
|
const exited = await dev_process.exited;
|
||||||
|
|
||||||
if (exited) {
|
if (exited) {
|
||||||
|
retries++;
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
retries = 0;
|
||||||
|
}, 10000);
|
||||||
return await start();
|
return await start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
retries = 0;
|
||||||
|
}, 10000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ type Params = {
|
|||||||
post_build_fn?: (params: {
|
post_build_fn?: (params: {
|
||||||
artifacts: BundlerCTXMap[];
|
artifacts: BundlerCTXMap[];
|
||||||
}) => Promise<void> | void;
|
}) => Promise<void> | void;
|
||||||
|
build_only?: boolean;
|
||||||
|
start?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function allPagesESBuildContextBundler(params?: Params) {
|
export default async function allPagesESBuildContextBundler(params?: Params) {
|
||||||
@@ -30,7 +32,7 @@ export default async function allPagesESBuildContextBundler(params?: Params) {
|
|||||||
|
|
||||||
const pages = grabAllPages({ exclude_api: true });
|
const pages = grabAllPages({ exclude_api: true });
|
||||||
|
|
||||||
global.PAGE_FILES = pages;
|
global.BUNEXT_PAGE_FILES = pages;
|
||||||
|
|
||||||
const dev = isDevelopment();
|
const dev = isDevelopment();
|
||||||
|
|
||||||
@@ -58,7 +60,7 @@ export default async function allPagesESBuildContextBundler(params?: Params) {
|
|||||||
(e) => `hydration-virtual:${e}`,
|
(e) => `hydration-virtual:${e}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
global.BUNDLER_CTX = await esbuild.context({
|
global.BUNEXT_BUNDLER_CTX = await esbuild.context({
|
||||||
entryPoints,
|
entryPoints,
|
||||||
outdir: HYDRATION_DST_DIR,
|
outdir: HYDRATION_DST_DIR,
|
||||||
bundle: true,
|
bundle: true,
|
||||||
@@ -82,6 +84,7 @@ export default async function allPagesESBuildContextBundler(params?: Params) {
|
|||||||
esbuildCTXArtifactTracker({
|
esbuildCTXArtifactTracker({
|
||||||
entryToPage,
|
entryToPage,
|
||||||
post_build_fn: params?.post_build_fn,
|
post_build_fn: params?.post_build_fn,
|
||||||
|
build_only: params?.build_only || params?.start,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
jsx: "automatic",
|
jsx: "automatic",
|
||||||
@@ -93,14 +96,14 @@ export default async function allPagesESBuildContextBundler(params?: Params) {
|
|||||||
"react-dom/client",
|
"react-dom/client",
|
||||||
"react/jsx-runtime",
|
"react/jsx-runtime",
|
||||||
"react/jsx-dev-runtime",
|
"react/jsx-dev-runtime",
|
||||||
...(global.CONFIG.page_compiler_excludes || []),
|
...(global.BUNEXT_CONFIG.page_compiler_excludes || []),
|
||||||
],
|
],
|
||||||
logLevel: did_process_exit_because_of_bundler_error
|
logLevel: did_process_exit_because_of_bundler_error
|
||||||
? "silent"
|
? "silent"
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
await global.BUNDLER_CTX.rebuild();
|
await global.BUNEXT_BUNDLER_CTX.rebuild();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(`ESBUILD Error =>`, error);
|
console.log(`ESBUILD Error =>`, error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,23 @@ export default async function buildOnstartErrorHandler(params?: Params) {
|
|||||||
// const error_msg = `Build Failed. Please check all your components and imports.`;
|
// const error_msg = `Build Failed. Please check all your components and imports.`;
|
||||||
// log.error(error_msg);
|
// log.error(error_msg);
|
||||||
|
|
||||||
global.BUNDLER_CTX_DISPOSED = true;
|
if (global.BUNEXT_BUNDLER_CTX_DISPOSED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
global.RECOMPILING = false;
|
// console.log(`Killing Bundler ...`);
|
||||||
global.IS_SERVER_COMPONENT = false;
|
// console.log(`global.BUNEXT_BUNDLER_CTX_DISPOSED`, global.BUNEXT_BUNDLER_CTX_DISPOSED);
|
||||||
|
|
||||||
Promise.all([
|
global.BUNEXT_BUNDLER_CTX_DISPOSED = true;
|
||||||
global.SSR_BUNDLER_CTX?.dispose(),
|
|
||||||
global.BUNDLER_CTX?.dispose(),
|
global.BUNEXT_RECOMPILING = false;
|
||||||
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
global.BUNEXT_SSR_BUNDLER_CTX?.dispose(),
|
||||||
|
global.BUNEXT_BUNDLER_CTX?.dispose(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
global.SSR_BUNDLER_CTX = undefined;
|
global.BUNEXT_SSR_BUNDLER_CTX = undefined;
|
||||||
global.BUNDLER_CTX = undefined;
|
global.BUNEXT_BUNDLER_CTX = undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default async function bunReactModulesBundler() {
|
|||||||
|
|
||||||
const PUBLIC_ROOT = BUNEXT_VENDOR_DIR.replace(BUNX_CWD_DIR, "/.bunext");
|
const PUBLIC_ROOT = BUNEXT_VENDOR_DIR.replace(BUNX_CWD_DIR, "/.bunext");
|
||||||
|
|
||||||
global.REACT_IMPORTS_MAP = {
|
global.BUNEXT_REACT_IMPORTS_MAP = {
|
||||||
imports: {
|
imports: {
|
||||||
react: `${PUBLIC_ROOT}/react.js`,
|
react: `${PUBLIC_ROOT}/react.js`,
|
||||||
"react-dom": `${PUBLIC_ROOT}/react-dom.js`,
|
"react-dom": `${PUBLIC_ROOT}/react-dom.js`,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default async function pagesSSRBundler(params?: Params) {
|
|||||||
include_server: true,
|
include_server: true,
|
||||||
});
|
});
|
||||||
const dev = isDevelopment();
|
const dev = isDevelopment();
|
||||||
const config = global.CONFIG;
|
const config = global.BUNEXT_CONFIG;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
@@ -75,6 +75,7 @@ export default async function pagesSSRBundler(params?: Params) {
|
|||||||
);
|
);
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
|
|
||||||
|
try {
|
||||||
await esbuild.build({
|
await esbuild.build({
|
||||||
entryPoints,
|
entryPoints,
|
||||||
outdir: BUNX_CWD_MODULE_CACHE_DIR,
|
outdir: BUNX_CWD_MODULE_CACHE_DIR,
|
||||||
@@ -107,11 +108,15 @@ export default async function pagesSSRBundler(params?: Params) {
|
|||||||
"react/jsx-runtime",
|
"react/jsx-runtime",
|
||||||
"react/jsx-dev-runtime",
|
"react/jsx-dev-runtime",
|
||||||
"bun:*",
|
"bun:*",
|
||||||
|
"bun",
|
||||||
"sqlite-vec",
|
"sqlite-vec",
|
||||||
"better-sqlite3",
|
"better-sqlite3",
|
||||||
...(config.ssr_compiler_excludes || []),
|
...(config.ssr_compiler_excludes || []),
|
||||||
],
|
],
|
||||||
splitting: true,
|
splitting: true,
|
||||||
// logLevel: "silent",
|
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED = true;
|
||||||
|
log.error(`SSR Bundler Error: ${error}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ export default async function pagesSSRContextBundler(params?: Params) {
|
|||||||
const pages = grabAllPages();
|
const pages = grabAllPages();
|
||||||
const dev = isDevelopment();
|
const dev = isDevelopment();
|
||||||
|
|
||||||
if (global.SSR_BUNDLER_CTX) {
|
if (global.BUNEXT_SSR_BUNDLER_CTX) {
|
||||||
await global.SSR_BUNDLER_CTX.dispose();
|
await global.BUNEXT_SSR_BUNDLER_CTX.dispose();
|
||||||
global.SSR_BUNDLER_CTX = undefined;
|
global.BUNEXT_SSR_BUNDLER_CTX = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const entryToPage = new Map<string, PageFiles & { tsx: string }>();
|
const entryToPage = new Map<string, PageFiles & { tsx: string }>();
|
||||||
@@ -46,7 +46,7 @@ export default async function pagesSSRContextBundler(params?: Params) {
|
|||||||
|
|
||||||
const entryPoints = [...entryToPage.keys()].map((e) => `ssr-virtual:${e}`);
|
const entryPoints = [...entryToPage.keys()].map((e) => `ssr-virtual:${e}`);
|
||||||
|
|
||||||
global.SSR_BUNDLER_CTX = await esbuild.context({
|
global.BUNEXT_SSR_BUNDLER_CTX = await esbuild.context({
|
||||||
entryPoints,
|
entryPoints,
|
||||||
outdir: BUNX_CWD_MODULE_CACHE_DIR,
|
outdir: BUNX_CWD_MODULE_CACHE_DIR,
|
||||||
bundle: true,
|
bundle: true,
|
||||||
@@ -82,5 +82,5 @@ export default async function pagesSSRContextBundler(params?: Params) {
|
|||||||
// logLevel: "silent",
|
// logLevel: "silent",
|
||||||
});
|
});
|
||||||
|
|
||||||
await global.SSR_BUNDLER_CTX.rebuild();
|
await global.BUNEXT_SSR_BUNDLER_CTX.rebuild();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const BunSkipNonBrowserPlugin: Bun.BunPlugin = {
|
|||||||
// const skipped_modules = new Set<string>();
|
// const skipped_modules = new Set<string>();
|
||||||
|
|
||||||
build.onResolve({ filter: skipFilter }, (args) => {
|
build.onResolve({ filter: skipFilter }, (args) => {
|
||||||
global.SKIPPED_BROWSER_MODULES.add(args.path);
|
global.BUNEXT_SKIPPED_BROWSER_MODULES.add(args.path);
|
||||||
return {
|
return {
|
||||||
path: args.path,
|
path: args.path,
|
||||||
namespace: "skipped",
|
namespace: "skipped",
|
||||||
@@ -18,8 +18,8 @@ const BunSkipNonBrowserPlugin: Bun.BunPlugin = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// build.onEnd(() => {
|
// build.onEnd(() => {
|
||||||
// log.warn(`global.SKIPPED_BROWSER_MODULES`, [
|
// log.warn(`global.BUNEXT_SKIPPED_BROWSER_MODULES`, [
|
||||||
// ...global.SKIPPED_BROWSER_MODULES,
|
// ...global.BUNEXT_SKIPPED_BROWSER_MODULES,
|
||||||
// ]);
|
// ]);
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import cleanupLogsDirs from "../../cleanup-logs-dir";
|
|||||||
const { BUNX_BUNDLER_ERROR_EXIT_FILE, BUNX_ERROR_LOGS_DIR } = grabDirNames();
|
const { BUNX_BUNDLER_ERROR_EXIT_FILE, BUNX_ERROR_LOGS_DIR } = grabDirNames();
|
||||||
|
|
||||||
let build_start = 0;
|
let build_start = 0;
|
||||||
let build_starts = 0;
|
|
||||||
const MAX_BUILD_STARTS = 2;
|
const MAX_BUILD_STARTS = 2;
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
@@ -25,17 +24,19 @@ type Params = {
|
|||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
post_build_fn?: (params: { artifacts: any[] }) => Promise<void> | void;
|
post_build_fn?: (params: { artifacts: any[] }) => Promise<void> | void;
|
||||||
|
build_only?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function esbuildCTXArtifactTracker({
|
export default function esbuildCTXArtifactTracker({
|
||||||
entryToPage,
|
entryToPage,
|
||||||
post_build_fn,
|
post_build_fn,
|
||||||
|
build_only,
|
||||||
}: Params) {
|
}: Params) {
|
||||||
const artifactTracker: Plugin = {
|
const artifactTracker: Plugin = {
|
||||||
name: "artifact-tracker",
|
name: "artifact-tracker",
|
||||||
setup(build) {
|
setup(build) {
|
||||||
build.onStart(async () => {
|
build.onStart(async () => {
|
||||||
build_starts++;
|
global.BUNEXT_MAIN_CTX_BUILD_STARTS++;
|
||||||
build_start = performance.now();
|
build_start = performance.now();
|
||||||
|
|
||||||
const does_error_file_exist = existsSync(
|
const does_error_file_exist = existsSync(
|
||||||
@@ -43,32 +44,37 @@ export default function esbuildCTXArtifactTracker({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
build_starts >= MAX_BUILD_STARTS &&
|
global.BUNEXT_MAIN_CTX_BUILD_STARTS >= MAX_BUILD_STARTS &&
|
||||||
!does_error_file_exist
|
!does_error_file_exist
|
||||||
) {
|
) {
|
||||||
await buildOnstartErrorHandler();
|
await buildOnstartErrorHandler();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
build.onEnd((result) => {
|
build.onEnd(async (result) => {
|
||||||
if (result.errors.length > 0) {
|
if (result.errors.length > 0) {
|
||||||
global.RECOMPILING = false;
|
global.BUNEXT_RECOMPILING = false;
|
||||||
global.IS_SERVER_COMPONENT = false;
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||||
build_starts = 0;
|
|
||||||
|
|
||||||
log.error(`Build errors:`);
|
log.error(`Build errors:`);
|
||||||
for (const err of result.errors) {
|
for (const err of result.errors) {
|
||||||
log.error(` ${err.text}${err.location ? ` (${err.location.file}:${err.location.line}:${err.location.column})` : ""}`);
|
log.error(
|
||||||
|
` ${err.text}${err.location ? ` (${err.location.file}:${err.location.line}:${err.location.column})` : ""}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = global.HMR_CONTROLLERS.length - 1; i >= 0; i--) {
|
for (
|
||||||
const controller = global.HMR_CONTROLLERS[i];
|
let i = global.BUNEXT_HMR_CONTROLLERS.length - 1;
|
||||||
|
i >= 0;
|
||||||
|
i--
|
||||||
|
) {
|
||||||
|
const controller = global.BUNEXT_HMR_CONTROLLERS[i];
|
||||||
try {
|
try {
|
||||||
controller?.controller?.enqueue(
|
controller?.controller?.enqueue(
|
||||||
`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`,
|
`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`,
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
global.HMR_CONTROLLERS.splice(i, 1);
|
global.BUNEXT_HMR_CONTROLLERS.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,31 +89,39 @@ export default function esbuildCTXArtifactTracker({
|
|||||||
if (artifacts?.[0] && artifacts.length > 0) {
|
if (artifacts?.[0] && artifacts.length > 0) {
|
||||||
for (let i = 0; i < artifacts.length; i++) {
|
for (let i = 0; i < artifacts.length; i++) {
|
||||||
const artifact = artifacts[i];
|
const artifact = artifacts[i];
|
||||||
if (artifact?.local_path && global.BUNDLER_CTX_MAP) {
|
if (
|
||||||
global.BUNDLER_CTX_MAP[artifact.local_path] =
|
artifact?.local_path &&
|
||||||
|
global.BUNEXT_BUNDLER_CTX_MAP
|
||||||
|
) {
|
||||||
|
global.BUNEXT_BUNDLER_CTX_MAP[artifact.local_path] =
|
||||||
_.merge(
|
_.merge(
|
||||||
global.BUNDLER_CTX_MAP[artifact.local_path],
|
global.BUNEXT_BUNDLER_CTX_MAP[
|
||||||
|
artifact.local_path
|
||||||
|
],
|
||||||
artifact,
|
artifact,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
post_build_fn?.({ artifacts });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const elapsed = (performance.now() - build_start).toFixed(0);
|
const elapsed = (performance.now() - build_start).toFixed(0);
|
||||||
log.success(`[Built] in ${elapsed}ms`);
|
log.success(`[Built] in ${elapsed}ms`);
|
||||||
|
|
||||||
global.RECOMPILING = false;
|
global.BUNEXT_MAIN_CTX_BUILD_STARTS = 0;
|
||||||
global.IS_SERVER_COMPONENT = false;
|
global.BUNEXT_BUNDLER_CTX_DISPOSED = false;
|
||||||
|
|
||||||
build_starts = 0;
|
|
||||||
|
|
||||||
const does_error_file_exist = existsSync(
|
const does_error_file_exist = existsSync(
|
||||||
BUNX_BUNDLER_ERROR_EXIT_FILE,
|
BUNX_BUNDLER_ERROR_EXIT_FILE,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (does_error_file_exist) {
|
// SSR must finish before HMR so server props are fresh
|
||||||
|
if (build_only) {
|
||||||
|
try {
|
||||||
|
await pagesSSRBundler();
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`SSR Bundler Error: ${error}`);
|
||||||
|
}
|
||||||
|
} else if (does_error_file_exist) {
|
||||||
mkdirSync(BUNX_ERROR_LOGS_DIR, { recursive: true });
|
mkdirSync(BUNX_ERROR_LOGS_DIR, { recursive: true });
|
||||||
cpSync(
|
cpSync(
|
||||||
BUNX_BUNDLER_ERROR_EXIT_FILE,
|
BUNX_BUNDLER_ERROR_EXIT_FILE,
|
||||||
@@ -115,20 +129,25 @@ export default function esbuildCTXArtifactTracker({
|
|||||||
);
|
);
|
||||||
rmSync(BUNX_BUNDLER_ERROR_EXIT_FILE, { force: true });
|
rmSync(BUNX_BUNDLER_ERROR_EXIT_FILE, { force: true });
|
||||||
cleanupLogsDirs();
|
cleanupLogsDirs();
|
||||||
fullRebuild();
|
await fullRebuild();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
pagesSSRBundler();
|
await pagesSSRBundler();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(`SSR Bundler Error: ${error}`);
|
log.error(`SSR Bundler Error: ${error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (artifacts?.[0] && artifacts.length > 0) {
|
||||||
|
try {
|
||||||
|
await post_build_fn?.({ artifacts });
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Post-build Error: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (global.SSR_BUNDLER_CTX) {
|
global.BUNEXT_RECOMPILING = false;
|
||||||
// global.SSR_BUNDLER_CTX.rebuild();
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||||
// } else {
|
|
||||||
// pagesSSRContextBundler();
|
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,17 +32,25 @@ export default function ssrCTXArtifactTracker({
|
|||||||
build_starts++;
|
build_starts++;
|
||||||
build_start = performance.now();
|
build_start = performance.now();
|
||||||
if (build_starts == MAX_BUILD_STARTS) {
|
if (build_starts == MAX_BUILD_STARTS) {
|
||||||
global.SSR_BUNDLER_CTX_DISPOSED = true;
|
global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED = true;
|
||||||
await global.SSR_BUNDLER_CTX?.dispose();
|
await global.BUNEXT_SSR_BUNDLER_CTX?.dispose();
|
||||||
global.SSR_BUNDLER_CTX = undefined;
|
global.BUNEXT_SSR_BUNDLER_CTX = undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
build.onEnd((result) => {
|
build.onEnd(async (result) => {
|
||||||
if (result.errors.length > 0) {
|
if (result.errors.length > 0) {
|
||||||
global.SSR_BUNDLER_CTX_DISPOSED = false;
|
global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED = true;
|
||||||
|
try {
|
||||||
|
await global.BUNEXT_SSR_BUNDLER_CTX?.dispose();
|
||||||
|
} catch {}
|
||||||
|
global.BUNEXT_SSR_BUNDLER_CTX = undefined;
|
||||||
build_starts = 0;
|
build_starts = 0;
|
||||||
console.log("SSR Build errors:", result.errors);
|
for (const err of result.errors) {
|
||||||
|
console.error(
|
||||||
|
`SSR Build error: ${err.text}${err.location ? ` (${err.location.file}:${err.location.line}:${err.location.column})` : ""}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,10 +65,11 @@ export default function ssrCTXArtifactTracker({
|
|||||||
const artifact = artifacts[i];
|
const artifact = artifacts[i];
|
||||||
if (
|
if (
|
||||||
artifact?.local_path &&
|
artifact?.local_path &&
|
||||||
global.SSR_BUNDLER_CTX_MAP
|
global.BUNEXT_SSR_BUNDLER_CTX_MAP
|
||||||
) {
|
) {
|
||||||
global.SSR_BUNDLER_CTX_MAP[artifact.local_path] =
|
global.BUNEXT_SSR_BUNDLER_CTX_MAP[
|
||||||
artifact;
|
artifact.local_path
|
||||||
|
] = artifact;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,11 +84,15 @@ export default function ssrCTXArtifactTracker({
|
|||||||
try {
|
try {
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
path.join(BUNX_TMP_DIR, "ctx-map.json"),
|
path.join(BUNX_TMP_DIR, "ctx-map.json"),
|
||||||
JSON.stringify(global.SSR_BUNDLER_CTX_MAP, null, 4),
|
JSON.stringify(
|
||||||
|
global.BUNEXT_SSR_BUNDLER_CTX_MAP,
|
||||||
|
null,
|
||||||
|
4,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
|
|
||||||
global.SSR_BUNDLER_CTX_DISPOSED = false;
|
global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { Plugin } from "esbuild";
|
import type { Plugin } from "esbuild";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import type { PageFiles } from "../../../types";
|
import type { PageFiles } from "../../../types";
|
||||||
import { log } from "../../../utils/log";
|
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
entryToPage: Map<
|
entryToPage: Map<
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export default async function reactModulesBundler() {
|
|||||||
|
|
||||||
const PUBLIC_ROOT = BUNEXT_VENDOR_DIR.replace(BUNX_CWD_DIR, "/.bunext");
|
const PUBLIC_ROOT = BUNEXT_VENDOR_DIR.replace(BUNX_CWD_DIR, "/.bunext");
|
||||||
|
|
||||||
global.REACT_IMPORTS_MAP = {
|
global.BUNEXT_REACT_IMPORTS_MAP = {
|
||||||
imports: {
|
imports: {
|
||||||
react: `${PUBLIC_ROOT}/react.js`,
|
react: `${PUBLIC_ROOT}/react.js`,
|
||||||
"react-dom": `${PUBLIC_ROOT}/react-dom.js`,
|
"react-dom": `${PUBLIC_ROOT}/react-dom.js`,
|
||||||
|
|||||||
@@ -21,8 +21,11 @@ export default async function recordArtifacts({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global.BUNDLER_CTX_MAP) {
|
if (global.BUNEXT_BUNDLER_CTX_MAP) {
|
||||||
global.BUNDLER_CTX_MAP = _.merge(global.BUNDLER_CTX_MAP, artifacts_map);
|
global.BUNEXT_BUNDLER_CTX_MAP = _.merge(
|
||||||
|
global.BUNEXT_BUNDLER_CTX_MAP,
|
||||||
|
artifacts_map,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// await Bun.write(
|
// await Bun.write(
|
||||||
|
|||||||
@@ -5,76 +5,81 @@ import type {
|
|||||||
PageFiles,
|
PageFiles,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import type { FileSystemRouter, Server } from "bun";
|
import type { FileSystemRouter, Server } from "bun";
|
||||||
import grabDirNames from "../utils/grab-dir-names";
|
import grabDirNames, { type DirNames } from "../utils/grab-dir-names";
|
||||||
import { type FSWatcher } from "fs";
|
|
||||||
import init from "./init";
|
import init from "./init";
|
||||||
import isDevelopment from "../utils/is-development";
|
import isDevelopment from "../utils/is-development";
|
||||||
import { log } from "../utils/log";
|
import { log } from "../utils/log";
|
||||||
import cron from "./server/cron";
|
import cron from "./server/cron";
|
||||||
import type { BuildContext } from "esbuild";
|
import type { BuildContext } from "esbuild";
|
||||||
import watcherEsbuildCTX from "./server/watcher-esbuild-ctx";
|
|
||||||
import allPagesESBuildContextBundler from "./bundler/all-pages-esbuild-context-bundler";
|
import allPagesESBuildContextBundler from "./bundler/all-pages-esbuild-context-bundler";
|
||||||
import serverPostBuildFn from "./server/server-post-build-fn";
|
import serverPostBuildFn from "./server/server-post-build-fn";
|
||||||
import reactModulesBundler from "./bundler/react-modules-bundler";
|
import reactModulesBundler from "./bundler/react-modules-bundler";
|
||||||
import grabConstants from "../utils/grab-constants";
|
import grabConstants from "../utils/grab-constants";
|
||||||
|
import watcherEsbuildCTX from "./server/watcher-esbuild-ctx";
|
||||||
|
import type { FSWatcher } from "fs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* # Declare Global Variables
|
* # Declare Global Variables
|
||||||
*/
|
*/
|
||||||
declare global {
|
declare global {
|
||||||
var CONFIG: BunextConfig;
|
var BUNEXT_CONFIG: BunextConfig;
|
||||||
var SERVER: Server<any> | undefined;
|
var BUNEXT_SERVER: Server<any> | undefined;
|
||||||
var RECOMPILING: boolean;
|
var BUNEXT_RECOMPILING: boolean;
|
||||||
var BUILDING_SSR: boolean;
|
var BUNEXT_BUILDING_SSR: boolean;
|
||||||
var IS_SERVER_COMPONENT: boolean;
|
var BUNEXT_IS_SERVER_COMPONENT: boolean;
|
||||||
var WATCHER_TIMEOUT: any;
|
var BUNEXT_WATCHER_TIMEOUT: any;
|
||||||
var ROUTER: FileSystemRouter;
|
var BUNEXT_ROUTER: FileSystemRouter;
|
||||||
var HMR_CONTROLLERS: GlobalHMRControllerObject[];
|
var BUNEXT_HMR_CONTROLLERS: GlobalHMRControllerObject[];
|
||||||
var LAST_BUILD_TIME: number;
|
var BUNEXT_LAST_BUILD_TIME: number;
|
||||||
var BUNDLER_CTX_MAP: { [k: string]: BundlerCTXMap };
|
var BUNEXT_BUNDLER_CTX_MAP: { [k: string]: BundlerCTXMap };
|
||||||
var SSR_BUNDLER_CTX_MAP: { [k: string]: BundlerCTXMap };
|
var BUNEXT_SSR_BUNDLER_CTX_MAP: { [k: string]: BundlerCTXMap };
|
||||||
// var API_ROUTES_BUNDLER_CTX_MAP: { [k: string]: BundlerCTXMap };
|
// var BUNEXT_API_ROUTES_BUNDLER_CTX_MAP: { [k: string]: BundlerCTXMap };
|
||||||
var BUNDLER_REBUILDS: 0;
|
var BUNEXT_BUNDLER_REBUILDS: 0;
|
||||||
var PAGES_SRC_WATCHER: FSWatcher | undefined;
|
var BUNEXT_PAGES_SRC_WATCHER: FSWatcher | undefined;
|
||||||
var CURRENT_VERSION: string | undefined;
|
var BUNEXT_CURRENT_VERSION: string | undefined;
|
||||||
var PAGE_FILES: PageFiles[];
|
var BUNEXT_PAGE_FILES: PageFiles[];
|
||||||
var ROOT_FILE_UPDATED: boolean;
|
var BUNEXT_ROOT_FILE_UPDATED: boolean;
|
||||||
var SKIPPED_BROWSER_MODULES: Set<string>;
|
var BUNEXT_SKIPPED_BROWSER_MODULES: Set<string>;
|
||||||
var BUNDLER_CTX: BuildContext | undefined;
|
var BUNEXT_BUNDLER_CTX: BuildContext | undefined;
|
||||||
var SSR_BUNDLER_CTX: BuildContext | undefined;
|
var BUNEXT_SSR_BUNDLER_CTX: BuildContext | undefined;
|
||||||
// var API_ROUTES_BUNDLER_CTX: BuildContext | undefined;
|
// var BUNEXT_API_ROUTES_BUNDLER_CTX: BuildContext | undefined;
|
||||||
var DIR_NAMES: ReturnType<typeof grabDirNames>;
|
var BUNEXT_DIR_NAMES: DirNames;
|
||||||
var REACT_IMPORTS_MAP: { imports: Record<string, string> };
|
var BUNEXT_REACT_IMPORTS_MAP: { imports: Record<string, string> };
|
||||||
var REACT_DOM_SERVER: any;
|
var BUNEXT_REACT_DOM_SERVER: any;
|
||||||
var REACT_DOM_MODULE_CACHE: Map<string, { main: any; css: string }>;
|
var BUNEXT_REACT_DOM_MODULE_CACHE: Map<string, { main: any; css: string }>;
|
||||||
var BUNDLER_CTX_DISPOSED: boolean | undefined;
|
var BUNEXT_BUNDLER_CTX_DISPOSED: boolean | undefined;
|
||||||
var SSR_BUNDLER_CTX_DISPOSED: boolean | undefined;
|
var BUNEXT_SSR_BUNDLER_CTX_DISPOSED: boolean | undefined;
|
||||||
var REBUILD_RETRIES: number;
|
var BUNEXT_REBUILD_RETRIES: number;
|
||||||
var IS_404_PAGE: boolean;
|
var BUNEXT_IS_404_PAGE: boolean;
|
||||||
var CONSTANTS: ReturnType<typeof grabConstants>;
|
var BUNEXT_CONSTANTS: ReturnType<typeof grabConstants>;
|
||||||
|
var BUNEXT_MAIN_CTX_BUILD_STARTS: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dirNames = grabDirNames();
|
const dirNames = grabDirNames();
|
||||||
const { PAGES_DIR } = dirNames;
|
const { PAGES_DIR } = dirNames;
|
||||||
|
|
||||||
export default async function bunextInit() {
|
type Params = {
|
||||||
global.HMR_CONTROLLERS = [];
|
build_only?: boolean;
|
||||||
global.BUNDLER_CTX_MAP = {};
|
};
|
||||||
global.SSR_BUNDLER_CTX_MAP = {};
|
|
||||||
// global.API_ROUTES_BUNDLER_CTX_MAP = {};
|
|
||||||
global.BUNDLER_REBUILDS = 0;
|
|
||||||
global.REBUILD_RETRIES = 0;
|
|
||||||
global.PAGE_FILES = [];
|
|
||||||
global.SKIPPED_BROWSER_MODULES = new Set<string>();
|
|
||||||
global.DIR_NAMES = dirNames;
|
|
||||||
global.REACT_IMPORTS_MAP = { imports: {} };
|
|
||||||
global.REACT_DOM_MODULE_CACHE = new Map<string, any>();
|
|
||||||
|
|
||||||
log.banner();
|
export default async function bunextInit(params?: Params) {
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS = [];
|
||||||
|
global.BUNEXT_BUNDLER_CTX_MAP = {};
|
||||||
|
global.BUNEXT_SSR_BUNDLER_CTX_MAP = {};
|
||||||
|
// global.BUNEXT_API_ROUTES_BUNDLER_CTX_MAP = {};
|
||||||
|
global.BUNEXT_BUNDLER_REBUILDS = 0;
|
||||||
|
global.BUNEXT_REBUILD_RETRIES = 0;
|
||||||
|
global.BUNEXT_PAGE_FILES = [];
|
||||||
|
global.BUNEXT_SKIPPED_BROWSER_MODULES = new Set<string>();
|
||||||
|
global.BUNEXT_DIR_NAMES = dirNames;
|
||||||
|
global.BUNEXT_REACT_IMPORTS_MAP = { imports: {} };
|
||||||
|
global.BUNEXT_REACT_DOM_MODULE_CACHE = new Map<string, any>();
|
||||||
|
global.BUNEXT_MAIN_CTX_BUILD_STARTS = 0;
|
||||||
|
|
||||||
await init();
|
await init();
|
||||||
|
log.banner();
|
||||||
|
|
||||||
global.CONSTANTS = grabConstants();
|
global.BUNEXT_CONSTANTS = grabConstants();
|
||||||
|
|
||||||
await reactModulesBundler();
|
await reactModulesBundler();
|
||||||
|
|
||||||
@@ -83,21 +88,24 @@ export default async function bunextInit() {
|
|||||||
dir: PAGES_DIR,
|
dir: PAGES_DIR,
|
||||||
});
|
});
|
||||||
|
|
||||||
global.ROUTER = router;
|
global.BUNEXT_ROUTER = router;
|
||||||
|
|
||||||
const is_dev = isDevelopment();
|
const is_dev = isDevelopment();
|
||||||
|
|
||||||
if (is_dev) {
|
if (params?.build_only) {
|
||||||
|
log.build(`Building Modules ...`);
|
||||||
|
await allPagesESBuildContextBundler();
|
||||||
|
} else if (is_dev) {
|
||||||
log.build(`Building Modules ...`);
|
log.build(`Building Modules ...`);
|
||||||
await allPagesESBuildContextBundler({
|
await allPagesESBuildContextBundler({
|
||||||
post_build_fn: () => {
|
post_build_fn: async () => {
|
||||||
serverPostBuildFn();
|
await serverPostBuildFn();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
watcherEsbuildCTX();
|
watcherEsbuildCTX();
|
||||||
} else {
|
} else {
|
||||||
log.build(`Building Modules ...`);
|
log.build(`Building Modules ...`);
|
||||||
await allPagesESBuildContextBundler();
|
await allPagesESBuildContextBundler({ start: true });
|
||||||
cron();
|
cron();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -17,6 +17,11 @@ export default async function trimAllCache() {
|
|||||||
const trim_key = await trimCacheKey({
|
const trim_key = await trimCacheKey({
|
||||||
key: cache_key,
|
key: cache_key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (trim_key.success) {
|
||||||
|
cached_items.splice(i, 1);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ export default async function trimCacheKey({
|
|||||||
key,
|
key,
|
||||||
});
|
});
|
||||||
|
|
||||||
const config = global.CONFIG;
|
const config = global.BUNEXT_CONFIG;
|
||||||
|
|
||||||
const default_expiry_time_seconds =
|
const default_expiry_time_seconds =
|
||||||
config.default_cache_expiry ||
|
config.default_cache_expiry ||
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default async function () {
|
|||||||
|
|
||||||
const current_version = package_json.version;
|
const current_version = package_json.version;
|
||||||
|
|
||||||
global.CURRENT_VERSION = current_version;
|
global.BUNEXT_CURRENT_VERSION = current_version;
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
|
|
||||||
const keys = Object.keys(dirNames) as (keyof ReturnType<
|
const keys = Object.keys(dirNames) as (keyof ReturnType<
|
||||||
@@ -65,7 +65,7 @@ export default async function () {
|
|||||||
|
|
||||||
const config: BunextConfig = (await grabConfig()) || {};
|
const config: BunextConfig = (await grabConfig()) || {};
|
||||||
|
|
||||||
global.CONFIG = {
|
global.BUNEXT_CONFIG = {
|
||||||
...config,
|
...config,
|
||||||
development: is_dev,
|
development: is_dev,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import handleBunextPublicAssets from "./handle-bunext-public-assets";
|
|||||||
import checkExcludedPatterns from "../../utils/check-excluded-patterns";
|
import checkExcludedPatterns from "../../utils/check-excluded-patterns";
|
||||||
import { AppData } from "../../data/app-data";
|
import { AppData } from "../../data/app-data";
|
||||||
import fullRebuild from "./full-rebuild";
|
import fullRebuild from "./full-rebuild";
|
||||||
|
|
||||||
|
const HMR_RETRY_COOLDOWN_MS = 5000;
|
||||||
|
let lastHmrRetryTime = 0;
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
req: Request;
|
req: Request;
|
||||||
server: Bun.Server<any>;
|
server: Bun.Server<any>;
|
||||||
@@ -29,8 +33,9 @@ export default async function bunextRequestHandler({
|
|||||||
|
|
||||||
let response: Response | undefined = undefined;
|
let response: Response | undefined = undefined;
|
||||||
|
|
||||||
if (global.CONSTANTS.config?.middleware) {
|
if (global.BUNEXT_CONSTANTS.config?.middleware) {
|
||||||
const middleware_res = await global.CONSTANTS.config.middleware({
|
const middleware_res =
|
||||||
|
await global.BUNEXT_CONSTANTS.config.middleware({
|
||||||
req: initial_req,
|
req: initial_req,
|
||||||
url,
|
url,
|
||||||
});
|
});
|
||||||
@@ -45,6 +50,11 @@ export default async function bunextRequestHandler({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_dev && url.pathname == AppData["BunextHMRRetryRoute"]) {
|
if (is_dev && url.pathname == AppData["BunextHMRRetryRoute"]) {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastHmrRetryTime < HMR_RETRY_COOLDOWN_MS) {
|
||||||
|
return new Response("Too Many Requests", { status: 429 });
|
||||||
|
}
|
||||||
|
lastHmrRetryTime = now;
|
||||||
await fullRebuild({ msg: `HMR Retry Rebuild ...` });
|
await fullRebuild({ msg: `HMR Retry Rebuild ...` });
|
||||||
return new Response("Modules Rebuilt");
|
return new Response("Modules Rebuilt");
|
||||||
}
|
}
|
||||||
@@ -76,8 +86,12 @@ export default async function bunextRequestHandler({
|
|||||||
|
|
||||||
return response;
|
return response;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
if (is_dev) {
|
||||||
return new Response(`Server Error: ${error.message}`, {
|
return new Response(`Server Error: ${error.message}`, {
|
||||||
status: 500,
|
status: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
console.error(`Server Error: ${error.message}`, error);
|
||||||
|
return new Response("Internal Server Error", { status: 500 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import chokidar from "chokidar";
|
||||||
|
import path from "path";
|
||||||
|
import { existsSync } from "fs";
|
||||||
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
|
import fullRebuild from "./full-rebuild";
|
||||||
|
import { AppData } from "../../data/app-data";
|
||||||
|
import checkExcludedPatterns from "../../utils/check-excluded-patterns";
|
||||||
|
import pagesSSRBundler from "../bundler/pages-ssr-bundler";
|
||||||
|
import { log } from "../../utils/log";
|
||||||
|
|
||||||
|
const { ROOT_DIR, BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames();
|
||||||
|
|
||||||
|
export default async function chokadirWatcherEsbuildCTX() {
|
||||||
|
const watcher = chokidar.watch(ROOT_DIR, {
|
||||||
|
ignored: [
|
||||||
|
/(^|[\/\\])\../,
|
||||||
|
/node_modules/,
|
||||||
|
/public/,
|
||||||
|
/\.bunext/,
|
||||||
|
/\.git/,
|
||||||
|
/dist/,
|
||||||
|
/bun\.lockb/,
|
||||||
|
(path: string) => path.endsWith(AppData["BunextTmpFileExt"]),
|
||||||
|
],
|
||||||
|
persistent: true,
|
||||||
|
ignoreInitial: true,
|
||||||
|
depth: 99,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleEvent = async (
|
||||||
|
event: "add" | "change" | "unlink" | "addDir" | "unlinkDir",
|
||||||
|
filePath: string,
|
||||||
|
) => {
|
||||||
|
let owns_recompile = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const filename = path.relative(ROOT_DIR, filePath);
|
||||||
|
|
||||||
|
if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) {
|
||||||
|
await fullRebuild();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global.BUNEXT_BUNDLER_CTX_DISPOSED) {
|
||||||
|
await fullRebuild({ msg: `Restarting Bundler ...` });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED) {
|
||||||
|
await pagesSSRBundler().catch((error) => {
|
||||||
|
log.error(`SSR Bundler Error: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename.match(/\/styles$/) || filename === "styles") {
|
||||||
|
owns_recompile = true;
|
||||||
|
global.BUNEXT_RECOMPILING = true;
|
||||||
|
await Bun.sleep(1000);
|
||||||
|
await fullRebuild({
|
||||||
|
msg: `Detected new \`styles\` directory. Rebuilding ...`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename.match(/bunext.config\.ts/)) {
|
||||||
|
await fullRebuild({
|
||||||
|
msg: `bunext.config.ts file changed. Rebuilding server ...`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target_files_match = /\.(tsx?|jsx?|css)$/;
|
||||||
|
|
||||||
|
if (event === "change") {
|
||||||
|
if (filename.match(target_files_match)) {
|
||||||
|
if (global.BUNEXT_RECOMPILING) return;
|
||||||
|
owns_recompile = true;
|
||||||
|
global.BUNEXT_RECOMPILING = true;
|
||||||
|
|
||||||
|
if (filename.match(/.*\.server\.tsx?/)) {
|
||||||
|
global.BUNEXT_IS_SERVER_COMPONENT = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global.BUNEXT_BUNDLER_CTX) {
|
||||||
|
await global.BUNEXT_BUNDLER_CTX.rebuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename.match(/(404|500)\.tsx?/)) {
|
||||||
|
for (
|
||||||
|
let i = global.BUNEXT_HMR_CONTROLLERS.length - 1;
|
||||||
|
i >= 0;
|
||||||
|
i--
|
||||||
|
) {
|
||||||
|
const controller = global.BUNEXT_HMR_CONTROLLERS[i];
|
||||||
|
try {
|
||||||
|
controller?.controller?.enqueue(
|
||||||
|
`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`,
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (["add", "unlink", "addDir", "unlinkDir"].includes(event)) {
|
||||||
|
const is_file_of_interest =
|
||||||
|
!!filename.match(target_files_match) ||
|
||||||
|
event.includes("Dir");
|
||||||
|
|
||||||
|
if (!is_file_of_interest) return;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!filename.match(/^src\/pages\/|\.css$/) ||
|
||||||
|
checkExcludedPatterns({ path: filename }) ||
|
||||||
|
filename.includes(" ")
|
||||||
|
) {
|
||||||
|
return reloadWatcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global.BUNEXT_RECOMPILING) return;
|
||||||
|
|
||||||
|
owns_recompile = true;
|
||||||
|
const action = event.startsWith("add") ? "created" : "deleted";
|
||||||
|
const type = filename.match(/\.css$/)
|
||||||
|
? "Stylesheet"
|
||||||
|
: event.includes("Dir")
|
||||||
|
? "Directory"
|
||||||
|
: filename.match(/\/pages\/api\//)
|
||||||
|
? "API Route"
|
||||||
|
: "Page";
|
||||||
|
|
||||||
|
await fullRebuild({
|
||||||
|
msg: `${type} ${action}: ${filename}. Rebuilding ...`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Watcher rebuild failed: ${error}`);
|
||||||
|
} finally {
|
||||||
|
if (owns_recompile) {
|
||||||
|
global.BUNEXT_RECOMPILING = false;
|
||||||
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watcher
|
||||||
|
.on("add", (path) => handleEvent("add", path))
|
||||||
|
.on("change", (path) => handleEvent("change", path))
|
||||||
|
.on("unlink", (path) => handleEvent("unlink", path))
|
||||||
|
.on("addDir", (path) => handleEvent("addDir", path))
|
||||||
|
.on("unlinkDir", (path) => handleEvent("unlinkDir", path));
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadWatcher() {
|
||||||
|
if (global.BUNEXT_PAGES_SRC_WATCHER) {
|
||||||
|
global.BUNEXT_PAGES_SRC_WATCHER.close();
|
||||||
|
chokadirWatcherEsbuildCTX();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { log } from "../../utils/log";
|
import { log } from "../../utils/log";
|
||||||
import allPagesESBuildContextBundler from "../bundler/all-pages-esbuild-context-bundler";
|
import allPagesESBuildContextBundler from "../bundler/all-pages-esbuild-context-bundler";
|
||||||
import pagesSSRBundler from "../bundler/pages-ssr-bundler";
|
|
||||||
import serverPostBuildFn from "./server-post-build-fn";
|
import serverPostBuildFn from "./server-post-build-fn";
|
||||||
import watcherEsbuildCTX from "./watcher-esbuild-ctx";
|
import watcherEsbuildCTX from "./watcher-esbuild-ctx";
|
||||||
|
|
||||||
@@ -8,35 +7,36 @@ export default async function fullRebuild(params?: { msg?: string }) {
|
|||||||
try {
|
try {
|
||||||
const { msg } = params || {};
|
const { msg } = params || {};
|
||||||
|
|
||||||
global.RECOMPILING = true;
|
global.BUNEXT_RECOMPILING = true;
|
||||||
|
|
||||||
if (msg) {
|
if (msg) {
|
||||||
log.watch(msg);
|
log.watch(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.ROUTER.reload();
|
global.BUNEXT_ROUTER.reload();
|
||||||
|
|
||||||
await global.BUNDLER_CTX?.dispose();
|
try {
|
||||||
global.BUNDLER_CTX = undefined;
|
await global.BUNEXT_BUNDLER_CTX?.dispose();
|
||||||
|
global.BUNEXT_BUNDLER_CTX = undefined;
|
||||||
|
|
||||||
await global.SSR_BUNDLER_CTX?.dispose();
|
await global.BUNEXT_SSR_BUNDLER_CTX?.dispose();
|
||||||
global.SSR_BUNDLER_CTX = undefined;
|
global.BUNEXT_SSR_BUNDLER_CTX = undefined;
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
await pagesSSRBundler();
|
await allPagesESBuildContextBundler({
|
||||||
|
post_build_fn: async () => {
|
||||||
allPagesESBuildContextBundler({
|
await serverPostBuildFn();
|
||||||
post_build_fn: () => {
|
|
||||||
serverPostBuildFn();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
global.RECOMPILING = false;
|
|
||||||
global.IS_SERVER_COMPONENT = false;
|
|
||||||
log.error(error);
|
log.error(error);
|
||||||
|
} finally {
|
||||||
|
global.BUNEXT_RECOMPILING = false;
|
||||||
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global.PAGES_SRC_WATCHER) {
|
if (global.BUNEXT_PAGES_SRC_WATCHER) {
|
||||||
global.PAGES_SRC_WATCHER.close();
|
global.BUNEXT_PAGES_SRC_WATCHER.close();
|
||||||
watcherEsbuildCTX();
|
watcherEsbuildCTX();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import grabDirNames from "../../utils/grab-dir-names";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import isDevelopment from "../../utils/is-development";
|
import isDevelopment from "../../utils/is-development";
|
||||||
import { readFileResponse } from "./handle-public";
|
import { readFileResponse } from "./handle-public";
|
||||||
|
import isSafePath from "../../utils/is-safe-path";
|
||||||
|
|
||||||
const { BUNEXT_PUBLIC_DIR } = grabDirNames();
|
const { BUNEXT_PUBLIC_DIR } = grabDirNames();
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ export default async function ({ req }: Params): Promise<Response> {
|
|||||||
url.pathname.replace(/\/\.bunext\/public\//, ""),
|
url.pathname.replace(/\/\.bunext\/public\//, ""),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!file_path.startsWith(BUNEXT_PUBLIC_DIR + path.sep)) {
|
if (!isSafePath({ filePath: file_path, allowedDir: BUNEXT_PUBLIC_DIR })) {
|
||||||
return new Response("Forbidden", { status: 403 });
|
return new Response("Forbidden", { status: 403 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import grabDirNames from "../../utils/grab-dir-names";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import isDevelopment from "../../utils/is-development";
|
import isDevelopment from "../../utils/is-development";
|
||||||
import { existsSync } from "fs";
|
import { existsSync } from "fs";
|
||||||
|
import isSafePath from "../../utils/is-safe-path";
|
||||||
|
|
||||||
const { PUBLIC_DIR } = grabDirNames();
|
const { PUBLIC_DIR } = grabDirNames();
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ export default async function ({ req }: Params): Promise<Response> {
|
|||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
const file_path = path.join(PUBLIC_DIR, url.pathname);
|
const file_path = path.join(PUBLIC_DIR, url.pathname);
|
||||||
|
|
||||||
if (!file_path.startsWith(PUBLIC_DIR + path.sep)) {
|
if (!isSafePath({ filePath: file_path, allowedDir: PUBLIC_DIR })) {
|
||||||
return new Response("Forbidden", { status: 403 });
|
return new Response("Forbidden", { status: 403 });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +27,13 @@ export default async function ({ req }: Params): Promise<Response> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const file = Bun.file(file_path);
|
const file = Bun.file(file_path);
|
||||||
return new Response(file);
|
const headers = new Headers();
|
||||||
|
|
||||||
|
if (!is_dev) {
|
||||||
|
headers.set("Cache-Control", "public, max-age=3600");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(file, { headers });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return new Response(`File Not Found`, {
|
return new Response(`File Not Found`, {
|
||||||
status: 404,
|
status: 404,
|
||||||
|
|||||||
@@ -2,12 +2,34 @@ type Params = {
|
|||||||
req: Request;
|
req: Request;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function removeController(controller: ReadableStreamDefaultController<string>) {
|
||||||
|
const idx = global.BUNEXT_HMR_CONTROLLERS.findIndex(
|
||||||
|
(c) => c.controller == controller,
|
||||||
|
);
|
||||||
|
if (typeof idx == "number" && idx >= 0) {
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS.splice(idx, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default async function ({ req }: Params): Promise<Response> {
|
export default async function ({ req }: Params): Promise<Response> {
|
||||||
const referer_url = new URL(req.headers.get("referer") || "");
|
const referer = req.headers.get("referer");
|
||||||
const match = global.ROUTER.match(referer_url.pathname);
|
const page_cookie = req.headers.get("cookie");
|
||||||
|
|
||||||
|
if (!referer) {
|
||||||
|
return new Response("Missing Referer Header", { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
let referer_url: URL;
|
||||||
|
try {
|
||||||
|
referer_url = new URL(referer);
|
||||||
|
} catch {
|
||||||
|
return new Response("Invalid Referer Header", { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = global.BUNEXT_ROUTER.match(referer_url.pathname);
|
||||||
|
|
||||||
const target_map = match?.filePath
|
const target_map = match?.filePath
|
||||||
? global.BUNDLER_CTX_MAP?.[match.filePath]
|
? global.BUNEXT_BUNDLER_CTX_MAP?.[match.filePath]
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
let controller: ReadableStreamDefaultController<string>;
|
let controller: ReadableStreamDefaultController<string>;
|
||||||
@@ -15,31 +37,24 @@ export default async function ({ req }: Params): Promise<Response> {
|
|||||||
const stream = new ReadableStream<string>({
|
const stream = new ReadableStream<string>({
|
||||||
start(c) {
|
start(c) {
|
||||||
controller = c;
|
controller = c;
|
||||||
global.HMR_CONTROLLERS.push({
|
global.BUNEXT_HMR_CONTROLLERS.push({
|
||||||
controller: c,
|
controller: c,
|
||||||
page_url: referer_url.href,
|
page_url: referer_url.href,
|
||||||
target_map,
|
target_map,
|
||||||
|
page_cookie,
|
||||||
});
|
});
|
||||||
heartbeat = setInterval(() => {
|
heartbeat = setInterval(() => {
|
||||||
try {
|
try {
|
||||||
c.enqueue(": keep-alive\n\n");
|
c.enqueue(": keep-alive\n\n");
|
||||||
} catch {
|
} catch {
|
||||||
clearInterval(heartbeat);
|
clearInterval(heartbeat);
|
||||||
|
removeController(controller);
|
||||||
}
|
}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
},
|
},
|
||||||
cancel() {
|
cancel() {
|
||||||
clearInterval(heartbeat);
|
clearInterval(heartbeat);
|
||||||
const targetControllerIndex = global.HMR_CONTROLLERS.findIndex(
|
removeController(controller);
|
||||||
(c) => c.controller == controller,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
typeof targetControllerIndex == "number" &&
|
|
||||||
targetControllerIndex >= 0
|
|
||||||
) {
|
|
||||||
global.HMR_CONTROLLERS.splice(targetControllerIndex, 1);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import grabDirNames from "../../utils/grab-dir-names";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import isDevelopment from "../../utils/is-development";
|
import isDevelopment from "../../utils/is-development";
|
||||||
import { existsSync } from "fs";
|
import { existsSync } from "fs";
|
||||||
|
import isSafePath from "../../utils/is-safe-path";
|
||||||
|
|
||||||
const { PUBLIC_DIR } = grabDirNames();
|
const { PUBLIC_DIR } = grabDirNames();
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ export default async function ({ req }: Params): Promise<Response> {
|
|||||||
url.pathname.replace(/^\/public/, ""),
|
url.pathname.replace(/^\/public/, ""),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!file_path.startsWith(PUBLIC_DIR + path.sep)) {
|
if (!isSafePath({ filePath: file_path, allowedDir: PUBLIC_DIR })) {
|
||||||
return new Response("Forbidden", { status: 403 });
|
return new Response("Forbidden", { status: 403 });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +54,8 @@ export function readFileResponse({ file_path, cache }: FileResponse) {
|
|||||||
headers.set("Cache-Control", "public, max-age=31536000, immutable");
|
headers.set("Cache-Control", "public, max-age=31536000, immutable");
|
||||||
} else if (cache?.duration) {
|
} else if (cache?.duration) {
|
||||||
headers.set("Cache-Control", `public, max-age=${cache.duration}`);
|
headers.set("Cache-Control", `public, max-age=${cache.duration}`);
|
||||||
|
} else if (!isDevelopment()) {
|
||||||
|
headers.set("Cache-Control", "public, max-age=3600");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Response(file, {
|
return new Response(file, {
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ export default async function ({ req }: Params): Promise<Response> {
|
|||||||
let module: any;
|
let module: any;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
if (is_dev && global.SSR_BUNDLER_CTX_MAP?.[match.filePath]?.path) {
|
if (is_dev && global.BUNEXT_SSR_BUNDLER_CTX_MAP?.[match.filePath]?.path) {
|
||||||
const target_import = path.join(
|
const target_import = path.join(
|
||||||
ROOT_DIR,
|
ROOT_DIR,
|
||||||
global.SSR_BUNDLER_CTX_MAP[match.filePath].path,
|
global.BUNEXT_SSR_BUNDLER_CTX_MAP[match.filePath].path,
|
||||||
);
|
);
|
||||||
|
|
||||||
module = await import(`${target_import}?t=${now}`);
|
module = await import(`${target_import}?t=${now}`);
|
||||||
@@ -68,16 +68,16 @@ export default async function ({ req }: Params): Promise<Response> {
|
|||||||
|
|
||||||
const config = module.config as BunextServerRouteConfig | undefined;
|
const config = module.config as BunextServerRouteConfig | undefined;
|
||||||
|
|
||||||
|
const maxBodyBytes = config?.max_request_body_mb
|
||||||
|
? config.max_request_body_mb * MBInBytes
|
||||||
|
: ServerDefaultRequestBodyLimitBytes;
|
||||||
|
|
||||||
const contentLength = req.headers.get("content-length");
|
const contentLength = req.headers.get("content-length");
|
||||||
|
|
||||||
if (contentLength) {
|
if (contentLength) {
|
||||||
const size = parseInt(contentLength, 10);
|
const size = parseInt(contentLength, 10);
|
||||||
|
|
||||||
if (
|
if (size > maxBodyBytes) {
|
||||||
(config?.max_request_body_mb &&
|
|
||||||
size > config.max_request_body_mb * MBInBytes) ||
|
|
||||||
size > ServerDefaultRequestBodyLimitBytes
|
|
||||||
) {
|
|
||||||
return Response.json(
|
return Response.json(
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
@@ -91,6 +91,24 @@ export default async function ({ req }: Params): Promise<Response> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (req.method !== "GET" && req.method !== "HEAD") {
|
||||||
|
const body = await req.arrayBuffer();
|
||||||
|
if (body.byteLength > maxBodyBytes) {
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
msg: "Request Body Too Large!",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 413,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
routeParams.body = JSON.parse(new TextDecoder().decode(body) || "{}");
|
||||||
}
|
}
|
||||||
|
|
||||||
const target_module = (module["default"] ||
|
const target_module = (module["default"] ||
|
||||||
|
|||||||
@@ -7,61 +7,64 @@ type Params = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function serverPostBuildFn(params?: Params) {
|
export default async function serverPostBuildFn(params?: Params) {
|
||||||
if (!global.HMR_CONTROLLERS?.[0] || !global.BUNDLER_CTX_MAP) {
|
if (!global.BUNEXT_HMR_CONTROLLERS?.[0] || !global.BUNEXT_BUNDLER_CTX_MAP) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reload_payload = { reload: true };
|
const reload_payload = { reload: true };
|
||||||
const reload_enqueue = `event: update\ndata: ${JSON.stringify(reload_payload)}\n\n`;
|
const reload_enqueue = `event: update\ndata: ${JSON.stringify(reload_payload)}\n\n`;
|
||||||
|
|
||||||
for (let i = global.HMR_CONTROLLERS.length - 1; i >= 0; i--) {
|
for (let i = global.BUNEXT_HMR_CONTROLLERS.length - 1; i >= 0; i--) {
|
||||||
const controller = global.HMR_CONTROLLERS[i];
|
const controller = global.BUNEXT_HMR_CONTROLLERS[i];
|
||||||
|
|
||||||
if (!controller) {
|
if (!controller) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!controller.target_map?.local_path) {
|
if (!controller.target_map?.local_path) {
|
||||||
// if (global.IS_404_PAGE) {
|
|
||||||
// controller.controller.enqueue(reload_enqueue);
|
|
||||||
// }
|
|
||||||
// if (!global.HMR_CONTROLLERS[i].page_reloaded) {
|
|
||||||
// controller.controller.enqueue(reload_enqueue);
|
|
||||||
// global.HMR_CONTROLLERS[i].page_reloaded = true;
|
|
||||||
// }
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params?.reload_all_controllers) {
|
if (params?.reload_all_controllers) {
|
||||||
|
try {
|
||||||
controller.controller.enqueue(reload_enqueue);
|
controller.controller.enqueue(reload_enqueue);
|
||||||
|
} catch {
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS.splice(i, 1);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target_artifact =
|
const target_artifact =
|
||||||
global.BUNDLER_CTX_MAP[controller.target_map.local_path];
|
global.BUNEXT_BUNDLER_CTX_MAP[controller.target_map.local_path];
|
||||||
|
|
||||||
if (!target_artifact.local_path) {
|
if (!target_artifact?.local_path) {
|
||||||
|
try {
|
||||||
controller.controller.enqueue(reload_enqueue);
|
controller.controller.enqueue(reload_enqueue);
|
||||||
|
} catch {
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS.splice(i, 1);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mock_req = target_artifact.req
|
const mock_req = target_artifact.req_url
|
||||||
? target_artifact.req.clone()
|
? new Request(target_artifact.req_url, {})
|
||||||
: new Request(controller.page_url);
|
: new Request(controller.page_url);
|
||||||
|
|
||||||
const page_component = global.IS_SERVER_COMPONENT
|
if (controller.page_cookie) {
|
||||||
? await grabPageComponent({
|
mock_req.headers.set("cookie", controller.page_cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
const page_component = await grabPageComponent({
|
||||||
req: mock_req,
|
req: mock_req,
|
||||||
return_server_res_only: true,
|
return_server_res_only: true,
|
||||||
is_hydration: true,
|
is_hydration: true,
|
||||||
})
|
});
|
||||||
: {};
|
|
||||||
|
|
||||||
if (page_component instanceof Response) {
|
if (page_component instanceof Response) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { serverRes } = page_component;
|
const { serverRes } = page_component || {};
|
||||||
|
|
||||||
const final_artifact: Omit<GlobalHMRControllerObject, "controller"> = {
|
const final_artifact: Omit<GlobalHMRControllerObject, "controller"> = {
|
||||||
..._.omit(controller, ["controller"]),
|
..._.omit(controller, ["controller"]),
|
||||||
@@ -72,14 +75,13 @@ export default async function serverPostBuildFn(params?: Params) {
|
|||||||
delete final_artifact.target_map;
|
delete final_artifact.target_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverRes) {
|
// Always replace so prior error props cannot linger
|
||||||
final_artifact.page_props = serverRes;
|
final_artifact.page_props = serverRes || {};
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let final_data: { [k: string]: any } = {};
|
let final_data: { [k: string]: any } = {};
|
||||||
|
|
||||||
if (global.ROOT_FILE_UPDATED) {
|
if (global.BUNEXT_ROOT_FILE_UPDATED) {
|
||||||
final_data = reload_payload;
|
final_data = reload_payload;
|
||||||
} else {
|
} else {
|
||||||
final_data = final_artifact;
|
final_data = final_artifact;
|
||||||
@@ -89,9 +91,9 @@ export default async function serverPostBuildFn(params?: Params) {
|
|||||||
`event: update\ndata: ${JSON.stringify(final_data)}\n\n`,
|
`event: update\ndata: ${JSON.stringify(final_data)}\n\n`,
|
||||||
);
|
);
|
||||||
|
|
||||||
global.ROOT_FILE_UPDATED = false;
|
global.BUNEXT_ROOT_FILE_UPDATED = false;
|
||||||
} catch {
|
} catch {
|
||||||
global.HMR_CONTROLLERS.splice(i, 1);
|
global.BUNEXT_HMR_CONTROLLERS.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,27 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { log } from "../../utils/log";
|
import { log } from "../../utils/log";
|
||||||
import serverParamsGen from "./server-params-gen";
|
import serverParamsGen from "./server-params-gen";
|
||||||
|
import isDevelopment from "../../utils/is-development";
|
||||||
|
import watcherEsbuildCTX from "./watcher-esbuild-ctx";
|
||||||
|
|
||||||
export default async function startServer() {
|
export default async function startServer() {
|
||||||
const serverParams = await serverParamsGen();
|
const serverParams = await serverParamsGen();
|
||||||
|
|
||||||
const server = Bun.serve(serverParams);
|
const server = Bun.serve(serverParams);
|
||||||
|
const is_dev = isDevelopment();
|
||||||
|
|
||||||
global.SERVER = server;
|
global.BUNEXT_SERVER = server;
|
||||||
|
|
||||||
log.server(`http://${server.hostname}:${server.port}`);
|
log.server(`http://${server.hostname}:${server.port}`);
|
||||||
|
|
||||||
|
if (is_dev) {
|
||||||
|
setInterval(() => {
|
||||||
|
if (global.BUNEXT_PAGES_SRC_WATCHER) {
|
||||||
|
global.BUNEXT_PAGES_SRC_WATCHER.close();
|
||||||
|
watcherEsbuildCTX();
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { watch, existsSync, statSync } from "fs";
|
import { watch, existsSync, statSync, glob } from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import grabDirNames from "../../utils/grab-dir-names";
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
import fullRebuild from "./full-rebuild";
|
import fullRebuild from "./full-rebuild";
|
||||||
import { AppData } from "../../data/app-data";
|
import { AppData } from "../../data/app-data";
|
||||||
import checkExcludedPatterns from "../../utils/check-excluded-patterns";
|
import checkExcludedPatterns from "../../utils/check-excluded-patterns";
|
||||||
import pagesSSRBundler from "../bundler/pages-ssr-bundler";
|
import pagesSSRBundler from "../bundler/pages-ssr-bundler";
|
||||||
|
import { log } from "../../utils/log";
|
||||||
|
|
||||||
const { ROOT_DIR, BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames();
|
const { ROOT_DIR, BUNX_BUNDLER_ERROR_EXIT_FILE } = grabDirNames();
|
||||||
|
|
||||||
@@ -16,7 +17,39 @@ export default async function watcherEsbuildCTX() {
|
|||||||
persistent: true,
|
persistent: true,
|
||||||
},
|
},
|
||||||
async (event, filename) => {
|
async (event, filename) => {
|
||||||
|
let owns_recompile = false;
|
||||||
|
|
||||||
|
try {
|
||||||
if (!filename) return;
|
if (!filename) return;
|
||||||
|
const full_file_path = path.join(ROOT_DIR, filename);
|
||||||
|
|
||||||
|
if (global.BUNEXT_CONFIG.exclude_watch_patterns) {
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < global.BUNEXT_CONFIG.exclude_watch_patterns.length;
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
const watch_pattern =
|
||||||
|
global.BUNEXT_CONFIG.exclude_watch_patterns[i];
|
||||||
|
|
||||||
|
if (watch_pattern instanceof RegExp) {
|
||||||
|
const is_path_excluded =
|
||||||
|
watch_pattern.test(filename);
|
||||||
|
if (is_path_excluded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const excluded_path = path.resolve(
|
||||||
|
ROOT_DIR,
|
||||||
|
watch_pattern,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (excluded_path == full_file_path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) {
|
if (existsSync(BUNX_BUNDLER_ERROR_EXIT_FILE)) {
|
||||||
await fullRebuild();
|
await fullRebuild();
|
||||||
@@ -27,27 +60,29 @@ export default async function watcherEsbuildCTX() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global.BUNDLER_CTX_DISPOSED) {
|
if (global.BUNEXT_BUNDLER_CTX_DISPOSED) {
|
||||||
await fullRebuild({ msg: `Restarting Bundler ...` });
|
await fullRebuild({ msg: `Restarting Bundler ...` });
|
||||||
global.BUNDLER_CTX_DISPOSED = false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global.SSR_BUNDLER_CTX_DISPOSED) {
|
if (global.BUNEXT_SSR_BUNDLER_CTX_DISPOSED) {
|
||||||
pagesSSRBundler();
|
await pagesSSRBundler().catch((error) => {
|
||||||
|
log.error(`SSR Bundler Error: ${error}`);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filename.endsWith(AppData["BunextTmpFileExt"])) {
|
if (filename.endsWith(AppData["BunextTmpFileExt"])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const full_file_path = path.join(ROOT_DIR, filename);
|
|
||||||
const does_file_exist = existsSync(full_file_path);
|
const does_file_exist = existsSync(full_file_path);
|
||||||
const file_stat = does_file_exist
|
const file_stat = does_file_exist
|
||||||
? statSync(full_file_path)
|
? statSync(full_file_path)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (full_file_path.match(/\/styles$/)) {
|
if (full_file_path.match(/\/styles$/)) {
|
||||||
global.RECOMPILING = true;
|
owns_recompile = true;
|
||||||
|
global.BUNEXT_RECOMPILING = true;
|
||||||
await Bun.sleep(1000);
|
await Bun.sleep(1000);
|
||||||
await fullRebuild({
|
await fullRebuild({
|
||||||
msg: `Detected new \`styles\` directory. Rebuilding ...`,
|
msg: `Detected new \`styles\` directory. Rebuilding ...`,
|
||||||
@@ -68,35 +103,37 @@ export default async function watcherEsbuildCTX() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const target_files_match = /\.(tsx?|jsx?|css)$/;
|
const target_files_match = /\.(tsx?|jsx?|css)$/;
|
||||||
// const rebuild_skip_paths = /\/pages\/api\//;
|
|
||||||
|
|
||||||
if (event !== "rename") {
|
if (event !== "rename") {
|
||||||
if (filename.match(target_files_match)) {
|
if (filename.match(target_files_match)) {
|
||||||
if (global.RECOMPILING) return;
|
if (global.BUNEXT_RECOMPILING) return;
|
||||||
global.RECOMPILING = true;
|
owns_recompile = true;
|
||||||
|
global.BUNEXT_RECOMPILING = true;
|
||||||
|
|
||||||
if (filename.match(/.*\.server\.tsx?/)) {
|
if (filename.match(/.*\.server\.tsx?/)) {
|
||||||
global.IS_SERVER_COMPONENT = true;
|
global.BUNEXT_IS_SERVER_COMPONENT = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global.BUNDLER_CTX) {
|
if (global.BUNEXT_BUNDLER_CTX) {
|
||||||
try {
|
await global.BUNEXT_BUNDLER_CTX.rebuild();
|
||||||
await global.BUNDLER_CTX.rebuild();
|
|
||||||
} catch (error) {
|
|
||||||
console.log(`ESBUILD Rebuild Error =>`, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filename.match(/(404|500)\.tsx?/)) {
|
if (filename.match(/(404|500)\.tsx?/)) {
|
||||||
for (
|
for (
|
||||||
let i = global.HMR_CONTROLLERS.length - 1;
|
let i =
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS.length - 1;
|
||||||
i >= 0;
|
i >= 0;
|
||||||
i--
|
i--
|
||||||
) {
|
) {
|
||||||
const controller = global.HMR_CONTROLLERS[i];
|
const controller =
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS[i];
|
||||||
|
try {
|
||||||
controller?.controller?.enqueue(
|
controller?.controller?.enqueue(
|
||||||
`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`,
|
`event: update\ndata: ${JSON.stringify({ reload: true })}\n\n`,
|
||||||
);
|
);
|
||||||
|
} catch {
|
||||||
|
global.BUNEXT_HMR_CONTROLLERS.splice(i, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,13 +148,15 @@ export default async function watcherEsbuildCTX() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!filename.match(/^src\/pages\/|\.css$/)) return reloadWatcher();
|
if (!filename.match(/^src\/pages\/|\.css$/))
|
||||||
|
return reloadWatcher();
|
||||||
if (checkExcludedPatterns({ path: filename }))
|
if (checkExcludedPatterns({ path: filename }))
|
||||||
return reloadWatcher();
|
return reloadWatcher();
|
||||||
if (filename.match(/ /)) return reloadWatcher();
|
if (filename.match(/ /)) return reloadWatcher();
|
||||||
|
|
||||||
if (global.RECOMPILING) return;
|
if (global.BUNEXT_RECOMPILING) return;
|
||||||
|
|
||||||
|
owns_recompile = true;
|
||||||
const action = does_file_exist ? "created" : "deleted";
|
const action = does_file_exist ? "created" : "deleted";
|
||||||
const type = filename.match(/\.css$/)
|
const type = filename.match(/\.css$/)
|
||||||
? "Sylesheet"
|
? "Sylesheet"
|
||||||
@@ -130,15 +169,23 @@ export default async function watcherEsbuildCTX() {
|
|||||||
await fullRebuild({
|
await fullRebuild({
|
||||||
msg: `${type} ${action}: ${filename}. Rebuilding ...`,
|
msg: `${type} ${action}: ${filename}. Rebuilding ...`,
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Watcher rebuild failed: ${error}`);
|
||||||
|
} finally {
|
||||||
|
if (owns_recompile) {
|
||||||
|
global.BUNEXT_RECOMPILING = false;
|
||||||
|
global.BUNEXT_IS_SERVER_COMPONENT = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
global.PAGES_SRC_WATCHER = pages_src_watcher;
|
global.BUNEXT_PAGES_SRC_WATCHER = pages_src_watcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadWatcher() {
|
function reloadWatcher() {
|
||||||
if (global.PAGES_SRC_WATCHER) {
|
if (global.BUNEXT_PAGES_SRC_WATCHER) {
|
||||||
global.PAGES_SRC_WATCHER.close();
|
global.BUNEXT_PAGES_SRC_WATCHER.close();
|
||||||
watcherEsbuildCTX();
|
watcherEsbuildCTX();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,12 +70,18 @@ export default async function genWebHTML({
|
|||||||
|
|
||||||
const final_meta = _.merge(root_meta, page_meta);
|
const final_meta = _.merge(root_meta, page_meta);
|
||||||
|
|
||||||
// const public_envs = Object.keys(process.env).filter((e) =>
|
const public_envs = Object.fromEntries(
|
||||||
// e.startsWith(`NEXT_PUBLIC_`),
|
Object.entries(process.env).filter(([k]) =>
|
||||||
// );
|
k.startsWith("BUNEXT_PUBLIC_"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const client_process = {
|
const client_process = {
|
||||||
env: {},
|
env: {
|
||||||
|
NODE_ENV: dev ? "development" : "production",
|
||||||
|
...public_envs,
|
||||||
|
...global.BUNEXT_CONFIG.public_envs,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let final_component = (
|
let final_component = (
|
||||||
@@ -116,7 +122,7 @@ export default async function genWebHTML({
|
|||||||
type="importmap"
|
type="importmap"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: JSON.stringify(
|
__html: JSON.stringify(
|
||||||
global.REACT_IMPORTS_MAP,
|
global.BUNEXT_REACT_IMPORTS_MAP,
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
defer
|
defer
|
||||||
@@ -178,6 +184,8 @@ export default async function genWebHTML({
|
|||||||
console.info = () => {};
|
console.info = () => {};
|
||||||
console.debug = () => {};
|
console.debug = () => {};
|
||||||
|
|
||||||
|
let htmlBody: string;
|
||||||
|
try {
|
||||||
const stream = await renderToReadableStream(final_component, {
|
const stream = await renderToReadableStream(final_component, {
|
||||||
onError(error: any) {
|
onError(error: any) {
|
||||||
if (error.message.includes('unique "key" prop')) return;
|
if (error.message.includes('unique "key" prop')) return;
|
||||||
@@ -185,9 +193,10 @@ export default async function genWebHTML({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const htmlBody = await new Response(stream).text();
|
htmlBody = await new Response(stream).text();
|
||||||
|
} finally {
|
||||||
Object.assign(console, originalConsole);
|
Object.assign(console, originalConsole);
|
||||||
|
}
|
||||||
|
|
||||||
html += htmlBody;
|
html += htmlBody;
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default async function grabFilePathModule<T extends any = any>({
|
|||||||
outfile: target_cache_file_path,
|
outfile: target_cache_file_path,
|
||||||
});
|
});
|
||||||
|
|
||||||
Loader.registry.delete(target_cache_file_path);
|
// Loader.registry.delete(target_cache_file_path);
|
||||||
const module = await import(`${target_cache_file_path}?t=${Date.now()}`);
|
const module = await import(`${target_cache_file_path}?t=${Date.now()}`);
|
||||||
|
|
||||||
return module as T;
|
return module as T;
|
||||||
|
|||||||
@@ -19,10 +19,13 @@ export default async function grabPageBundledReactComponent({
|
|||||||
return_tsx_only,
|
return_tsx_only,
|
||||||
}: Params): Promise<GrabPageReactBundledComponentRes | undefined> {
|
}: Params): Promise<GrabPageReactBundledComponentRes | undefined> {
|
||||||
try {
|
try {
|
||||||
if (global.SSR_BUNDLER_CTX_MAP?.[file_path]) {
|
if (global.BUNEXT_SSR_BUNDLER_CTX_MAP?.[file_path]) {
|
||||||
const mod = await import(
|
const abs = path.join(
|
||||||
path.join(ROOT_DIR, global.SSR_BUNDLER_CTX_MAP[file_path].path)
|
ROOT_DIR,
|
||||||
|
global.BUNEXT_SSR_BUNDLER_CTX_MAP[file_path].path,
|
||||||
);
|
);
|
||||||
|
// Loader.registry.delete(abs);
|
||||||
|
const mod = await import(`${abs}?t=${Date.now()}`);
|
||||||
|
|
||||||
const Main = mod.default as FC;
|
const Main = mod.default as FC;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user