dinou 7.1.0 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [7.3.0]
9
+
10
+ ### Added
11
+ - **Universal Setup Files & Environment Polyfills**:
12
+ - Early-stage execution guarantee: Setup scripts are injected at Line 1 of all compilation bundles before any module imports or engine code run, enabling browser/canvas polyfills (`DOMMatrix`, `ImageData`, `Path2D`) and APM/telemetry agents without ejecting.
13
+ - Unified configuration hierarchy: `plugin.setupFiles` (plugins run first) -> explicit `setupFiles: [...]` in `dinou.config.*` (strict source of truth; replaces convention) -> zero-config root convention `./dinou.setup.(mjs|js|ts)`.
14
+ - Comprehensive multi-platform support: Injected uniformly across Node.js (`Pass A`, `Pass B`, `Pass C`), Bun native AOT, Cloudflare Workers, Deno / Deno Deploy, Static Site Generation (SSG), and Hot-Reload Dev Server.
15
+ - Added TypeScript definitions for `setupFiles?: string[]` in both `DinouConfig` and `DinouPlugin`.
16
+
17
+ ### Fixed
18
+ - **Bun Standalone Compilation Port Collision (`EADDRINUSE`)**:
19
+ - Resolved port binding conflict when compiling the Node.js orchestrator into a standalone executable using `bun build --compile`.
20
+ - Separated exports in `.dinou/node/server.mjs`: `fetch` is now exported exclusively as a named export (`export { fetch }`), while `export default` exposes `{ port, startServer }`, preventing Bun's runtime from launching a duplicate background `Bun.serve` instance over Node's `http.createServer()`.
21
+ - Robust `isMain` execution detection supporting both Node.js (`process.argv[1]`) and compiled Bun binaries (`import.meta.main`).
22
+
23
+ ## [7.2.0]
24
+
25
+ ### Added
26
+ - **Plugin Hook `configureServer`**: Attach WebSockets (Socket.IO, WS) and native Node.js HTTP middlewares before server startup without ejecting.
27
+ - **Node Standalone Binary Compilation**: Added `dinou build:node:compile` and cross-compilation matrix (`:linux`, `:linux_arm`, `:mac`, `:win`).
28
+ - **Client-Side Server Function Auth Hooks**: Added `window.__DINOU_SERVER_FUNCTION_HEADERS__` and `window.__DINOU_GET_AUTH_TOKEN__` to inject dynamic JWT/Bearer authorization headers into `"use server"` calls.
29
+ - **Esbuild-Powered Config Loader**: `dinou.config.*` is automatically pre-bundled with Esbuild, enabling native `tsconfig.json` path aliases (`@/*`) and TypeScript configuration files out of the box.
30
+
31
+ ### Changed
32
+ - Removed legacy `babel-esm-loader.js` and cleaned up residual loader registrations.
33
+
8
34
  ## [7.1.0] - 2026-10-16
9
35
 
10
36
  ### Added
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # **Dinou**
2
2
 
3
- [![Documentation](https://img.shields.io/badge/docs-dinou.dev-blue?style=flat-square)](https://dinou.dev) [![Version](https://img.shields.io/badge/version-7.0.0-orange?style=flat-square)](https://www.npmjs.com/package/dinou)
3
+ [![Documentation](https://img.shields.io/badge/docs-dinou.dev-blue?style=flat-square)](https://dinou.dev) [![Version](https://img.shields.io/badge/version-7.3.0-orange?style=flat-square)](https://www.npmjs.com/package/dinou)
4
4
 
5
5
  ### **Full-Stack React 19 Framework**
6
6
 
package/cli.js CHANGED
@@ -121,6 +121,91 @@ program
121
121
  runCommand(`cross-env NODE_ENV=production DINOU_BUILD_TOOL=webpack node ${p("adapters/node.mjs")}`);
122
122
  });
123
123
 
124
+ // --- NODE (COMPILE VIA BUN) ---
125
+
126
+ const nodeTargets = [
127
+ { name: "", flags: "", outfile: "dist/server" },
128
+ { name: ":linux", flags: "--target=bun-linux-x64 ", outfile: "dist/server-linux" },
129
+ { name: ":linux_arm", flags: "--target=bun-linux-arm64 ", outfile: "dist/server-linux-arm" },
130
+ { name: ":mac", flags: "--target=bun-darwin-arm64 ", outfile: "dist/server-mac" },
131
+ { name: ":win", flags: "--target=bun-windows-x64 ", outfile: "dist/server-win" },
132
+ ];
133
+
134
+ const runNodeBuild = (bundler) => {
135
+ const bundlerCmd = bundler === "rollup"
136
+ ? `cross-env NODE_ENV=production npx rollup -c ${p("rollup/rollup.config.js")}`
137
+ : bundler === "webpack"
138
+ ? `cross-env NODE_ENV=production npx webpack --config ${p("webpack/webpack.config.js")}`
139
+ : `cross-env NODE_ENV=production node ${p("esbuild/build.mjs")}`;
140
+
141
+ const envPrefix = bundler === "webpack" ? "cross-env DINOU_BUILD_TOOL=webpack " : "";
142
+ return `${bundlerCmd} && ${envPrefix}node ${p("node/build.mjs")}`;
143
+ };
144
+
145
+ for (const t of nodeTargets) {
146
+ for (const b of ["esbuild", "rollup", "webpack"]) {
147
+ const cmdNames = b === "esbuild"
148
+ ? [`build:node:compile${t.name}`, `build:node:compile${t.name}:esbuild`]
149
+ : [`build:node:compile${t.name}:${b}`];
150
+ for (const cmdName of cmdNames) {
151
+ program
152
+ .command(cmdName)
153
+ .description(`Compile standalone Node.js server executable with Bun (${t.name || "host"}, ${b})`)
154
+ .action(() => {
155
+ runCommand(`${runNodeBuild(b)} && bun build --compile ${t.flags}.dinou/node/server.mjs --outfile ${t.outfile}`);
156
+ });
157
+ }
158
+ }
159
+ }
160
+
161
+ // --- NODE (PURE BUN RUNNER COMPILE) ---
162
+
163
+ const runNodePureBuild = (bundler) => {
164
+ if (bundler === "rollup") {
165
+ return `cross-env NODE_ENV=production npx rollup -c ${p("rollup/rollup.config.js")} && bun ${p("node/build.mjs")}`;
166
+ }
167
+ if (bundler === "webpack") {
168
+ return `cross-env NODE_ENV=production npx webpack --config ${p("webpack/webpack.config.js")} && cross-env DINOU_BUILD_TOOL=webpack bun ${p("node/build.mjs")}`;
169
+ }
170
+ return `cross-env NODE_ENV=production bun ${p("esbuild/build.mjs")} && bun ${p("node/build.mjs")}`;
171
+ };
172
+
173
+ program
174
+ .command("build:node:pure")
175
+ .description("Build the app for Node.js with 100% Bun (using esbuild)")
176
+ .action(() => { runCommand(runNodePureBuild("esbuild")); });
177
+
178
+ program
179
+ .command("build:node:pure:esbuild")
180
+ .description("Build the app for Node.js with 100% Bun (Esbuild)")
181
+ .action(() => { runCommand(runNodePureBuild("esbuild")); });
182
+
183
+ program
184
+ .command("build:node:pure:rollup")
185
+ .description("Build the app for Node.js with 100% Bun (Rollup)")
186
+ .action(() => { runCommand(runNodePureBuild("rollup")); });
187
+
188
+ program
189
+ .command("build:node:pure:webpack")
190
+ .description("Build the app for Node.js with 100% Bun (Webpack)")
191
+ .action(() => { runCommand(runNodePureBuild("webpack")); });
192
+
193
+ for (const t of nodeTargets) {
194
+ for (const b of ["esbuild", "rollup", "webpack"]) {
195
+ const cmdNames = b === "esbuild"
196
+ ? [`build:node:pure:compile${t.name}`, `build:node:pure:compile${t.name}:esbuild`]
197
+ : [`build:node:pure:compile${t.name}:${b}`];
198
+ for (const cmdName of cmdNames) {
199
+ program
200
+ .command(cmdName)
201
+ .description(`Compile standalone Node.js server executable with 100% Bun (${t.name || "host"}, ${b})`)
202
+ .action(() => {
203
+ runCommand(`${runNodePureBuild(b)} && bun build --compile ${t.flags}.dinou/node/server.mjs --outfile ${t.outfile}`);
204
+ });
205
+ }
206
+ }
207
+ }
208
+
124
209
  // --- CLOUDFLARE WORKERS ---
125
210
 
126
211
  program
@@ -3,21 +3,9 @@
3
3
  // Automatically routes all requests to Dinou's universal Web Standards handler.
4
4
  Object.assign(process.env, { NODE_ENV: "production" });
5
5
 
6
- import { register } from "node:module";
7
- import { pathToFileURL } from "node:url";
8
6
  import path from "node:path";
9
7
  import Module from "node:module";
10
8
 
11
- import { dinouCoreDir } from "../core/dinou-paths.js";
12
-
13
- if (typeof register === "function") {
14
- try {
15
- const loaderPath = path.join(dinouCoreDir, "babel-esm-loader.js");
16
- register(pathToFileURL(loaderPath).href, pathToFileURL("./"));
17
- } catch (e) {
18
- console.error("[Dinou Adapter] Loader registration failed:", e);
19
- }
20
- }
21
9
 
22
10
  let reactServerPath, reactDomServerPath, reactJsxRuntimePath, reactJsxDevRuntimePath;
23
11
  let roggcServerNodePath, webpackServerNodePath;
@@ -30,17 +18,17 @@ try {
30
18
 
31
19
  const reactDomPkgJson = require.resolve("react-dom/package.json");
32
20
  reactDomServerPath = path.join(path.dirname(reactDomPkgJson), "react-dom.react-server.js");
33
- } catch (e) {}
21
+ } catch (e) { }
34
22
 
35
23
  try {
36
24
  const roggcPkgJson = require.resolve("@roggc/react-server-dom-esm/package.json");
37
25
  roggcServerNodePath = path.join(path.dirname(roggcPkgJson), "server.node.js");
38
- } catch (e) {}
26
+ } catch (e) { }
39
27
 
40
28
  try {
41
29
  const webpackPkgJson = require.resolve("react-server-dom-webpack/package.json");
42
30
  webpackServerNodePath = path.join(path.dirname(webpackPkgJson), "server.node.js");
43
- } catch (e) {}
31
+ } catch (e) { }
44
32
 
45
33
  if (Module && Module._resolveFilename) {
46
34
  const originalResolveFilename = Module._resolveFilename;
@@ -19,11 +19,19 @@ const {
19
19
  isSupportedClientModule,
20
20
  scanProjectDependenciesForClientComponents,
21
21
  } = require("../core/scan-dependency-components.js");
22
+ const { getDinouConfig } = require("../core/load-dinou-config.js");
23
+ const { resolveSetupFiles } = require("../core/resolve-setup-files.js");
22
24
 
23
25
  const projectRoot = process.cwd();
24
26
  const bunDir = path.resolve(projectRoot, ".dinou/bun");
25
27
  fs.mkdirSync(bunDir, { recursive: true });
26
28
 
29
+ const dinouConfig = await getDinouConfig(projectRoot);
30
+ const setupFiles = resolveSetupFiles(projectRoot, dinouConfig);
31
+ if (setupFiles.length > 0) {
32
+ console.log(` 🔌 [Dinou Setup] Active setup files (${setupFiles.length}): ${setupFiles.map((f) => path.basename(f)).join(", ")}`);
33
+ }
34
+
27
35
  try {
28
36
  generateRouteTypes(projectRoot);
29
37
  } catch (e) {}
@@ -1012,6 +1020,7 @@ try {
1012
1020
  alias: commonAlias,
1013
1021
  loader: commonLoader,
1014
1022
  jsx: "automatic",
1023
+ inject: setupFiles.length > 0 ? setupFiles : undefined,
1015
1024
  define: {
1016
1025
  "process.env.NODE_ENV": '"production"',
1017
1026
  "process.env.DINOU_RUNTIME": '"bun-bundle"',
@@ -1035,6 +1044,7 @@ try {
1035
1044
  alias: commonAlias,
1036
1045
  loader: commonLoader,
1037
1046
  jsx: "automatic",
1047
+ inject: setupFiles.length > 0 ? setupFiles : undefined,
1038
1048
  define: {
1039
1049
  "process.env.NODE_ENV": '"production"',
1040
1050
  "process.env.DINOU_RUNTIME": '"bun-bundle"',
@@ -1056,6 +1066,7 @@ try {
1056
1066
  conditions: ["bun", "worker", "browser"],
1057
1067
  external: externalList,
1058
1068
  banner,
1069
+ inject: setupFiles.length > 0 ? setupFiles : undefined,
1059
1070
  alias: commonAlias,
1060
1071
  loader: commonLoader,
1061
1072
  define: {
@@ -19,11 +19,19 @@ const {
19
19
  isSupportedClientModule,
20
20
  scanProjectDependenciesForClientComponents,
21
21
  } = require("../core/scan-dependency-components.js");
22
+ const { getDinouConfig } = require("../core/load-dinou-config.js");
23
+ const { resolveSetupFiles } = require("../core/resolve-setup-files.js");
22
24
 
23
25
  const projectRoot = process.cwd();
24
26
  const cloudflareDir = path.resolve(projectRoot, ".dinou/cloudflare");
25
27
  fs.mkdirSync(cloudflareDir, { recursive: true });
26
28
 
29
+ const dinouConfig = await getDinouConfig(projectRoot);
30
+ const setupFiles = resolveSetupFiles(projectRoot, dinouConfig);
31
+ if (setupFiles.length > 0) {
32
+ console.log(` 🔌 [Dinou Setup] Active setup files (${setupFiles.length}): ${setupFiles.map((f) => path.basename(f)).join(", ")}`);
33
+ }
34
+
27
35
  try {
28
36
  generateRouteTypes(projectRoot);
29
37
  } catch (e) {}
@@ -819,6 +827,7 @@ const commonAlias = {
819
827
  "@": path.resolve(projectRoot, "src"),
820
828
  "dinou/config": path.resolve(dinouDir, "core/config.js"),
821
829
  dinou: dinouDir,
830
+ esbuild: path.resolve(shimsDir, "empty.js"),
822
831
  fs: path.resolve(shimsDir, "fs.js"),
823
832
  "node:fs": path.resolve(shimsDir, "fs.js"),
824
833
  "fs/promises": path.resolve(shimsDir, "fs.js"),
@@ -1056,6 +1065,7 @@ try {
1056
1065
  alias: commonAlias,
1057
1066
  loader: commonLoader,
1058
1067
  jsx: "automatic",
1068
+ inject: setupFiles.length > 0 ? setupFiles : undefined,
1059
1069
  define: {
1060
1070
  "process.env.NODE_ENV": '"production"',
1061
1071
  "process.env.DINOU_RUNTIME": '"edge"',
@@ -1079,6 +1089,7 @@ try {
1079
1089
  alias: commonAlias,
1080
1090
  loader: commonLoader,
1081
1091
  jsx: "automatic",
1092
+ inject: setupFiles.length > 0 ? setupFiles : undefined,
1082
1093
  define: {
1083
1094
  "process.env.NODE_ENV": '"production"',
1084
1095
  "process.env.DINOU_RUNTIME": '"edge"',
@@ -1100,6 +1111,7 @@ try {
1100
1111
  conditions: ["workerd", "worker", "browser"],
1101
1112
  external: externalList,
1102
1113
  banner,
1114
+ inject: setupFiles.length > 0 ? setupFiles : undefined,
1103
1115
  alias: commonAlias,
1104
1116
  loader: commonLoader,
1105
1117
  define: {
@@ -1,5 +1,5 @@
1
1
  const path = require("path");
2
- const { existsSync } = require("fs");
2
+ const { existsSync, mkdirSync } = require("fs");
3
3
  const { pathToFileURL } = require("url");
4
4
 
5
5
  let dinouConfig = { plugins: [] };
@@ -12,13 +12,45 @@ async function getDinouConfig() {
12
12
  if (dinouConfigPromise) return dinouConfigPromise;
13
13
  dinouConfigPromise = (async () => {
14
14
  const cwd = typeof process !== "undefined" && typeof process.cwd === "function" ? process.cwd() : ".";
15
- for (const filename of ["dinou.config.js", "dinou.config.mjs", "dinou.config.cjs", "dinou.config.ts"]) {
15
+ for (const filename of ["dinou.config.ts", "dinou.config.mjs", "dinou.config.js", "dinou.config.cjs"]) {
16
16
  const p = path.resolve(cwd, filename);
17
17
  if (existsSync(p)) {
18
18
  try {
19
19
  let loaded;
20
20
  const isDev = process.env.NODE_ENV !== "production";
21
- if (p.endsWith(".mjs")) {
21
+ let esbuild;
22
+ if (
23
+ typeof process !== "undefined" &&
24
+ process.versions &&
25
+ process.versions.node &&
26
+ process.env.DINOU_RUNTIME !== "edge"
27
+ ) {
28
+ try {
29
+ const req = typeof require === "function" ? require : null;
30
+ const esbuildPkg = "esbuild";
31
+ if (req) {
32
+ esbuild = req(esbuildPkg);
33
+ }
34
+ } catch (e) {}
35
+ }
36
+
37
+ if (esbuild && typeof esbuild.build === "function") {
38
+ const outDir = path.resolve(cwd, ".dinou");
39
+ if (!existsSync(outDir)) {
40
+ mkdirSync(outDir, { recursive: true });
41
+ }
42
+ const bundledPath = path.resolve(outDir, "dinou.config.bundled.mjs");
43
+ await esbuild.build({
44
+ entryPoints: [p],
45
+ outfile: bundledPath,
46
+ bundle: true,
47
+ format: "esm",
48
+ platform: "node",
49
+ packages: "external",
50
+ sourcemap: "inline",
51
+ });
52
+ loaded = await import(pathToFileURL(bundledPath).href + (isDev ? `?t=${Date.now()}` : ""));
53
+ } else if (p.endsWith(".mjs")) {
22
54
  loaded = await import(pathToFileURL(p).href + (isDev ? `?t=${Date.now()}` : ""));
23
55
  } else {
24
56
  const nodeReq =
@@ -0,0 +1,69 @@
1
+ // dinou/core/resolve-setup-files.js
2
+ const fs = require("node:fs");
3
+ const path = require("node:path");
4
+
5
+ /**
6
+ * Resolves setup files according to Dinou's configuration hierarchy:
7
+ * 1. Plugin setup files (plugin.setupFiles) always execute first.
8
+ * 2. User setup files:
9
+ * - If config.setupFiles is explicitly provided in dinou.config.mjs, it is the sole source of truth.
10
+ * - Otherwise, falls back to checking the project root for dinou.setup.mjs, dinou.setup.js, or dinou.setup.ts.
11
+ *
12
+ * @param {string} projectRoot
13
+ * @param {object} config - Dinou config object
14
+ * @returns {string[]} Array of absolute file paths to inject/execute
15
+ */
16
+ function resolveSetupFiles(projectRoot, config = {}) {
17
+ const pluginSetupFiles = [];
18
+ if (config.plugins && Array.isArray(config.plugins)) {
19
+ for (const plugin of config.plugins) {
20
+ if (plugin && Array.isArray(plugin.setupFiles)) {
21
+ for (const file of plugin.setupFiles) {
22
+ const resolved = path.isAbsolute(file)
23
+ ? file
24
+ : path.resolve(projectRoot, file);
25
+ if (fs.existsSync(resolved)) {
26
+ pluginSetupFiles.push(resolved);
27
+ } else {
28
+ console.warn(
29
+ `⚠️ [Dinou] Plugin "${plugin.name || "unnamed"}" specified setup file not found: ${file}`,
30
+ );
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+
37
+ let userSetupFiles = [];
38
+ if (Array.isArray(config.setupFiles)) {
39
+ // Explicit configuration takes precedence over convention
40
+ for (const file of config.setupFiles) {
41
+ const resolved = path.isAbsolute(file)
42
+ ? file
43
+ : path.resolve(projectRoot, file);
44
+ if (fs.existsSync(resolved)) {
45
+ userSetupFiles.push(resolved);
46
+ } else {
47
+ console.warn(`⚠️ [Dinou] Configured setupFile not found: ${file}`);
48
+ }
49
+ }
50
+ } else {
51
+ // Convention: check ONLY project root
52
+ const rootCandidates = [
53
+ "dinou.setup.mjs",
54
+ "dinou.setup.js",
55
+ "dinou.setup.ts",
56
+ ];
57
+ for (const name of rootCandidates) {
58
+ const fullPath = path.resolve(projectRoot, name);
59
+ if (fs.existsSync(fullPath)) {
60
+ userSetupFiles.push(fullPath);
61
+ break; // Only pick the highest priority extension match
62
+ }
63
+ }
64
+ }
65
+
66
+ return [...new Set([...pluginSetupFiles, ...userSetupFiles])];
67
+ }
68
+
69
+ module.exports = { resolveSetupFiles };
@@ -17,14 +17,33 @@ function executeRedirect(url) {
17
17
  function createServerFunctionProxy(id) {
18
18
  return new Proxy(() => { }, {
19
19
  apply: async (_target, _thisArg, args) => {
20
- let body;
20
+ let customHeaders = {};
21
+ if (typeof window !== "undefined") {
22
+ if (typeof window.__DINOU_SERVER_FUNCTION_HEADERS__ === "function") {
23
+ try {
24
+ customHeaders = window.__DINOU_SERVER_FUNCTION_HEADERS__() || {};
25
+ } catch (e) {
26
+ console.error("[Dinou] Error getting custom headers for server function:", e);
27
+ }
28
+ } else if (typeof window.__DINOU_GET_AUTH_TOKEN__ === "function") {
29
+ try {
30
+ const token = window.__DINOU_GET_AUTH_TOKEN__();
31
+ if (token) customHeaders = { Authorization: `Bearer ${token}` };
32
+ } catch (e) {
33
+ console.error("[Dinou] Error getting auth token for server function:", e);
34
+ }
35
+ }
36
+ }
37
+
21
38
  const headers = {
22
39
  "x-server-function-call": "1",
40
+ ...customHeaders,
23
41
  };
24
42
  if (typeof window !== "undefined" && window.location) {
25
43
  headers["x-dinou-current-path"] = window.location.pathname;
26
44
  }
27
45
 
46
+ let body;
28
47
  const formDataIndex = args.findIndex((arg) => arg instanceof FormData);
29
48
 
30
49
  if (formDataIndex !== -1) {
@@ -17,14 +17,33 @@ function executeRedirect(url) {
17
17
  export function createServerFunctionProxy(id) {
18
18
  return new Proxy(() => { }, {
19
19
  apply: async (_target, _thisArg, args) => {
20
- let body;
20
+ let customHeaders = {};
21
+ if (typeof window !== "undefined") {
22
+ if (typeof window.__DINOU_SERVER_FUNCTION_HEADERS__ === "function") {
23
+ try {
24
+ customHeaders = window.__DINOU_SERVER_FUNCTION_HEADERS__() || {};
25
+ } catch (e) {
26
+ console.error("[Dinou] Error getting custom headers for server function:", e);
27
+ }
28
+ } else if (typeof window.__DINOU_GET_AUTH_TOKEN__ === "function") {
29
+ try {
30
+ const token = window.__DINOU_GET_AUTH_TOKEN__();
31
+ if (token) customHeaders = { Authorization: `Bearer ${token}` };
32
+ } catch (e) {
33
+ console.error("[Dinou] Error getting auth token for server function:", e);
34
+ }
35
+ }
36
+ }
37
+
21
38
  const headers = {
22
39
  "x-server-function-call": "1",
40
+ ...customHeaders,
23
41
  };
24
42
  if (typeof window !== "undefined" && window.location) {
25
43
  headers["x-dinou-current-path"] = window.location.pathname;
26
44
  }
27
45
 
46
+ let body;
28
47
  const formDataIndex = args.findIndex((arg) => arg instanceof FormData);
29
48
 
30
49
  if (formDataIndex !== -1) {
@@ -19,11 +19,19 @@ const {
19
19
  isSupportedClientModule,
20
20
  scanProjectDependenciesForClientComponents,
21
21
  } = require("../core/scan-dependency-components.js");
22
+ const { getDinouConfig } = require("../core/load-dinou-config.js");
23
+ const { resolveSetupFiles } = require("../core/resolve-setup-files.js");
22
24
 
23
25
  const projectRoot = process.cwd();
24
26
  const denoDir = path.resolve(projectRoot, ".dinou/deno");
25
27
  fs.mkdirSync(denoDir, { recursive: true });
26
28
 
29
+ const dinouConfig = await getDinouConfig(projectRoot);
30
+ const setupFiles = resolveSetupFiles(projectRoot, dinouConfig);
31
+ if (setupFiles.length > 0) {
32
+ console.log(` 🔌 [Dinou Setup] Active setup files (${setupFiles.length}): ${setupFiles.map((f) => path.basename(f)).join(", ")}`);
33
+ }
34
+
27
35
  try {
28
36
  generateRouteTypes(projectRoot);
29
37
  } catch (e) {}
@@ -1046,6 +1054,7 @@ try {
1046
1054
  alias: commonAlias,
1047
1055
  loader: commonLoader,
1048
1056
  jsx: "automatic",
1057
+ inject: setupFiles.length > 0 ? setupFiles : undefined,
1049
1058
  define: {
1050
1059
  "process.env.NODE_ENV": '"production"',
1051
1060
  "process.env.DINOU_RUNTIME": '"deno-edge"',
@@ -1069,6 +1078,7 @@ try {
1069
1078
  alias: commonAlias,
1070
1079
  loader: commonLoader,
1071
1080
  jsx: "automatic",
1081
+ inject: setupFiles.length > 0 ? setupFiles : undefined,
1072
1082
  define: {
1073
1083
  "process.env.NODE_ENV": '"production"',
1074
1084
  "process.env.DINOU_RUNTIME": '"deno-edge"',
@@ -1090,6 +1100,7 @@ try {
1090
1100
  conditions: ["deno", "worker", "browser"],
1091
1101
  external: externalList,
1092
1102
  banner,
1103
+ inject: setupFiles.length > 0 ? setupFiles : undefined,
1093
1104
  alias: commonAlias,
1094
1105
  loader: commonLoader,
1095
1106
  define: {
package/dinou/index.d.ts CHANGED
@@ -120,8 +120,35 @@ export interface RequestContextStore {
120
120
  // DINOU CONFIGURATION & PLUGIN INTERFACES
121
121
  // ====================================================================
122
122
 
123
+ export type NodeMiddleware = (
124
+ req: import("node:http").IncomingMessage,
125
+ res: import("node:http").ServerResponse,
126
+ next: () => void
127
+ ) => void | Promise<void>;
128
+
129
+ export interface ServerHookContext {
130
+ /** The underlying Node.js http.Server instance */
131
+ server: import("node:http").Server;
132
+ /** Whether the server is running in development mode */
133
+ isDev: boolean;
134
+ /** Register a raw Node.js middleware (req, res, next) executed before Dinou routes */
135
+ use(middleware: NodeMiddleware): void;
136
+ }
137
+
123
138
  export interface DinouPlugin {
124
139
  name?: string;
140
+ /**
141
+ * Hook invoked when the Node.js HTTP server is instantiated,
142
+ * allowing plugins to attach WebSockets (Socket.IO, ws), listeners, or raw Node middlewares.
143
+ */
144
+ configureServer?(context: ServerHookContext): Promise<void> | void;
145
+
146
+ /**
147
+ * Array of setup/polyfill file paths to be executed at the earliest stage
148
+ * and injected into all bundled engines before any modules are loaded.
149
+ */
150
+ setupFiles?: string[];
151
+
125
152
  /**
126
153
  * Universal Web Standards middleware and webhook handler.
127
154
  * Runs on every incoming HTTP request before React rendering.
@@ -161,6 +188,13 @@ export interface DinouConfig {
161
188
  */
162
189
  storage?: any;
163
190
 
191
+ /**
192
+ * Array of setup/polyfill file paths to be executed at the earliest stage
193
+ * before any server or client components are imported.
194
+ * If provided, overrides the root `dinou.setup.(mjs|js|ts)` convention.
195
+ */
196
+ setupFiles?: string[];
197
+
164
198
  /**
165
199
  * Array of Dinou plugins.
166
200
  */
@@ -23,7 +23,7 @@ try {
23
23
  outDir: ".dinou/node",
24
24
  });
25
25
 
26
- const { dinouDirSlash, externalList, banner, commonAlias, commonLoader, isWebpackBuild } = engineResult;
26
+ const { dinouDirSlash, externalList, banner, commonAlias, commonLoader, isWebpackBuild, setupFiles } = engineResult;
27
27
 
28
28
  // Pass C: Node Orchestrator Entry
29
29
  const nodeEntryContent = `// Auto-generated main entry for Node.js (Dual-Bundle)
@@ -119,12 +119,32 @@ export async function fetch(req) {
119
119
 
120
120
  const PORT = Number(process.env.PORT || 3000);
121
121
 
122
- export function startServer(port = PORT, host = "0.0.0.0") {
122
+ const nodeMiddlewares = [];
123
+
124
+ export async function startServer(port = PORT, host = "0.0.0.0") {
123
125
  const server = http.createServer(async (req, res) => {
124
126
  try {
127
+ if (res.writableEnded || res.headersSent) {
128
+ return;
129
+ }
130
+
131
+ if (nodeMiddlewares.length > 0) {
132
+ for (const mw of nodeMiddlewares) {
133
+ let continued = false;
134
+ await mw(req, res, () => {
135
+ continued = true;
136
+ });
137
+ if (!continued || res.writableEnded || res.headersSent) {
138
+ return;
139
+ }
140
+ }
141
+ }
142
+
125
143
  const webReq = nodeToWebRequest(req);
126
144
  const webRes = await fetch(webReq);
127
- await sendWebResponseToNode(webRes, res);
145
+ if (!res.writableEnded && !res.headersSent) {
146
+ await sendWebResponseToNode(webRes, res);
147
+ }
128
148
  } catch (err) {
129
149
  console.error("[Dinou Node Server] Request error:", err);
130
150
  if (!res.headersSent) {
@@ -135,6 +155,29 @@ export function startServer(port = PORT, host = "0.0.0.0") {
135
155
  }
136
156
  });
137
157
 
158
+ try {
159
+ const { getDinouConfig } = await import("${dinouDirSlash}/core/load-dinou-config.js");
160
+ const cfg = await getDinouConfig();
161
+ if (cfg.plugins && Array.isArray(cfg.plugins)) {
162
+ const serverContext = {
163
+ server,
164
+ isDev: false,
165
+ use(middleware) {
166
+ if (typeof middleware === "function") {
167
+ nodeMiddlewares.push(middleware);
168
+ }
169
+ },
170
+ };
171
+ for (const plugin of cfg.plugins) {
172
+ if (typeof plugin.configureServer === "function") {
173
+ await plugin.configureServer(serverContext);
174
+ }
175
+ }
176
+ }
177
+ } catch (err) {
178
+ console.error("❌ [Dinou Node Server] Error running configureServer plugins:", err);
179
+ }
180
+
138
181
  return new Promise((resolve, reject) => {
139
182
  server.on("error", reject);
140
183
  server.listen(port, () => {
@@ -146,17 +189,20 @@ export function startServer(port = PORT, host = "0.0.0.0") {
146
189
 
147
190
  export default {
148
191
  port: PORT,
149
- fetch,
150
192
  startServer,
151
193
  };
152
194
 
153
- const isMain = typeof process !== "undefined" &&
154
- process.argv[1] &&
155
- (path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url)));
195
+ const isMain = Boolean(
196
+ (typeof import.meta !== "undefined" && import.meta.main) ||
197
+ (typeof process !== "undefined" &&
198
+ process.argv[1] &&
199
+ (path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url))))
200
+ );
156
201
 
157
202
  if (isMain) {
158
203
  startServer(PORT);
159
204
  }
205
+
160
206
  `;
161
207
  const nodeEntryPath = path.join(nodeDir, "node-entry.mjs");
162
208
  fs.writeFileSync(nodeEntryPath, nodeEntryContent, "utf8");
@@ -175,6 +221,7 @@ if (isMain) {
175
221
  conditions: ["node", "worker", "browser"],
176
222
  external: [...externalList, "./rsc-engine.mjs", "./ssr-engine.mjs"],
177
223
  banner,
224
+ inject: setupFiles,
178
225
  alias: commonAlias,
179
226
  loader: commonLoader,
180
227
  define: {
@@ -20,6 +20,8 @@ const {
20
20
  isSupportedClientModule,
21
21
  scanProjectDependenciesForClientComponents,
22
22
  } = require("../core/scan-dependency-components.js");
23
+ const { getDinouConfig } = require("../core/load-dinou-config.js");
24
+ const { resolveSetupFiles } = require("../core/resolve-setup-files.js");
23
25
 
24
26
  export async function bundleDualEngine(options = {}) {
25
27
  const isDev = Boolean(options.isDev);
@@ -27,6 +29,12 @@ export async function bundleDualEngine(options = {}) {
27
29
  const nodeDir = path.resolve(projectRoot, options.outDir || ".dinou/node");
28
30
  fs.mkdirSync(nodeDir, { recursive: true });
29
31
 
32
+ const dinouConfig = await getDinouConfig();
33
+ const setupFiles = resolveSetupFiles(projectRoot, dinouConfig);
34
+ if (setupFiles.length > 0) {
35
+ console.log(` 🔌 [Dinou Setup] Active setup files (${setupFiles.length}):`, setupFiles.map((f) => path.relative(projectRoot, f)).join(", "));
36
+ }
37
+
30
38
  try {
31
39
  generateRouteTypes(projectRoot);
32
40
  } catch (e) {}
@@ -956,6 +964,7 @@ var __webpack_chunk_load__ = function(chunkId) {
956
964
  conditions: ["node", "worker", "react-server"],
957
965
  external: externalList,
958
966
  banner,
967
+ inject: setupFiles,
959
968
  plugins: [clientReferencesPlugin, serverReferencesPlugin, serverAssetPlugin],
960
969
  alias: commonAlias,
961
970
  loader: commonLoader,
@@ -980,6 +989,7 @@ var __webpack_chunk_load__ = function(chunkId) {
980
989
  conditions: ["node", "worker", "browser"],
981
990
  external: externalList,
982
991
  banner,
992
+ inject: setupFiles,
983
993
  plugins: [serverReferencesPluginSsr, serverAssetPlugin],
984
994
  alias: commonAlias,
985
995
  loader: commonLoader,
@@ -1006,6 +1016,7 @@ var __webpack_chunk_load__ = function(chunkId) {
1006
1016
  commonAlias,
1007
1017
  commonLoader,
1008
1018
  isWebpackBuild,
1019
+ setupFiles,
1009
1020
  };
1010
1021
  }
1011
1022
 
@@ -76,6 +76,22 @@ const dinouDir = fs.existsSync(path.resolve(projectRoot, "dinou"))
76
76
  : path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
77
77
  const dinouDirSlash = dinouDir.replace(/\\/g, "/");
78
78
 
79
+ // Resolve and execute setupFiles immediately before any compilation
80
+ const { getDinouConfig } = require(path.join(dinouDir, "core/load-dinou-config.js"));
81
+ const { resolveSetupFiles } = require(path.join(dinouDir, "core/resolve-setup-files.js"));
82
+ const dinouConfig = await getDinouConfig();
83
+ const setupFiles = resolveSetupFiles(projectRoot, dinouConfig);
84
+ if (setupFiles.length > 0) {
85
+ console.log(` 🔌 [Dinou Setup] Executing setup files (${setupFiles.length}):`, setupFiles.map((f) => path.relative(projectRoot, f)).join(", "));
86
+ for (const f of setupFiles) {
87
+ try {
88
+ await dynamicImportWithRetry(pathToFileURL(f).href);
89
+ } catch (e) {
90
+ console.error(`❌ [Dinou Dev] Error executing setupFile "${f}":`, e);
91
+ }
92
+ }
93
+ }
94
+
79
95
  function getFallbackChunkId(absPath) {
80
96
  const norm = absPath.replace(/\\/g, "/");
81
97
  const name = path.basename(absPath, path.extname(absPath));
@@ -1463,6 +1479,7 @@ const ctxA = await esbuild.context({
1463
1479
  conditions: ["node", "worker", "react-server"],
1464
1480
  external: externalList,
1465
1481
  banner,
1482
+ inject: setupFiles,
1466
1483
  plugins: [clientReferencesPlugin, serverReferencesPlugin, serverAssetPlugin, createDevExternalPackagesPlugin(true)],
1467
1484
  alias: commonAlias,
1468
1485
  loader: commonLoader,
@@ -1490,6 +1507,7 @@ const ctxB = await esbuild.context({
1490
1507
  conditions: ["node", "worker", "browser"],
1491
1508
  external: externalList,
1492
1509
  banner,
1510
+ inject: setupFiles,
1493
1511
  plugins: [serverReferencesPluginSsr, serverAssetPlugin, createDevExternalPackagesPlugin(false)],
1494
1512
  alias: commonAlias,
1495
1513
  loader: commonLoader,
@@ -2302,9 +2320,52 @@ async function startClientBundler(tool) {
2302
2320
  throw new Error(`Unsupported DINOU_BUILD_TOOL: ${tool}`);
2303
2321
  }
2304
2322
 
2323
+ // Node.js Middlewares registered by plugins via configureServer
2324
+ const nodeMiddlewares = [];
2325
+
2326
+ async function applyServerPlugins(serverInstance) {
2327
+ try {
2328
+ const { getDinouConfig } = require(path.join(dinouDir, "core/load-dinou-config.js"));
2329
+ const config = await getDinouConfig();
2330
+ if (config.plugins && Array.isArray(config.plugins)) {
2331
+ const serverContext = {
2332
+ server: serverInstance,
2333
+ isDev: true,
2334
+ use(middleware) {
2335
+ if (typeof middleware === "function") {
2336
+ nodeMiddlewares.push(middleware);
2337
+ }
2338
+ },
2339
+ };
2340
+ for (const plugin of config.plugins) {
2341
+ if (typeof plugin.configureServer === "function") {
2342
+ await plugin.configureServer(serverContext);
2343
+ }
2344
+ }
2345
+ }
2346
+ } catch (err) {
2347
+ console.error("❌ [Dinou Dev] Error applying plugin configureServer hooks:", err);
2348
+ }
2349
+ }
2350
+
2305
2351
  // HTTP Server
2306
2352
  const server = http.createServer(async (req, res) => {
2307
2353
  try {
2354
+ if (res.writableEnded || res.headersSent) {
2355
+ return;
2356
+ }
2357
+
2358
+ if (nodeMiddlewares.length > 0) {
2359
+ for (const mw of nodeMiddlewares) {
2360
+ let continued = false;
2361
+ await mw(req, res, () => {
2362
+ continued = true;
2363
+ });
2364
+ if (!continued || res.writableEnded || res.headersSent) {
2365
+ return;
2366
+ }
2367
+ }
2368
+ }
2308
2369
  if (clientBundlerPromise) {
2309
2370
  await clientBundlerPromise;
2310
2371
  }
@@ -2493,7 +2554,9 @@ const server = http.createServer(async (req, res) => {
2493
2554
  console.log(`[DEV 404] ${req.method} ${pathname}`);
2494
2555
  }
2495
2556
 
2496
- await sendWebResponseToNode(webRes, res);
2557
+ if (!res.writableEnded && !res.headersSent) {
2558
+ await sendWebResponseToNode(webRes, res);
2559
+ }
2497
2560
  } catch (err) {
2498
2561
  console.error("[Dinou Dev Server] Request error:", err);
2499
2562
  if (!res.headersSent) {
@@ -2515,6 +2578,8 @@ server.on("error", (err) => {
2515
2578
  process.exit(1);
2516
2579
  });
2517
2580
 
2581
+ await applyServerPlugins(server);
2582
+
2518
2583
  server.listen(PORT, async () => {
2519
2584
  const buildTool = isWebpackBuild ? "webpack" : (process.env.DINOU_BUILD_TOOL || "esbuild");
2520
2585
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dinou-ejected",
3
- "version": "7.1.0",
3
+ "version": "7.3.0",
4
4
  "main": "index.js",
5
5
  "private": true,
6
6
  "exports": {
package/eject.js CHANGED
@@ -28,6 +28,50 @@ const ejectedScripts = {
28
28
  "build:node:esbuild": "npm run build:node",
29
29
  "build:node:rollup": "npm run build:client:rollup && node ./dinou/node/build.mjs",
30
30
  "build:node:webpack": "npm run build:client:webpack && cross-env DINOU_BUILD_TOOL=webpack node ./dinou/node/build.mjs",
31
+ "build:node:compile": "npm run build:node:compile:esbuild",
32
+ "build:node:compile:esbuild": "npm run build:node:esbuild && bun build --compile .dinou/node/server.mjs --outfile dist/server",
33
+ "build:node:compile:rollup": "npm run build:node:rollup && bun build --compile .dinou/node/server.mjs --outfile dist/server",
34
+ "build:node:compile:webpack": "npm run build:node:webpack && bun build --compile .dinou/node/server.mjs --outfile dist/server",
35
+ "build:node:compile:linux": "npm run build:node:compile:linux:esbuild",
36
+ "build:node:compile:linux:esbuild": "npm run build:node:esbuild && bun build --compile --target=bun-linux-x64 .dinou/node/server.mjs --outfile dist/server-linux",
37
+ "build:node:compile:linux:rollup": "npm run build:node:rollup && bun build --compile --target=bun-linux-x64 .dinou/node/server.mjs --outfile dist/server-linux",
38
+ "build:node:compile:linux:webpack": "npm run build:node:webpack && bun build --compile --target=bun-linux-x64 .dinou/node/server.mjs --outfile dist/server-linux",
39
+ "build:node:compile:linux_arm": "npm run build:node:compile:linux_arm:esbuild",
40
+ "build:node:compile:linux_arm:esbuild": "npm run build:node:esbuild && bun build --compile --target=bun-linux-arm64 .dinou/node/server.mjs --outfile dist/server-linux-arm",
41
+ "build:node:compile:linux_arm:rollup": "npm run build:node:rollup && bun build --compile --target=bun-linux-arm64 .dinou/node/server.mjs --outfile dist/server-linux-arm",
42
+ "build:node:compile:linux_arm:webpack": "npm run build:node:webpack && bun build --compile --target=bun-linux-arm64 .dinou/node/server.mjs --outfile dist/server-linux-arm",
43
+ "build:node:compile:mac": "npm run build:node:compile:mac:esbuild",
44
+ "build:node:compile:mac:esbuild": "npm run build:node:esbuild && bun build --compile --target=bun-darwin-arm64 .dinou/node/server.mjs --outfile dist/server-mac",
45
+ "build:node:compile:mac:rollup": "npm run build:node:rollup && bun build --compile --target=bun-darwin-arm64 .dinou/node/server.mjs --outfile dist/server-mac",
46
+ "build:node:compile:mac:webpack": "npm run build:node:webpack && bun build --compile --target=bun-darwin-arm64 .dinou/node/server.mjs --outfile dist/server-mac",
47
+ "build:node:compile:win": "npm run build:node:compile:win:esbuild",
48
+ "build:node:compile:win:esbuild": "npm run build:node:esbuild && bun build --compile --target=bun-windows-x64 .dinou/node/server.mjs --outfile dist/server-win",
49
+ "build:node:compile:win:rollup": "npm run build:node:rollup && bun build --compile --target=bun-windows-x64 .dinou/node/server.mjs --outfile dist/server-win",
50
+ "build:node:compile:win:webpack": "npm run build:node:webpack && bun build --compile --target=bun-windows-x64 .dinou/node/server.mjs --outfile dist/server-win",
51
+ "build:node:pure": "npm run build:node:pure:esbuild",
52
+ "build:node:pure:esbuild": "cross-env NODE_ENV=production bun dinou/esbuild/build.mjs && bun ./dinou/node/build.mjs",
53
+ "build:node:pure:rollup": "cross-env NODE_ENV=production rollup -c ./dinou/rollup/rollup.config.js && bun ./dinou/node/build.mjs",
54
+ "build:node:pure:webpack": "cross-env NODE_ENV=production webpack --config dinou/webpack/webpack.config.js && cross-env DINOU_BUILD_TOOL=webpack bun ./dinou/node/build.mjs",
55
+ "build:node:pure:compile": "npm run build:node:pure:compile:esbuild",
56
+ "build:node:pure:compile:esbuild": "cross-env NODE_ENV=production bun dinou/esbuild/build.mjs && bun ./dinou/node/build.mjs && bun build --compile .dinou/node/server.mjs --outfile dist/server",
57
+ "build:node:pure:compile:rollup": "cross-env NODE_ENV=production rollup -c ./dinou/rollup/rollup.config.js && bun ./dinou/node/build.mjs && bun build --compile .dinou/node/server.mjs --outfile dist/server",
58
+ "build:node:pure:compile:webpack": "cross-env NODE_ENV=production webpack --config dinou/webpack/webpack.config.js && cross-env DINOU_BUILD_TOOL=webpack bun ./dinou/node/build.mjs && bun build --compile .dinou/node/server.mjs --outfile dist/server",
59
+ "build:node:pure:compile:linux": "npm run build:node:pure:compile:linux:esbuild",
60
+ "build:node:pure:compile:linux:esbuild": "cross-env NODE_ENV=production bun dinou/esbuild/build.mjs && bun ./dinou/node/build.mjs && bun build --compile --target=bun-linux-x64 .dinou/node/server.mjs --outfile dist/server-linux",
61
+ "build:node:pure:compile:linux:rollup": "cross-env NODE_ENV=production rollup -c ./dinou/rollup/rollup.config.js && bun ./dinou/node/build.mjs && bun build --compile --target=bun-linux-x64 .dinou/node/server.mjs --outfile dist/server-linux",
62
+ "build:node:pure:compile:linux:webpack": "cross-env NODE_ENV=production webpack --config dinou/webpack/webpack.config.js && cross-env DINOU_BUILD_TOOL=webpack bun ./dinou/node/build.mjs && bun build --compile --target=bun-linux-x64 .dinou/node/server.mjs --outfile dist/server-linux",
63
+ "build:node:pure:compile:linux_arm": "npm run build:node:pure:compile:linux_arm:esbuild",
64
+ "build:node:pure:compile:linux_arm:esbuild": "cross-env NODE_ENV=production bun dinou/esbuild/build.mjs && bun ./dinou/node/build.mjs && bun build --compile --target=bun-linux-arm64 .dinou/node/server.mjs --outfile dist/server-linux-arm",
65
+ "build:node:pure:compile:linux_arm:rollup": "cross-env NODE_ENV=production rollup -c ./dinou/rollup/rollup.config.js && bun ./dinou/node/build.mjs && bun build --compile --target=bun-linux-arm64 .dinou/node/server.mjs --outfile dist/server-linux-arm",
66
+ "build:node:pure:compile:linux_arm:webpack": "cross-env NODE_ENV=production webpack --config dinou/webpack/webpack.config.js && cross-env DINOU_BUILD_TOOL=webpack bun ./dinou/node/build.mjs && bun build --compile --target=bun-linux-arm64 .dinou/node/server.mjs --outfile dist/server-linux-arm",
67
+ "build:node:pure:compile:mac": "npm run build:node:pure:compile:mac:esbuild",
68
+ "build:node:pure:compile:mac:esbuild": "cross-env NODE_ENV=production bun dinou/esbuild/build.mjs && bun ./dinou/node/build.mjs && bun build --compile --target=bun-darwin-arm64 .dinou/node/server.mjs --outfile dist/server-mac",
69
+ "build:node:pure:compile:mac:rollup": "cross-env NODE_ENV=production rollup -c ./dinou/rollup/rollup.config.js && bun ./dinou/node/build.mjs && bun build --compile --target=bun-darwin-arm64 .dinou/node/server.mjs --outfile dist/server-mac",
70
+ "build:node:pure:compile:mac:webpack": "cross-env NODE_ENV=production webpack --config dinou/webpack/webpack.config.js && cross-env DINOU_BUILD_TOOL=webpack bun ./dinou/node/build.mjs && bun build --compile --target=bun-darwin-arm64 .dinou/node/server.mjs --outfile dist/server-mac",
71
+ "build:node:pure:compile:win": "npm run build:node:pure:compile:win:esbuild",
72
+ "build:node:pure:compile:win:esbuild": "cross-env NODE_ENV=production bun dinou/esbuild/build.mjs && bun ./dinou/node/build.mjs && bun build --compile --target=bun-windows-x64 .dinou/node/server.mjs --outfile dist/server-win",
73
+ "build:node:pure:compile:win:rollup": "cross-env NODE_ENV=production rollup -c ./dinou/rollup/rollup.config.js && bun ./dinou/node/build.mjs && bun build --compile --target=bun-windows-x64 .dinou/node/server.mjs --outfile dist/server-win",
74
+ "build:node:pure:compile:win:webpack": "cross-env NODE_ENV=production webpack --config dinou/webpack/webpack.config.js && cross-env DINOU_BUILD_TOOL=webpack bun ./dinou/node/build.mjs && bun build --compile --target=bun-windows-x64 .dinou/node/server.mjs --outfile dist/server-win",
31
75
  "start:node": "cross-env NODE_ENV=production node dinou/adapters/node.mjs",
32
76
  "start:node:esbuild": "npm run start:node",
33
77
  "start:node:rollup": "npm run start:node",
@@ -146,7 +190,7 @@ function detectPackageManager() {
146
190
 
147
191
  if (fs.existsSync(path.join(projectRoot, "yarn.lock"))) return "yarn";
148
192
  if (fs.existsSync(path.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
149
- if (fs.existsSync(path.join(projectRoot, "bun.lockb"))) return "bun";
193
+ if (fs.existsSync(path.join(projectRoot, "bun.lockb")) || fs.existsSync(path.join(projectRoot, "bun.lock"))) return "bun";
150
194
  if (fs.existsSync(path.join(projectRoot, "package-lock.json"))) return "npm";
151
195
 
152
196
  return "npm";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dinou",
3
- "version": "7.1.0",
3
+ "version": "7.3.0",
4
4
  "description": "Full-stack React 19 framework with native RSC, Server Functions, progressive streaming, and zero vendor lock-in.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -1,267 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const { transformAsync } = require("@babel/core");
4
- const { fileURLToPath, pathToFileURL } = require("url");
5
- const createScopedName = require("./createScopedName");
6
- const { extensionsWithDot } = require("./asset-extensions.js");
7
- const { getAbsPathWithExt } = require("./get-abs-path-with-ext.js");
8
-
9
- const { normalizePathCase } = require("./path-utils.js");
10
-
11
- const Module = require("module");
12
- const originalResolveFilename = Module._resolveFilename;
13
- const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
14
-
15
- let reactServerPath, reactDomServerPath, reactJsxRuntimePath, reactJsxDevRuntimePath;
16
-
17
- const reactPkgJson = require.resolve("react/package.json");
18
- reactServerPath = path.join(path.dirname(reactPkgJson), "react.react-server.js");
19
- reactJsxRuntimePath = path.join(path.dirname(reactPkgJson), "jsx-runtime.react-server.js");
20
- reactJsxDevRuntimePath = path.join(path.dirname(reactPkgJson), "jsx-dev-runtime.react-server.js");
21
-
22
- const reactDomPkgJson = require.resolve("react-dom/package.json");
23
- reactDomServerPath = path.join(path.dirname(reactDomPkgJson), "react-dom.react-server.js");
24
-
25
- let roggcServerNodePath, webpackServerNodePath;
26
- try {
27
- const roggcPkg = require.resolve("@roggc/react-server-dom-esm/package.json");
28
- roggcServerNodePath = path.join(path.dirname(roggcPkg), "server.node.js");
29
- } catch (e) {}
30
-
31
- try {
32
- const wpPkg = require.resolve("react-server-dom-webpack/package.json");
33
- webpackServerNodePath = path.join(path.dirname(wpPkg), "server.node.js");
34
- } catch (e) {}
35
-
36
- Module._resolveFilename = function (request, parent, isMain, options) {
37
- if (request === "react") {
38
- return reactServerPath;
39
- } else if (request === "react-dom") {
40
- return reactDomServerPath;
41
- } else if (request === "react/jsx-runtime") {
42
- return reactJsxRuntimePath;
43
- } else if (request === "react/jsx-dev-runtime") {
44
- return reactJsxDevRuntimePath;
45
- } else if (request === "@roggc/react-server-dom-esm/server" && roggcServerNodePath) {
46
- return roggcServerNodePath;
47
- } else if (request === "react-server-dom-webpack/server" && webpackServerNodePath) {
48
- return webpackServerNodePath;
49
- }
50
- return originalResolveFilename.call(this, request, parent, isMain, options);
51
- };
52
-
53
- require("./css-require-hook.js")();
54
-
55
- function getMtimeParam(absPath) {
56
- try {
57
- const stats = fs.statSync(absPath);
58
- return Math.round(stats.mtimeMs);
59
- } catch (e) {
60
- return Date.now();
61
- }
62
- }
63
-
64
- exports.resolve = async function resolve(specifier, context, defaultResolve) {
65
- if (process.env.DINOU_PROCESS !== "ssr-html") {
66
- if (specifier === "react") {
67
- return { url: pathToFileURL(reactServerPath).href, shortCircuit: true };
68
- }
69
- if (specifier === "react/jsx-runtime") {
70
- return { url: pathToFileURL(reactJsxRuntimePath).href, shortCircuit: true };
71
- }
72
- if (specifier === "react/jsx-dev-runtime") {
73
- return { url: pathToFileURL(reactJsxDevRuntimePath).href, shortCircuit: true };
74
- }
75
- if (specifier === "react-dom") {
76
- return { url: pathToFileURL(reactDomServerPath).href, shortCircuit: true };
77
- }
78
- if (specifier === "@roggc/react-server-dom-esm/server" && roggcServerNodePath) {
79
- return { url: pathToFileURL(roggcServerNodePath).href, shortCircuit: true };
80
- }
81
- if (specifier === "react-server-dom-webpack/server" && webpackServerNodePath) {
82
- return { url: pathToFileURL(webpackServerNodePath).href, shortCircuit: true };
83
- }
84
- }
85
-
86
- const absPathWithExt = getAbsPathWithExt(specifier, context);
87
- if (absPathWithExt) {
88
- let url = pathToFileURL(absPathWithExt).href;
89
- if (process.env.NODE_ENV !== "production") {
90
- url += `?mtime=${getMtimeParam(absPathWithExt)}`;
91
- }
92
-
93
- return {
94
- url,
95
- shortCircuit: true,
96
- };
97
- }
98
-
99
- // Fallback to default resolver
100
- return defaultResolve(specifier, context, defaultResolve);
101
- };
102
-
103
- exports.load = async function load(url, context, defaultLoad) {
104
- // --- 🟢 Handle non-JS assets (png, jpg, etc.)
105
- const assetExts = extensionsWithDot;
106
- const ext = path.extname(url.split("?")[0]); // remove search params if any
107
-
108
- if (assetExts.includes(ext)) {
109
- // Return a tiny stub that mimics what asset-require-hook would do
110
- const filepath = fileURLToPath(url.split("?")[0]);
111
- const localName = path.basename(filepath, ext);
112
- const hashedName = createScopedName(localName, filepath);
113
- const virtualExport = `export default "/assets/${hashedName}${ext}";`;
114
-
115
- return {
116
- format: "module",
117
- source: virtualExport,
118
- shortCircuit: true,
119
- url,
120
- };
121
- }
122
-
123
- if (ext === ".css") {
124
- const mod = require(fileURLToPath(url.split("?")[0]));
125
- const source = `export default ${JSON.stringify(mod)};`;
126
- return { format: "module", source, shortCircuit: true, url };
127
- }
128
-
129
- const cleanUrl = url.split("?")[0];
130
- if (/\.(jsx|tsx|ts|js)$/.test(cleanUrl)) {
131
- let filename;
132
- try {
133
- filename = fileURLToPath(
134
- cleanUrl.startsWith("file://") ? cleanUrl : pathToFileURL(cleanUrl).href
135
- );
136
- } catch (e) {
137
- throw e;
138
- }
139
- const rel = path.relative(
140
- normalizePathCase(process.cwd()),
141
- normalizePathCase(filename)
142
- );
143
- const source = fs.readFileSync(filename, "utf-8");
144
- const urlToReturn = pathToFileURL(filename).href;
145
-
146
- const useClientRegex =
147
- /^\s*(?:(?:\/\/[^\n]*\n\s*)|(?:\/\*[\s\S]*?\*\/\s*))*['"]use client['"]/;
148
- const hasUseClient = useClientRegex.test(source);
149
-
150
- const esmSyntaxRegex = /^(?:import|export)\b/m;
151
- const hasESMSyntax = esmSyntaxRegex.test(source);
152
-
153
- if (ext === ".js" && !rel.startsWith("src" + path.sep) && !hasESMSyntax) {
154
- return defaultLoad(url, context, defaultLoad);
155
- }
156
-
157
- const isReactServer = process.env.DINOU_PROCESS !== "ssr-html";
158
- if (isReactServer && hasUseClient) {
159
- const parseExports = require("./parse-exports.js");
160
- const exports = parseExports(source);
161
- const clientPkgPath = isWebpack
162
- ? require.resolve("react-server-dom-webpack/package.json")
163
- : require.resolve("@roggc/react-server-dom-esm/package.json");
164
- const clientServerNodePath = path.join(path.dirname(clientPkgPath), "server.node.js");
165
- const clientServerNodeUrl = pathToFileURL(clientServerNodePath).href;
166
- let newSrc = `import pkg from ${JSON.stringify(clientServerNodeUrl)};\n`;
167
- newSrc += 'const {registerClientReference} = pkg;\n';
168
- for (const name of exports) {
169
- if (name === 'default') {
170
- newSrc += 'export default ';
171
- newSrc += 'registerClientReference(function() {';
172
- newSrc += 'throw new Error(' + JSON.stringify("Attempted to call the default export of " + urlToReturn + " from the server but it's on the client.") + ');';
173
- newSrc += '},';
174
- newSrc += JSON.stringify(urlToReturn) + ',';
175
- newSrc += JSON.stringify(name) + ');\n';
176
- } else {
177
- newSrc += 'export const ' + name + ' = ';
178
- newSrc += 'registerClientReference(function() {';
179
- newSrc += 'throw new Error(' + JSON.stringify("Attempted to call " + name + "() from the server but " + name + " is on the client.") + ');';
180
- newSrc += '},';
181
- newSrc += JSON.stringify(urlToReturn) + ',';
182
- newSrc += JSON.stringify(name) + ');\n';
183
- }
184
- }
185
- return {
186
- format: "module",
187
- source: newSrc,
188
- shortCircuit: true,
189
- url,
190
- };
191
- }
192
-
193
- const { useServerRegex } = require("../constants.js");
194
- const hasUseServer = useServerRegex.test(source);
195
-
196
- if (isReactServer && hasUseServer) {
197
- const parseExports = require("./parse-exports.js");
198
- const exports = parseExports(source);
199
-
200
- const { code } = await transformAsync(source, {
201
- filename,
202
- presets: [
203
- ["@babel/preset-react", { runtime: "automatic" }],
204
- "@babel/preset-typescript",
205
- ],
206
- sourceMaps: "inline",
207
- ast: false,
208
- });
209
-
210
- let newSrc = code + "\n\n";
211
-
212
- const serverPkgPath = isWebpack
213
- ? require.resolve("react-server-dom-webpack/package.json")
214
- : require.resolve("@roggc/react-server-dom-esm/package.json");
215
- const serverNodePath = path.join(path.dirname(serverPkgPath), "server.node.js");
216
- const serverNodeUrl = pathToFileURL(serverNodePath).href;
217
- newSrc += `import pkgServer from ${JSON.stringify(serverNodeUrl)};\n`;
218
- newSrc += 'const {registerServerReference} = pkgServer;\n';
219
-
220
- const relativeFileUrl = "file:///" + rel.replace(/\\/g, "/");
221
- for (const name of exports) {
222
- if (name !== 'default') {
223
- newSrc += `registerServerReference(${name}, ${JSON.stringify(relativeFileUrl)}, ${JSON.stringify(name)});\n`;
224
- }
225
- }
226
-
227
- return {
228
- format: "module",
229
- source: newSrc,
230
- shortCircuit: true,
231
- url,
232
- };
233
- }
234
-
235
- if (ext === ".js") {
236
- return {
237
- format: "module",
238
- source,
239
- shortCircuit: true,
240
- url,
241
- };
242
- }
243
-
244
- const { code } = await transformAsync(source, {
245
- filename,
246
- presets: [
247
- ["@babel/preset-react", { runtime: "automatic" }],
248
- "@babel/preset-typescript",
249
- ],
250
- sourceMaps: "inline",
251
- ast: false,
252
- });
253
-
254
- // const urlToReturn = pathToFileURL(filename).href;
255
-
256
- return {
257
- format: "module",
258
- source: code,
259
- shortCircuit: true,
260
- url,
261
- };
262
- }
263
-
264
- if (url) {
265
- return defaultLoad(url, context, defaultLoad);
266
- }
267
- };