dev: add main inject plugin

This commit is contained in:
dswbx
2025-12-05 12:48:32 +01:00
parent 6bbc922710
commit f5f591fc4a
9 changed files with 173 additions and 57 deletions
+1
View File
@@ -21,6 +21,7 @@ const result = await Bun.build({
outdir: "./dist/cli",
env: "PUBLIC_*",
minify: true,
banner: `const __originalLog=console.log;console.log=(...o)=>{const n=o[0];"string"==typeof n&&n.includes("[dotenv@")||__originalLog.apply(console,o)};`,
external,
define: {
__isDev: "0",
+1 -1
View File
@@ -143,7 +143,7 @@
"vite-tsconfig-paths": "^5.1.4",
"vitest": "3.0.9",
"wouter": "^3.7.1",
"wrangler": "^4.45.4"
"wrangler": "^4.52.1"
},
"optionalDependencies": {
"@hono/node-server": "^1.19.6"
+12 -2
View File
@@ -1,17 +1,27 @@
import type { CliCommand } from "cli/types";
import { Option } from "commander";
import { withConfigOptions, type WithConfigOptions } from "cli/utils/options";
import * as utils from "./utils";
import { $console } from "core/utils";
import { Project } from "./lib/Project";
export const dev: CliCommand = (program) =>
withConfigOptions(program.command("dev")).description("dev server").action(action);
withConfigOptions(program.command("dev"))
.description("dev server")
.addOption(new Option("--build", "build the project"))
.action(action);
async function action(options: WithConfigOptions<{}>) {
async function action(options: WithConfigOptions<{ build?: boolean }>) {
console.log("options", options);
const project = new Project({
templatePath: utils.TEMPLATE_PATH,
});
await project.init();
if (options.build) {
await project.build();
process.exit(0);
}
await project.listen();
}
+21 -9
View File
@@ -1,6 +1,6 @@
import { RelativeFS } from "./RelativeFS";
//import { $console } from "bknd/utils";
import { type ViteDevServer, createServer } from "vite";
import { type ViteDevServer, createServer, type UserConfig, createBuilder } from "vite";
import { readdir, copyFile, stat } from "node:fs/promises";
import path from "node:path";
import react from "@vitejs/plugin-react";
@@ -64,10 +64,11 @@ export class Project {
};
await copyRecursive(source, destination);
this._server = await createServer(this.getViteConfig());
}
async listen() {
this._server = await createServer({
private getViteConfig(): UserConfig {
return {
clearScreen: false,
publicDir: getRelativeDistPath() + "/static",
plugins: [
@@ -81,16 +82,27 @@ export class Project {
},
}),
injectMain({
mainPath: "/.bknd/main.tsx",
appPath: "./src/App.tsx",
rootId: "root",
}),
],
build: {},
build: {
minify: true,
},
resolve: {
dedupe: ["react", "react-dom"],
},
});
await this._server.listen();
this._server.printUrls();
this._server.bindCLIShortcuts({ print: true });
};
}
async build() {
const builder = await createBuilder(this.getViteConfig());
await builder.buildApp();
}
async listen() {
await this.server.listen();
this.server.printUrls();
this.server.bindCLIShortcuts({ print: true });
}
}
@@ -1,22 +1,60 @@
import type { PluginOption } from "vite";
export function injectMain({ mainPath }: { mainPath: string }): PluginOption {
return {
name: "inject-main-script",
transformIndexHtml: {
order: "pre", // run before other transforms
handler(html) {
export function injectMain(options?: { appPath?: string; rootId?: string }): PluginOption {
const appPath = options?.appPath ?? "/App.tsx";
const rootId = options?.rootId ?? "root";
const publicId = "/@virtual/react-main.js";
const internalId = "\0virtual-react-main.js";
return [
{
name: "bknd-virtual-react-entry",
transformIndexHtml(html) {
return {
html,
tags: [
{
tag: "script",
attrs: { type: "module", src: mainPath },
injectTo: "body",
attrs: {
type: "module",
src: publicId,
},
},
],
};
},
},
};
resolveId(id) {
if (id === publicId) {
return internalId;
}
return null;
},
load(id) {
if (id === internalId) {
return `
import React from "react";
import ReactDOM from "react-dom/client";
import App from "${appPath}";
import { ClientProvider } from "bknd/client";
const container = document.getElementById("${rootId}");
if (!container) {
throw new Error("Cannot find #${rootId} element");
}
const root = ReactDOM.createRoot(container);
root.render(
React.createElement(
React.StrictMode,
null,
React.createElement(ClientProvider, null, React.createElement(App, null))
)
);
`;
}
return null;
},
},
];
}
@@ -1,14 +0,0 @@
/// <reference types="vite/client" />
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { ClientProvider } from "bknd/client";
import App from "../src/App.tsx";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ClientProvider>
<App />
</ClientProvider>
</StrictMode>,
);
@@ -8,7 +8,7 @@
},
"assets": {
"binding": "ASSETS",
"directory": "./dist/client",
"directory": "../dist/client",
"not_found_handling": "single-page-application",
"run_worker_first": ["!/", "/admin*", "/api*", "!/assets/*"]
},
-1
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env node
import { Command } from "commander";
import color from "picocolors";
import * as commands from "./commands";