dinou 6.1.1 → 7.0.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 +94 -0
- package/README.md +9461 -1080
- package/cli.js +392 -169
- package/dinou/adapters/bun.js +62 -0
- package/dinou/adapters/cloudflare.js +65 -0
- package/dinou/adapters/deno.js +48 -0
- package/dinou/adapters/netlify.js +85 -0
- package/dinou/adapters/node.mjs +31 -0
- package/dinou/bun/build.mjs +1077 -0
- package/dinou/cloudflare/build.mjs +1121 -0
- package/dinou/cloudflare/shims/child_process.js +14 -0
- package/dinou/cloudflare/shims/empty.js +2 -0
- package/dinou/cloudflare/shims/fs.js +99 -0
- package/dinou/cloudflare/shims/os.js +29 -0
- package/dinou/config.js +1 -0
- package/dinou/config.mjs +2 -0
- package/dinou/constants.js +29 -4
- package/dinou/core/babel-esm-loader.js +66 -37
- package/dinou/core/bailout-proxy.js +61 -0
- package/dinou/core/build-static-pages.js +309 -157
- package/dinou/core/cache-revalidate.js +471 -144
- package/dinou/core/cache-slot.js +387 -0
- package/dinou/core/client-redirect.jsx +12 -7
- package/dinou/core/client-webpack.jsx +457 -82
- package/dinou/core/client.jsx +452 -82
- package/dinou/core/config.js +11 -0
- package/dinou/core/dinou-paths.js +52 -0
- package/dinou/core/edge-ssr.js +122 -0
- package/dinou/core/export-static.mjs +64 -0
- package/dinou/core/generate-route-types.js +252 -0
- package/dinou/core/generate-static-page.js +55 -94
- package/dinou/core/generate-static-rsc.js +47 -15
- package/dinou/core/generate-static.js +212 -10
- package/dinou/core/generating-isg.js +5 -0
- package/dinou/core/get-asset-from-manifest.js +9 -3
- package/dinou/core/get-error-jsx.js +221 -41
- package/dinou/core/get-file-path-and-dynamic-params.js +9 -13
- package/dinou/core/get-jsx.js +172 -95
- package/dinou/core/handler.js +2296 -0
- package/dinou/core/http-adapter.js +115 -0
- package/dinou/core/import-module.js +83 -53
- package/dinou/core/layout-functions.js +181 -0
- package/dinou/core/link.jsx +18 -6
- package/dinou/core/load-dinou-config.js +60 -0
- package/dinou/core/manifest-provider.js +261 -0
- package/dinou/core/navigation-utils.js +4 -0
- package/dinou/core/navigation.js +50 -39
- package/dinou/core/parse-exports.js +207 -2
- package/dinou/core/ppr-context.js +105 -0
- package/dinou/core/ppr-runtime.js +263 -0
- package/dinou/core/redirect.jsx +20 -19
- package/dinou/core/render-jsx-to-client-jsx.js +133 -15
- package/dinou/core/request-context.js +47 -13
- package/dinou/core/resolve-module-ppr.js +32 -0
- package/dinou/core/revalidating.js +28 -9
- package/dinou/core/route-generator.js +107 -0
- package/dinou/core/rsc-renderer.js +90 -0
- package/dinou/core/run-ssg.js +19 -0
- package/dinou/core/scan-dependency-components.js +218 -0
- package/dinou/core/server-function-proxy-webpack.js +3 -0
- package/dinou/core/server-function-proxy.js +3 -0
- package/dinou/core/slot.js +302 -0
- package/dinou/core/status-manifest.js +41 -1
- package/dinou/core/storage-adapter.js +522 -0
- package/dinou/core/url-resolver.js +16 -16
- package/dinou/core/vfs.js +139 -68
- package/dinou/deno/build.mjs +1112 -0
- package/dinou/deno/seed-kv.js +115 -0
- package/dinou/esbuild/build.mjs +51 -1
- package/dinou/esbuild/dev.mjs +290 -224
- package/dinou/esbuild/helpers-esbuild/get-config-esbuild-prod.mjs +3 -2
- package/dinou/esbuild/helpers-esbuild/get-config-esbuild.mjs +104 -16
- package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +31 -19
- package/dinou/esbuild/helpers-esbuild/strip-namespace.mjs +9 -0
- package/dinou/esbuild/helpers-esbuild/swc-disk-cache.mjs +225 -0
- package/dinou/esbuild/helpers-esbuild/update-manifest-for-module.mjs +7 -0
- package/dinou/esbuild/helpers-esbuild/write.mjs +111 -2
- package/dinou/esbuild/plugins-esbuild/assets-plugin.mjs +236 -161
- package/dinou/esbuild/plugins-esbuild/css-processor-plugin.mjs +101 -5
- package/dinou/esbuild/plugins-esbuild/react-client-manifest-plugin.mjs +63 -24
- package/dinou/esbuild/plugins-esbuild/server-functions-plugin.mjs +177 -56
- package/dinou/esbuild/plugins-esbuild/skip-missing-entry-points-plugin.mjs +3 -0
- package/dinou/esbuild/plugins-esbuild/stable-chunk-names-and-maps-plugin.mjs +182 -137
- package/dinou/esbuild/plugins-postcss/postcss-extract-plugin.js +38 -11
- package/dinou/esbuild/react-refresh/esm-hmr/client.mjs +55 -8
- package/dinou/esbuild/react-refresh/esm-hmr/server.js +1 -1
- package/dinou/esbuild/react-refresh/esm-hmr-plugin.mjs +238 -101
- package/dinou/esbuild/react-refresh/is-react-refresh-boundary.mjs +18 -4
- package/dinou/index.d.ts +315 -11
- package/dinou/index.js +12 -6
- package/dinou/index.mjs +9 -0
- package/dinou/node/build.mjs +195 -0
- package/dinou/node/bundle-dual-engine.mjs +1012 -0
- package/dinou/node/dev.mjs +2589 -0
- package/dinou/node/port-selector.mjs +164 -0
- package/dinou/node/terminal-status.mjs +345 -0
- package/dinou/package.json +16 -14
- package/dinou/rollup/react-refresh/esm-hmr/client.js +66 -10
- package/dinou/rollup/react-refresh/esm-hmr/server.js +1 -1
- package/dinou/rollup/react-refresh/is-react-refresh-boundary.js +24 -5
- package/dinou/rollup/react-refresh/react-refresh-runtime.js +13 -6
- package/dinou/rollup/react-refresh/react-refresh-wrap-modules.js +56 -11
- package/dinou/rollup/react-refresh/rollup-plugin-esm-hmr.js +176 -48
- package/dinou/rollup/rollup-plugins/dinou-asset-plugin.js +9 -5
- package/dinou/rollup/rollup-plugins/rollup-plugin-memory.js +66 -0
- package/dinou/rollup/rollup-plugins/rollup-plugin-react-client-manifest.js +468 -123
- package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +44 -12
- package/dinou/rollup/rollup-plugins/rollup-plugin-swc.js +100 -0
- package/dinou/rollup/rollup.config.js +155 -43
- package/dinou/server.d.ts +114 -12
- package/dinou/server.js +35 -10
- package/dinou/server.mjs +30 -9
- package/dinou/webpack/helpers/get-webpack-entries.js +14 -19
- package/dinou/webpack/loaders/server-functions-loader.js +22 -1
- package/dinou/webpack/loaders/swc-loader.js +76 -0
- package/dinou/webpack/plugins/manifest-generator-plugin.js +1 -0
- package/dinou/webpack/plugins/server-functions-plugin.js +75 -19
- package/dinou/webpack/plugins/webpack-memory-plugin.js +66 -0
- package/dinou/webpack/webpack.config.js +227 -29
- package/eject.js +173 -94
- package/package.json +110 -108
- package/dinou/core/asset-require-hook.js +0 -58
- package/dinou/core/client-error-webpack.jsx +0 -432
- package/dinou/core/client-error.jsx +0 -432
- package/dinou/core/concurrency-manager.js +0 -48
- package/dinou/core/context-proxy.js +0 -68
- package/dinou/core/generate-static-pages.js +0 -130
- package/dinou/core/generate-static-rscs.js +0 -151
- package/dinou/core/get-ssg-metadata.js +0 -31
- package/dinou/core/register-loader.mjs +0 -11
- package/dinou/core/register-paths.js +0 -27
- package/dinou/core/render-app-to-html.js +0 -430
- package/dinou/core/render-html.js +0 -407
- package/dinou/core/server.js +0 -1542
- package/dinou/esbuild/plugins-esbuild/write-metafile-plugin.mjs +0 -16
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// dinou/node/port-selector.mjs
|
|
2
|
+
// Interactive development port detection and fallback for Dinou.
|
|
3
|
+
// Ensures dual consecutive free ports (HTTP server + HMR / WebSocket server)
|
|
4
|
+
// across all supported bundlers (esbuild, rollup, webpack).
|
|
5
|
+
|
|
6
|
+
import net from "node:net";
|
|
7
|
+
import readline from "node:readline";
|
|
8
|
+
|
|
9
|
+
const C_RESET = "\x1b[0m";
|
|
10
|
+
const C_BOLD = "\x1b[1m";
|
|
11
|
+
const C_DIM = "\x1b[90m";
|
|
12
|
+
const C_CYAN = "\x1b[36m";
|
|
13
|
+
const C_GREEN = "\x1b[32m";
|
|
14
|
+
const C_YELLOW = "\x1b[33m";
|
|
15
|
+
const C_RED = "\x1b[31m";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Checks if a TCP port is currently free on both default and IPv4 loopback.
|
|
19
|
+
* @param {number} port
|
|
20
|
+
* @returns {Promise<boolean>}
|
|
21
|
+
*/
|
|
22
|
+
export function isPortAvailable(port) {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
// 1. Try active connection to 127.0.0.1
|
|
25
|
+
const client = net.connect({ port, host: "127.0.0.1" });
|
|
26
|
+
client.once("connect", () => {
|
|
27
|
+
client.destroy();
|
|
28
|
+
resolve(false);
|
|
29
|
+
});
|
|
30
|
+
client.once("error", () => {
|
|
31
|
+
client.destroy();
|
|
32
|
+
// 2. Try listening on default host
|
|
33
|
+
const s1 = net.createServer();
|
|
34
|
+
s1.unref();
|
|
35
|
+
s1.once("error", () => resolve(false));
|
|
36
|
+
s1.once("listening", () => {
|
|
37
|
+
s1.close(() => {
|
|
38
|
+
// 3. Try listening specifically on 127.0.0.1
|
|
39
|
+
const s2 = net.createServer();
|
|
40
|
+
s2.unref();
|
|
41
|
+
s2.once("error", () => resolve(false));
|
|
42
|
+
s2.once("listening", () => {
|
|
43
|
+
s2.close(() => resolve(true));
|
|
44
|
+
});
|
|
45
|
+
s2.listen(port, "127.0.0.1");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
s1.listen(port);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Checks if both server port and companion HMR port are available.
|
|
55
|
+
* @param {number} serverPort
|
|
56
|
+
* @param {number} hmrPort
|
|
57
|
+
* @returns {Promise<boolean>}
|
|
58
|
+
*/
|
|
59
|
+
export async function arePortsAvailable(serverPort, hmrPort) {
|
|
60
|
+
const isServerFree = await isPortAvailable(serverPort);
|
|
61
|
+
if (!isServerFree) return false;
|
|
62
|
+
const isHmrFree = await isPortAvailable(hmrPort);
|
|
63
|
+
return isHmrFree;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Finds the next available pair of consecutive ports [P, P+1].
|
|
68
|
+
* Keeps even/odd parity so HTTP servers remain on even ports.
|
|
69
|
+
* @param {number} startPort
|
|
70
|
+
* @returns {Promise<number>}
|
|
71
|
+
*/
|
|
72
|
+
export async function findAvailablePortPair(startPort = 3000) {
|
|
73
|
+
let current = startPort;
|
|
74
|
+
while (current < 65534) {
|
|
75
|
+
if (await arePortsAvailable(current, current + 1)) {
|
|
76
|
+
return current;
|
|
77
|
+
}
|
|
78
|
+
current += current % 2 === 0 ? 2 : 1;
|
|
79
|
+
}
|
|
80
|
+
throw new Error("No available port pair found");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Prompts the user interactively in the terminal via readline.
|
|
85
|
+
* @param {string} question
|
|
86
|
+
* @returns {Promise<boolean>}
|
|
87
|
+
*/
|
|
88
|
+
function promptUser(question) {
|
|
89
|
+
return new Promise((resolve) => {
|
|
90
|
+
const rl = readline.createInterface({
|
|
91
|
+
input: process.stdin,
|
|
92
|
+
output: process.stdout,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
rl.on("SIGINT", () => {
|
|
96
|
+
rl.close();
|
|
97
|
+
console.log(`\n\n ${C_RED}❌ Server startup aborted by user.${C_RESET}\n`);
|
|
98
|
+
process.exit(0);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
rl.question(question, (answer) => {
|
|
102
|
+
rl.close();
|
|
103
|
+
const trimmed = answer.trim().toLowerCase();
|
|
104
|
+
if (trimmed === "" || trimmed === "y" || trimmed === "yes") {
|
|
105
|
+
resolve(true);
|
|
106
|
+
} else {
|
|
107
|
+
resolve(false);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Resolves dev server ports.
|
|
115
|
+
* In development, if the target port (or HMR port) is in use:
|
|
116
|
+
* - In interactive TTY: prompts the user to switch to the next consecutive free port pair.
|
|
117
|
+
* - In non-interactive environments (CI, scripts): exits immediately with an error.
|
|
118
|
+
* @returns {Promise<{ port: number, hmrPort: number }>}
|
|
119
|
+
*/
|
|
120
|
+
export async function resolveDevPorts() {
|
|
121
|
+
const requestedPort = Number(process.env.PORT || 3000);
|
|
122
|
+
const requestedHmrPort = Number(process.env.HMR_PORT || (requestedPort + 1));
|
|
123
|
+
|
|
124
|
+
const isServerFree = await isPortAvailable(requestedPort);
|
|
125
|
+
const isHmrFree = await isPortAvailable(requestedHmrPort);
|
|
126
|
+
|
|
127
|
+
if (isServerFree && isHmrFree) {
|
|
128
|
+
process.env.PORT = String(requestedPort);
|
|
129
|
+
process.env.HMR_PORT = String(requestedHmrPort);
|
|
130
|
+
return { port: requestedPort, hmrPort: requestedHmrPort };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const busyPort = !isServerFree ? requestedPort : requestedHmrPort;
|
|
134
|
+
|
|
135
|
+
// Non-interactive environments (CI, pipes) cannot prompt user
|
|
136
|
+
const isInteractive = Boolean(
|
|
137
|
+
(process.stdout.isTTY || process.env.DINOU_FORCE_INTERACTIVE === "true") &&
|
|
138
|
+
!process.env.CI &&
|
|
139
|
+
process.env.TERM !== "dumb"
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
if (!isInteractive) {
|
|
143
|
+
console.error(`\n❌ FATAL ERROR: Port ${busyPort} is already in use!\n`);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Find next consecutive pair (e.g. 3002 & 3003)
|
|
148
|
+
const fallbackPort = await findAvailablePortPair(requestedPort + 2);
|
|
149
|
+
const fallbackHmrPort = fallbackPort + 1;
|
|
150
|
+
|
|
151
|
+
const confirmed = await promptUser(
|
|
152
|
+
`\n ${C_YELLOW}⚠️ Port ${busyPort} is in use.${C_RESET}\n ${C_BOLD}? Would you like to use port ${fallbackPort} instead?${C_RESET} ${C_DIM}(Y/n)${C_RESET} `
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
if (!confirmed) {
|
|
156
|
+
console.log(`\n ${C_RED}❌ Server startup aborted by user.${C_RESET}\n`);
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.log(`\n ${C_GREEN}➜${C_RESET} ${C_BOLD}Using port:${C_RESET} ${C_CYAN}${fallbackPort}${C_RESET} ${C_DIM}(HMR: ${fallbackHmrPort})${C_RESET}\n`);
|
|
161
|
+
process.env.PORT = String(fallbackPort);
|
|
162
|
+
process.env.HMR_PORT = String(fallbackHmrPort);
|
|
163
|
+
return { port: fallbackPort, hmrPort: fallbackHmrPort };
|
|
164
|
+
}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
// dinou/node/terminal-status.mjs
|
|
2
|
+
// Lightweight terminal status & animated spinner for Dinou Development Server.
|
|
3
|
+
// Zero external dependencies. Fully TTY-aware and safe for CI/Playwright.
|
|
4
|
+
|
|
5
|
+
import readline from "node:readline";
|
|
6
|
+
|
|
7
|
+
export const isTTY = Boolean(
|
|
8
|
+
process.stdout.isTTY && !process.env.CI && process.env.TERM !== "dumb"
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
12
|
+
const C_RESET = "\x1b[0m";
|
|
13
|
+
const C_BOLD = "\x1b[1m";
|
|
14
|
+
const C_DIM = "\x1b[90m";
|
|
15
|
+
const C_CYAN = "\x1b[36m";
|
|
16
|
+
const C_GREEN = "\x1b[32m";
|
|
17
|
+
const C_YELLOW = "\x1b[33m";
|
|
18
|
+
const C_MAGENTA = "\x1b[35m";
|
|
19
|
+
|
|
20
|
+
export function getTerminalCols() {
|
|
21
|
+
if (process.stdout.isTTY) {
|
|
22
|
+
if (typeof process.stdout._refreshSize === "function") {
|
|
23
|
+
try {
|
|
24
|
+
process.stdout._refreshSize();
|
|
25
|
+
} catch (e) {}
|
|
26
|
+
}
|
|
27
|
+
if (typeof process.stdout.getWindowSize === "function") {
|
|
28
|
+
try {
|
|
29
|
+
const [w] = process.stdout.getWindowSize();
|
|
30
|
+
if (w > 0) return w;
|
|
31
|
+
} catch (e) {}
|
|
32
|
+
}
|
|
33
|
+
return process.stdout.columns || 80;
|
|
34
|
+
}
|
|
35
|
+
return 80;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let active = false;
|
|
39
|
+
let frameIdx = 0;
|
|
40
|
+
let currentText = "";
|
|
41
|
+
let timer = null;
|
|
42
|
+
let isWatching = false;
|
|
43
|
+
|
|
44
|
+
// Intercept stdout/stderr writes so normal logs don't collide with the spinner line
|
|
45
|
+
let hooked = false;
|
|
46
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
47
|
+
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
48
|
+
|
|
49
|
+
function hookStreams() {
|
|
50
|
+
if (hooked || !isTTY) return;
|
|
51
|
+
hooked = true;
|
|
52
|
+
|
|
53
|
+
process.stdout.write = function (chunk, encoding, callback) {
|
|
54
|
+
if (active) {
|
|
55
|
+
originalStdoutWrite("\r\x1b[2K");
|
|
56
|
+
}
|
|
57
|
+
const result = originalStdoutWrite(chunk, encoding, callback);
|
|
58
|
+
if (active) {
|
|
59
|
+
render();
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
process.stderr.write = function (chunk, encoding, callback) {
|
|
65
|
+
if (active) {
|
|
66
|
+
originalStdoutWrite("\r\x1b[2K");
|
|
67
|
+
}
|
|
68
|
+
const result = originalStderrWrite(chunk, encoding, callback);
|
|
69
|
+
if (active) {
|
|
70
|
+
render();
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function render() {
|
|
77
|
+
if (!active || !isTTY) return;
|
|
78
|
+
const cols = getTerminalCols();
|
|
79
|
+
if (cols <= 0) return;
|
|
80
|
+
if (cols < 8) {
|
|
81
|
+
const frame = FRAMES[frameIdx];
|
|
82
|
+
originalStdoutWrite(`\r\x1b[2K${frame}\x1b[K\x1b[J`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const maxTextLen = Math.max(1, cols - 6);
|
|
86
|
+
let text = currentText;
|
|
87
|
+
if (text.length > maxTextLen) {
|
|
88
|
+
text = text.slice(0, Math.max(1, maxTextLen - 3)) + "...";
|
|
89
|
+
}
|
|
90
|
+
const frame = FRAMES[frameIdx];
|
|
91
|
+
const line = `\r\x1b[2K ${C_CYAN}${C_BOLD}${frame}${C_RESET} ${text}\x1b[K\x1b[J`;
|
|
92
|
+
originalStdoutWrite(line);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function startSpinner(text) {
|
|
96
|
+
if (!isTTY) return;
|
|
97
|
+
hookStreams();
|
|
98
|
+
currentText = text;
|
|
99
|
+
active = true;
|
|
100
|
+
frameIdx = 0;
|
|
101
|
+
if (!timer) {
|
|
102
|
+
timer = setInterval(() => {
|
|
103
|
+
frameIdx = (frameIdx + 1) % FRAMES.length;
|
|
104
|
+
render();
|
|
105
|
+
}, 80);
|
|
106
|
+
timer.unref();
|
|
107
|
+
}
|
|
108
|
+
render();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function updateSpinner(text) {
|
|
112
|
+
currentText = text;
|
|
113
|
+
if (isTTY && active) {
|
|
114
|
+
render();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function stopSpinner() {
|
|
119
|
+
active = false;
|
|
120
|
+
if (timer) {
|
|
121
|
+
clearInterval(timer);
|
|
122
|
+
timer = null;
|
|
123
|
+
}
|
|
124
|
+
if (isTTY) {
|
|
125
|
+
originalStdoutWrite("\r\x1b[2K\x1b[J\r");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let buildFrameIdx = 0;
|
|
130
|
+
let buildTimer = null;
|
|
131
|
+
let currentBuildText = "";
|
|
132
|
+
let isBuildActive = false;
|
|
133
|
+
|
|
134
|
+
function smartTruncate(text, maxLen) {
|
|
135
|
+
if (text.length <= maxLen) return text;
|
|
136
|
+
const m = text.match(/^(\[SSG\].*?:\s+)(.*)$/);
|
|
137
|
+
if (m) {
|
|
138
|
+
const prefix = m[1];
|
|
139
|
+
const route = m[2];
|
|
140
|
+
const avail = maxLen - prefix.length;
|
|
141
|
+
if (avail > 10) {
|
|
142
|
+
const half = Math.floor((avail - 3) / 2);
|
|
143
|
+
return prefix + route.slice(0, half) + "..." + route.slice(route.length - (avail - 3 - half));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (maxLen <= 3) {
|
|
147
|
+
return text.slice(0, maxLen);
|
|
148
|
+
}
|
|
149
|
+
return text.slice(0, maxLen - 3) + "...";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let lastBuildLineLen = 0;
|
|
153
|
+
let lastBuildCols = 0;
|
|
154
|
+
|
|
155
|
+
function renderBuildProgress() {
|
|
156
|
+
if (!isBuildActive || !isTTY) return;
|
|
157
|
+
const cols = getTerminalCols();
|
|
158
|
+
if (cols <= 0) return;
|
|
159
|
+
|
|
160
|
+
// Ultra-narrow terminal (< 8 cols): render just the spinner frame to guarantee 0 wrapping
|
|
161
|
+
if (cols < 8) {
|
|
162
|
+
const frame = FRAMES[buildFrameIdx];
|
|
163
|
+
buildFrameIdx = (buildFrameIdx + 1) % FRAMES.length;
|
|
164
|
+
lastBuildLineLen = 1;
|
|
165
|
+
lastBuildCols = cols;
|
|
166
|
+
process.stdout.write(`\r\x1b[2K${frame}\x1b[K\x1b[J`);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// If terminal narrowed since last render, the previous line reflowed across multiple physical rows.
|
|
171
|
+
// Move up linesOccupied - 1 rows and clear each one to stay on a single line!
|
|
172
|
+
if (lastBuildLineLen > 0 && lastBuildCols > cols) {
|
|
173
|
+
const linesOccupied = Math.min(3, Math.ceil(lastBuildLineLen / Math.max(1, cols)));
|
|
174
|
+
if (linesOccupied > 1) {
|
|
175
|
+
for (let i = 0; i < linesOccupied - 1; i++) {
|
|
176
|
+
process.stdout.write("\x1b[1A\r\x1b[2K");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Dynamically adapt to full terminal width with a 2-column margin against auto-wrap
|
|
182
|
+
const maxLineLen = Math.max(4, cols - 2);
|
|
183
|
+
const maxTextLen = Math.max(1, maxLineLen - 4);
|
|
184
|
+
|
|
185
|
+
const safeText = smartTruncate(currentBuildText, maxTextLen);
|
|
186
|
+
|
|
187
|
+
const frame = FRAMES[buildFrameIdx];
|
|
188
|
+
buildFrameIdx = (buildFrameIdx + 1) % FRAMES.length;
|
|
189
|
+
|
|
190
|
+
lastBuildLineLen = safeText.length + 4;
|
|
191
|
+
lastBuildCols = cols;
|
|
192
|
+
|
|
193
|
+
// \r (carriage return) + \x1b[2K (clear line) + text + \x1b[K (clear trailing) + \x1b[J (clear all lines below)
|
|
194
|
+
process.stdout.write(`\r\x1b[2K ${C_CYAN}${C_BOLD}${frame}${C_RESET} ${safeText}\x1b[K\x1b[J`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function updateBuildProgress(text) {
|
|
198
|
+
if (!isTTY) return;
|
|
199
|
+
currentBuildText = text;
|
|
200
|
+
if (!isBuildActive) {
|
|
201
|
+
isBuildActive = true;
|
|
202
|
+
buildFrameIdx = 0;
|
|
203
|
+
lastBuildLineLen = 0;
|
|
204
|
+
lastBuildCols = 0;
|
|
205
|
+
// Hide cursor for smooth, flicker-free rendering
|
|
206
|
+
process.stdout.write("\x1b[?25l");
|
|
207
|
+
renderBuildProgress();
|
|
208
|
+
if (!buildTimer) {
|
|
209
|
+
buildTimer = setInterval(renderBuildProgress, 80);
|
|
210
|
+
if (buildTimer.unref) buildTimer.unref();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function clearBuildProgress() {
|
|
216
|
+
if (!isTTY) return;
|
|
217
|
+
isBuildActive = false;
|
|
218
|
+
lastBuildLineLen = 0;
|
|
219
|
+
lastBuildCols = 0;
|
|
220
|
+
if (buildTimer) {
|
|
221
|
+
clearInterval(buildTimer);
|
|
222
|
+
buildTimer = null;
|
|
223
|
+
}
|
|
224
|
+
// Clear the line, clear any lines below, and restore cursor visibility
|
|
225
|
+
process.stdout.write("\r\x1b[2K\x1b[J\x1b[?25h");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Debounce terminal resize events so mouse dragging does not flood stdout
|
|
229
|
+
let resizeTimer = null;
|
|
230
|
+
if (typeof process.stdout.on === "function") {
|
|
231
|
+
process.stdout.on("resize", () => {
|
|
232
|
+
if (isBuildActive && isTTY) {
|
|
233
|
+
if (!resizeTimer) {
|
|
234
|
+
resizeTimer = setTimeout(() => {
|
|
235
|
+
resizeTimer = null;
|
|
236
|
+
renderBuildProgress();
|
|
237
|
+
}, 40);
|
|
238
|
+
if (resizeTimer.unref) resizeTimer.unref();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Ensure cursor is always restored on process exit
|
|
245
|
+
process.on("exit", () => {
|
|
246
|
+
if (isTTY) {
|
|
247
|
+
try {
|
|
248
|
+
process.stdout.write("\x1b[?25h");
|
|
249
|
+
} catch (e) {}
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
export function logSuccess(text) {
|
|
254
|
+
stopSpinner();
|
|
255
|
+
console.log(` ${C_GREEN}✓${C_RESET} ${text}`);
|
|
256
|
+
if (isWatching) {
|
|
257
|
+
showIdleStatus();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function logInfo(text) {
|
|
262
|
+
stopSpinner();
|
|
263
|
+
console.log(` ${C_CYAN}ℹ${C_RESET} ${text}`);
|
|
264
|
+
if (isWatching) {
|
|
265
|
+
showIdleStatus();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function showIdleStatus() {
|
|
270
|
+
if (!isTTY) return;
|
|
271
|
+
startSpinner(`${C_DIM}watching for file changes...${C_RESET}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function printReadyBanner({ port, tool, durationMs, timings }) {
|
|
275
|
+
stopSpinner();
|
|
276
|
+
isWatching = true;
|
|
277
|
+
|
|
278
|
+
const durationStr = durationMs ? `${durationMs}ms` : "";
|
|
279
|
+
const timeBadge = durationStr ? ` ${C_DIM}in ${C_BOLD}${durationStr}${C_RESET}` : "";
|
|
280
|
+
|
|
281
|
+
console.log("");
|
|
282
|
+
console.log(` ${C_CYAN}${C_BOLD}🦖 Dinou v7${C_RESET} ${C_GREEN}(Dual-Bundle, 0-fork)${C_RESET}${timeBadge}`);
|
|
283
|
+
console.log("");
|
|
284
|
+
console.log(` ${C_GREEN}➜${C_RESET} ${C_BOLD}Local:${C_RESET} ${C_CYAN}http://localhost:${port}/${C_RESET}`);
|
|
285
|
+
console.log(` ${C_GREEN}➜${C_RESET} ${C_BOLD}Bundler:${C_RESET} ${tool}`);
|
|
286
|
+
const isDebug =
|
|
287
|
+
process.env.DINOU_DEBUG === "true" ||
|
|
288
|
+
process.env.DINOU_DEBUG === "1" ||
|
|
289
|
+
process.env.DEBUG === "true" ||
|
|
290
|
+
process.env.DEBUG === "1";
|
|
291
|
+
|
|
292
|
+
if (timings && isDebug) {
|
|
293
|
+
console.log("");
|
|
294
|
+
console.log(` ${C_BOLD}⚡ Startup Breakdown:${C_RESET}`);
|
|
295
|
+
if (timings.discovery != null) {
|
|
296
|
+
console.log(` ${C_DIM}├─ Discovery & Routing:${C_RESET} ${C_CYAN}${timings.discovery}ms${C_RESET}`);
|
|
297
|
+
}
|
|
298
|
+
if (timings.engineBuild != null) {
|
|
299
|
+
console.log(` ${C_DIM}├─ Dual-Engine (RSC+SSR):${C_RESET} ${C_CYAN}${timings.engineBuild}ms${C_RESET}`);
|
|
300
|
+
}
|
|
301
|
+
if (timings.engineImport != null) {
|
|
302
|
+
console.log(` ${C_DIM}├─ Engine V8 Evaluation:${C_RESET} ${C_CYAN}${timings.engineImport}ms${C_RESET}`);
|
|
303
|
+
}
|
|
304
|
+
if (timings.clientEntriesBabel != null) {
|
|
305
|
+
console.log(` ${C_DIM}├─ Client Babel AST Scan:${C_RESET} ${C_CYAN}${timings.clientEntriesBabel}ms${C_RESET}`);
|
|
306
|
+
}
|
|
307
|
+
if (timings.clientBuild != null) {
|
|
308
|
+
console.log(` ${C_DIM}├─ Client Bundling (esbuild):${C_RESET} ${C_CYAN}${timings.clientBuild}ms${C_RESET}`);
|
|
309
|
+
const subParts = [];
|
|
310
|
+
if (timings.postCss) subParts.push(`PostCSS: ${timings.postCss}ms (${timings.postCssCount || 0} css)`);
|
|
311
|
+
if (timings.swc) subParts.push(`SWC Refresh: ${timings.swc}ms (${timings.swcCount || 0} files)`);
|
|
312
|
+
if (timings.sf) subParts.push(`Server Actions: ${timings.sf}ms (${timings.sfCount || 0} files)`);
|
|
313
|
+
if (timings.rcm) subParts.push(`Client Manifest: ${timings.rcm}ms`);
|
|
314
|
+
if (timings.stable) subParts.push(`Stable Chunks: ${timings.stable}ms`);
|
|
315
|
+
if (timings.writeDisk != null) {
|
|
316
|
+
if (timings.writeDisk === 0) {
|
|
317
|
+
subParts.push("Memory: 0ms (disk skipped)");
|
|
318
|
+
} else {
|
|
319
|
+
subParts.push(`Disk Write: ${timings.writeDisk}ms`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (subParts.length > 0) {
|
|
323
|
+
console.log(` ${C_DIM}│ └─ [${subParts.join(" | ")}]${C_RESET}`);
|
|
324
|
+
}
|
|
325
|
+
} else if (timings.clientBundlerTotal != null) {
|
|
326
|
+
console.log(` ${C_DIM}├─ Client Bundler Total:${C_RESET} ${C_CYAN}${timings.clientBundlerTotal}ms${C_RESET}`);
|
|
327
|
+
}
|
|
328
|
+
console.log(` ${C_DIM}└─ Total Startup Time:${C_RESET} ${C_GREEN}${C_BOLD}${durationMs}ms${C_RESET}`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
console.log("");
|
|
332
|
+
showIdleStatus();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export default {
|
|
336
|
+
startSpinner,
|
|
337
|
+
updateSpinner,
|
|
338
|
+
stopSpinner,
|
|
339
|
+
updateBuildProgress,
|
|
340
|
+
clearBuildProgress,
|
|
341
|
+
logSuccess,
|
|
342
|
+
logInfo,
|
|
343
|
+
showIdleStatus,
|
|
344
|
+
printReadyBanner,
|
|
345
|
+
};
|
package/dinou/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dinou-ejected",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"private": true,
|
|
6
6
|
"exports": {
|
|
@@ -16,7 +16,17 @@
|
|
|
16
16
|
"types": "./server.d.ts",
|
|
17
17
|
"import": "./server.mjs",
|
|
18
18
|
"require": "./server.js"
|
|
19
|
-
}
|
|
19
|
+
},
|
|
20
|
+
"./config": {
|
|
21
|
+
"types": "./index.d.ts",
|
|
22
|
+
"import": "./core/config.js",
|
|
23
|
+
"require": "./core/config.js"
|
|
24
|
+
},
|
|
25
|
+
"./adapters/node": "./adapters/node.mjs",
|
|
26
|
+
"./adapters/bun": "./adapters/bun.js",
|
|
27
|
+
"./adapters/deno": "./adapters/deno.js",
|
|
28
|
+
"./adapters/cloudflare": "./adapters/cloudflare.js",
|
|
29
|
+
"./adapters/netlify": "./adapters/netlify.js"
|
|
20
30
|
},
|
|
21
31
|
"types": "./index.d.ts",
|
|
22
32
|
"scripts": {
|
|
@@ -26,13 +36,11 @@
|
|
|
26
36
|
"@babel/core": "^7.27.4",
|
|
27
37
|
"@babel/parser": "^7.28.3",
|
|
28
38
|
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
|
29
|
-
"@babel/plugin-transform-modules-commonjs": "^7.27.1",
|
|
30
39
|
"@babel/preset-react": "^7.27.1",
|
|
31
40
|
"@babel/preset-typescript": "^7.27.1",
|
|
32
|
-
"@babel/register": "^7.27.1",
|
|
33
41
|
"@babel/traverse": "^7.28.3",
|
|
34
42
|
"@esbuild-plugins/tsconfig-paths": "^0.1.2",
|
|
35
|
-
"@roggc/react-server-dom-esm": "^
|
|
43
|
+
"@roggc/react-server-dom-esm": "^19.3.0",
|
|
36
44
|
"@rollup/plugin-babel": "^6.0.4",
|
|
37
45
|
"@rollup/plugin-commonjs": "^28.0.6",
|
|
38
46
|
"@rollup/plugin-json": "^6.1.0",
|
|
@@ -45,32 +53,26 @@
|
|
|
45
53
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
46
54
|
"chokidar": "^4.0.3",
|
|
47
55
|
"commander": "^14.0.0",
|
|
48
|
-
"concurrently": "^10.0.3",
|
|
49
|
-
"cookie-parser": "^1.4.7",
|
|
50
56
|
"copy-webpack-plugin": "^14.0.0",
|
|
51
57
|
"cross-env": "^7.0.3",
|
|
52
58
|
"css-loader": "^7.1.2",
|
|
53
59
|
"dotenv": "^16.5.0",
|
|
54
60
|
"esbuild": "^0.27.0",
|
|
55
61
|
"esbuild-copy-static-files": "^0.1.0",
|
|
56
|
-
"express": "^5.1.0",
|
|
57
62
|
"generic-names": "^4.0.0",
|
|
58
|
-
"loader-utils": "^3.3.1",
|
|
59
63
|
"mini-css-extract-plugin": "^2.9.4",
|
|
60
|
-
"multer": "^2.0.2",
|
|
61
64
|
"postcss": "^8.5.5",
|
|
62
65
|
"postcss-import": "^16.1.1",
|
|
63
66
|
"postcss-loader": "^8.2.0",
|
|
64
67
|
"postcss-modules": "^6.0.1",
|
|
65
68
|
"react-refresh": "^0.17.0",
|
|
66
|
-
"react-server-dom-webpack": "^19.
|
|
69
|
+
"react-server-dom-webpack": "^19.3.0",
|
|
67
70
|
"rollup": "^4.46.2",
|
|
68
71
|
"rollup-plugin-copy": "^3.5.0",
|
|
69
72
|
"rollup-plugin-delete": "^3.0.1",
|
|
70
73
|
"rollup-plugin-postcss": "^4.0.2",
|
|
71
74
|
"rollup-plugin-tsconfig-paths": "^1.5.2",
|
|
72
75
|
"tailwindcss": "^4.1.10",
|
|
73
|
-
"tsconfig-paths": "^4.2.0",
|
|
74
76
|
"tsconfig-paths-webpack-plugin": "^4.2.0",
|
|
75
77
|
"webpack": "^5.103.0",
|
|
76
78
|
"webpack-cli": "^6.0.1",
|
|
@@ -78,7 +80,7 @@
|
|
|
78
80
|
"ws": "^8.18.3"
|
|
79
81
|
},
|
|
80
82
|
"peerDependencies": {
|
|
81
|
-
"react": ">=19.
|
|
82
|
-
"react-dom": ">=19.
|
|
83
|
+
"react": ">=19.3.0",
|
|
84
|
+
"react-dom": ">=19.3.0"
|
|
83
85
|
}
|
|
84
86
|
}
|