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.
Files changed (135) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +9461 -1080
  3. package/cli.js +392 -169
  4. package/dinou/adapters/bun.js +62 -0
  5. package/dinou/adapters/cloudflare.js +65 -0
  6. package/dinou/adapters/deno.js +48 -0
  7. package/dinou/adapters/netlify.js +85 -0
  8. package/dinou/adapters/node.mjs +31 -0
  9. package/dinou/bun/build.mjs +1077 -0
  10. package/dinou/cloudflare/build.mjs +1121 -0
  11. package/dinou/cloudflare/shims/child_process.js +14 -0
  12. package/dinou/cloudflare/shims/empty.js +2 -0
  13. package/dinou/cloudflare/shims/fs.js +99 -0
  14. package/dinou/cloudflare/shims/os.js +29 -0
  15. package/dinou/config.js +1 -0
  16. package/dinou/config.mjs +2 -0
  17. package/dinou/constants.js +29 -4
  18. package/dinou/core/babel-esm-loader.js +66 -37
  19. package/dinou/core/bailout-proxy.js +61 -0
  20. package/dinou/core/build-static-pages.js +309 -157
  21. package/dinou/core/cache-revalidate.js +471 -144
  22. package/dinou/core/cache-slot.js +387 -0
  23. package/dinou/core/client-redirect.jsx +12 -7
  24. package/dinou/core/client-webpack.jsx +457 -82
  25. package/dinou/core/client.jsx +452 -82
  26. package/dinou/core/config.js +11 -0
  27. package/dinou/core/dinou-paths.js +52 -0
  28. package/dinou/core/edge-ssr.js +122 -0
  29. package/dinou/core/export-static.mjs +64 -0
  30. package/dinou/core/generate-route-types.js +252 -0
  31. package/dinou/core/generate-static-page.js +55 -94
  32. package/dinou/core/generate-static-rsc.js +47 -15
  33. package/dinou/core/generate-static.js +212 -10
  34. package/dinou/core/generating-isg.js +5 -0
  35. package/dinou/core/get-asset-from-manifest.js +9 -3
  36. package/dinou/core/get-error-jsx.js +221 -41
  37. package/dinou/core/get-file-path-and-dynamic-params.js +9 -13
  38. package/dinou/core/get-jsx.js +172 -95
  39. package/dinou/core/handler.js +2296 -0
  40. package/dinou/core/http-adapter.js +115 -0
  41. package/dinou/core/import-module.js +83 -53
  42. package/dinou/core/layout-functions.js +181 -0
  43. package/dinou/core/link.jsx +18 -6
  44. package/dinou/core/load-dinou-config.js +60 -0
  45. package/dinou/core/manifest-provider.js +261 -0
  46. package/dinou/core/navigation-utils.js +4 -0
  47. package/dinou/core/navigation.js +50 -39
  48. package/dinou/core/parse-exports.js +207 -2
  49. package/dinou/core/ppr-context.js +105 -0
  50. package/dinou/core/ppr-runtime.js +263 -0
  51. package/dinou/core/redirect.jsx +20 -19
  52. package/dinou/core/render-jsx-to-client-jsx.js +133 -15
  53. package/dinou/core/request-context.js +47 -13
  54. package/dinou/core/resolve-module-ppr.js +32 -0
  55. package/dinou/core/revalidating.js +28 -9
  56. package/dinou/core/route-generator.js +107 -0
  57. package/dinou/core/rsc-renderer.js +90 -0
  58. package/dinou/core/run-ssg.js +19 -0
  59. package/dinou/core/scan-dependency-components.js +218 -0
  60. package/dinou/core/server-function-proxy-webpack.js +3 -0
  61. package/dinou/core/server-function-proxy.js +3 -0
  62. package/dinou/core/slot.js +302 -0
  63. package/dinou/core/status-manifest.js +41 -1
  64. package/dinou/core/storage-adapter.js +522 -0
  65. package/dinou/core/url-resolver.js +16 -16
  66. package/dinou/core/vfs.js +139 -68
  67. package/dinou/deno/build.mjs +1112 -0
  68. package/dinou/deno/seed-kv.js +115 -0
  69. package/dinou/esbuild/build.mjs +51 -1
  70. package/dinou/esbuild/dev.mjs +290 -224
  71. package/dinou/esbuild/helpers-esbuild/get-config-esbuild-prod.mjs +3 -2
  72. package/dinou/esbuild/helpers-esbuild/get-config-esbuild.mjs +104 -16
  73. package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +31 -19
  74. package/dinou/esbuild/helpers-esbuild/strip-namespace.mjs +9 -0
  75. package/dinou/esbuild/helpers-esbuild/swc-disk-cache.mjs +225 -0
  76. package/dinou/esbuild/helpers-esbuild/update-manifest-for-module.mjs +7 -0
  77. package/dinou/esbuild/helpers-esbuild/write.mjs +111 -2
  78. package/dinou/esbuild/plugins-esbuild/assets-plugin.mjs +236 -161
  79. package/dinou/esbuild/plugins-esbuild/css-processor-plugin.mjs +101 -5
  80. package/dinou/esbuild/plugins-esbuild/react-client-manifest-plugin.mjs +63 -24
  81. package/dinou/esbuild/plugins-esbuild/server-functions-plugin.mjs +177 -56
  82. package/dinou/esbuild/plugins-esbuild/skip-missing-entry-points-plugin.mjs +3 -0
  83. package/dinou/esbuild/plugins-esbuild/stable-chunk-names-and-maps-plugin.mjs +182 -137
  84. package/dinou/esbuild/plugins-postcss/postcss-extract-plugin.js +38 -11
  85. package/dinou/esbuild/react-refresh/esm-hmr/client.mjs +55 -8
  86. package/dinou/esbuild/react-refresh/esm-hmr/server.js +1 -1
  87. package/dinou/esbuild/react-refresh/esm-hmr-plugin.mjs +238 -101
  88. package/dinou/esbuild/react-refresh/is-react-refresh-boundary.mjs +18 -4
  89. package/dinou/index.d.ts +315 -11
  90. package/dinou/index.js +12 -6
  91. package/dinou/index.mjs +9 -0
  92. package/dinou/node/build.mjs +195 -0
  93. package/dinou/node/bundle-dual-engine.mjs +1012 -0
  94. package/dinou/node/dev.mjs +2589 -0
  95. package/dinou/node/port-selector.mjs +164 -0
  96. package/dinou/node/terminal-status.mjs +345 -0
  97. package/dinou/package.json +16 -14
  98. package/dinou/rollup/react-refresh/esm-hmr/client.js +66 -10
  99. package/dinou/rollup/react-refresh/esm-hmr/server.js +1 -1
  100. package/dinou/rollup/react-refresh/is-react-refresh-boundary.js +24 -5
  101. package/dinou/rollup/react-refresh/react-refresh-runtime.js +13 -6
  102. package/dinou/rollup/react-refresh/react-refresh-wrap-modules.js +56 -11
  103. package/dinou/rollup/react-refresh/rollup-plugin-esm-hmr.js +176 -48
  104. package/dinou/rollup/rollup-plugins/dinou-asset-plugin.js +9 -5
  105. package/dinou/rollup/rollup-plugins/rollup-plugin-memory.js +66 -0
  106. package/dinou/rollup/rollup-plugins/rollup-plugin-react-client-manifest.js +468 -123
  107. package/dinou/rollup/rollup-plugins/rollup-plugin-server-functions.js +44 -12
  108. package/dinou/rollup/rollup-plugins/rollup-plugin-swc.js +100 -0
  109. package/dinou/rollup/rollup.config.js +155 -43
  110. package/dinou/server.d.ts +114 -12
  111. package/dinou/server.js +35 -10
  112. package/dinou/server.mjs +30 -9
  113. package/dinou/webpack/helpers/get-webpack-entries.js +14 -19
  114. package/dinou/webpack/loaders/server-functions-loader.js +22 -1
  115. package/dinou/webpack/loaders/swc-loader.js +76 -0
  116. package/dinou/webpack/plugins/manifest-generator-plugin.js +1 -0
  117. package/dinou/webpack/plugins/server-functions-plugin.js +75 -19
  118. package/dinou/webpack/plugins/webpack-memory-plugin.js +66 -0
  119. package/dinou/webpack/webpack.config.js +227 -29
  120. package/eject.js +173 -94
  121. package/package.json +110 -108
  122. package/dinou/core/asset-require-hook.js +0 -58
  123. package/dinou/core/client-error-webpack.jsx +0 -432
  124. package/dinou/core/client-error.jsx +0 -432
  125. package/dinou/core/concurrency-manager.js +0 -48
  126. package/dinou/core/context-proxy.js +0 -68
  127. package/dinou/core/generate-static-pages.js +0 -130
  128. package/dinou/core/generate-static-rscs.js +0 -151
  129. package/dinou/core/get-ssg-metadata.js +0 -31
  130. package/dinou/core/register-loader.mjs +0 -11
  131. package/dinou/core/register-paths.js +0 -27
  132. package/dinou/core/render-app-to-html.js +0 -430
  133. package/dinou/core/render-html.js +0 -407
  134. package/dinou/core/server.js +0 -1542
  135. package/dinou/esbuild/plugins-esbuild/write-metafile-plugin.mjs +0 -16
@@ -0,0 +1,115 @@
1
+ // dinou/core/http-adapter.js
2
+ // Bidirectional adapter between Node.js HTTP (IncomingMessage/ServerResponse)
3
+ // and WHATWG Web Standards (Request/Response/ReadableStream).
4
+
5
+ const { Readable } = require("node:stream");
6
+
7
+ /**
8
+ * Converts a Node.js IncomingMessage / Express Request into a standard Web Request.
9
+ * @param {import('express').Request | import('http').IncomingMessage} req
10
+ * @returns {Request}
11
+ */
12
+ function nodeToWebRequest(req) {
13
+ const protocol = req.headers["x-forwarded-proto"] || (req.socket?.encrypted ? "https" : "http");
14
+ const host = req.headers["x-forwarded-host"] || req.headers.host || "localhost";
15
+ const url = new URL(req.originalUrl || req.url || "/", `${protocol}://${host}`).href;
16
+
17
+ const headers = new Headers();
18
+ for (const [key, value] of Object.entries(req.headers)) {
19
+ if (value === undefined) continue;
20
+ if (Array.isArray(value)) {
21
+ for (const v of value) headers.append(key, v);
22
+ } else {
23
+ headers.set(key, value);
24
+ }
25
+ }
26
+
27
+ const method = req.method || "GET";
28
+ const isBodyAllowed = method !== "GET" && method !== "HEAD";
29
+
30
+ let body = null;
31
+ if (isBodyAllowed) {
32
+ if (req.body && typeof req.body === "object" && !(req.body instanceof Buffer)) {
33
+ const contentType = req.headers["content-type"] || "";
34
+ if (contentType.includes("application/json")) {
35
+ body = JSON.stringify(req.body);
36
+ } else {
37
+ body = JSON.stringify(req.body);
38
+ }
39
+ } else if (Buffer.isBuffer(req.body) || typeof req.body === "string") {
40
+ body = req.body;
41
+ } else if (typeof req.on === "function") {
42
+ // If body hasn't been consumed yet, wrap Node stream into Web ReadableStream
43
+ body = Readable.toWeb(req);
44
+ }
45
+ }
46
+
47
+ const init = {
48
+ method,
49
+ headers,
50
+ body: isBodyAllowed ? body : null,
51
+ };
52
+
53
+ // Node.js fetch / undici requires duplex: 'half' when streaming request bodies
54
+ if (body && typeof body.getReader === "function") {
55
+ init.duplex = "half";
56
+ }
57
+
58
+ return new Request(url, init);
59
+ }
60
+
61
+ /**
62
+ * Sends a standard Web Response into a Node.js ServerResponse / Express Response.
63
+ * @param {Response} webResponse
64
+ * @param {import('express').Response | import('http').ServerResponse} res
65
+ */
66
+ async function sendWebResponseToNode(webResponse, res) {
67
+ res.statusCode = webResponse.status;
68
+ if (webResponse.statusText) {
69
+ res.statusMessage = webResponse.statusText;
70
+ }
71
+
72
+ // Handle cookies (multiple Set-Cookie headers)
73
+ if (typeof webResponse.headers.getSetCookie === "function") {
74
+ const cookies = webResponse.headers.getSetCookie();
75
+ if (cookies && cookies.length > 0) {
76
+ res.setHeader("Set-Cookie", cookies);
77
+ }
78
+ }
79
+
80
+ for (const [key, value] of webResponse.headers.entries()) {
81
+ if (key.toLowerCase() === "set-cookie") {
82
+ if (typeof webResponse.headers.getSetCookie !== "function") {
83
+ res.setHeader("Set-Cookie", value);
84
+ }
85
+ } else {
86
+ res.setHeader(key, value);
87
+ }
88
+ }
89
+
90
+ if (!webResponse.body) {
91
+ return res.end();
92
+ }
93
+
94
+ if (typeof webResponse.body.getReader === "function") {
95
+ const nodeStream = Readable.fromWeb(webResponse.body);
96
+ nodeStream.on("error", (err) => {
97
+ console.error("[http-adapter] Error in response stream:", err);
98
+ if (!res.headersSent) {
99
+ res.statusCode = 500;
100
+ res.end("Internal Server Error");
101
+ } else {
102
+ res.destroy(err);
103
+ }
104
+ });
105
+ nodeStream.pipe(res);
106
+ } else {
107
+ const arrayBuffer = await webResponse.arrayBuffer();
108
+ res.end(Buffer.from(arrayBuffer));
109
+ }
110
+ }
111
+
112
+ module.exports = {
113
+ nodeToWebRequest,
114
+ sendWebResponseToNode,
115
+ };
@@ -1,53 +1,83 @@
1
- const { pathToFileURL } = require("url");
2
- const path = require("path");
3
- const fs = require("fs");
4
- const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
5
-
6
- function getMtimeParam(absPath) {
7
- try {
8
- const stats = fs.statSync(absPath);
9
- return Math.round(stats.mtimeMs);
10
- } catch (e) {
11
- return Date.now();
12
- }
13
- }
14
-
15
- async function importModule(modulePath) {
16
- const absPath = path.isAbsolute(modulePath)
17
- ? modulePath
18
- : path.resolve(process.cwd(), modulePath);
19
-
20
- if (!isWebpack) {
21
- let fileUrl = pathToFileURL(absPath).href;
22
- if (process.env.NODE_ENV !== "production") {
23
- fileUrl += `?mtime=${getMtimeParam(absPath)}`;
24
- }
25
- const mod = await import(fileUrl);
26
- return mod;
27
- }
28
-
29
- try {
30
- if (process.env.NODE_ENV !== "production") {
31
- try {
32
- const resolved = require.resolve(absPath);
33
- delete require.cache[resolved];
34
- } catch (e) {}
35
- }
36
- return require(absPath);
37
- } catch (err) {
38
- if (
39
- err.code === "ERR_REQUIRE_ESM" ||
40
- /require\(\) of ES Module/.test(err.message)
41
- ) {
42
- let fileUrl = pathToFileURL(absPath).href;
43
- if (process.env.NODE_ENV !== "production") {
44
- fileUrl += `?mtime=${getMtimeParam(absPath)}`;
45
- }
46
- const mod = await import(fileUrl);
47
- return mod;
48
- }
49
- throw err;
50
- }
51
- }
52
-
53
- module.exports = importModule;
1
+ const { pathToFileURL } = require("url");
2
+ const path = require("path");
3
+ const fs = require("fs");
4
+
5
+ function getMtimeParam(absPath) {
6
+ try {
7
+ const stats = fs.statSync(absPath);
8
+ return Math.round(stats.mtimeMs);
9
+ } catch (e) {
10
+ return Date.now();
11
+ }
12
+ }
13
+
14
+ function normalizeKey(p) {
15
+ if (!p) return "";
16
+ let s = String(p).replace(/\\/g, "/");
17
+ s = s.replace(/^file:\/\/\/?/, "");
18
+ if (s.length > 2 && s[1] === ":") s = s.slice(2);
19
+ const srcIdx = s.indexOf("/src/");
20
+ if (srcIdx !== -1) {
21
+ s = s.slice(srcIdx + 1);
22
+ } else if (s.startsWith("/src/")) {
23
+ s = s.slice(1);
24
+ }
25
+ const cwd = typeof process !== "undefined" && typeof process.cwd === "function"
26
+ ? process.cwd().replace(/\\/g, "/")
27
+ : "";
28
+ if (cwd && s.startsWith(cwd)) {
29
+ s = s.slice(cwd.length);
30
+ }
31
+ s = s.replace(/^\/+/, "").replace(/^\.\//, "");
32
+ return s;
33
+ }
34
+
35
+ async function importModule(modulePath) {
36
+ const registry = typeof globalThis !== "undefined" ? globalThis.__DINOU_ROUTE_MODULES__ : null;
37
+ if (registry) {
38
+ const key = normalizeKey(modulePath);
39
+ if (registry[key]) {
40
+ const loader = registry[key];
41
+ return typeof loader === "function" ? await loader() : loader;
42
+ }
43
+ // Also try with or without extensions / leading src/
44
+ const alternateKeys = [
45
+ key,
46
+ key.startsWith("src/") ? key.slice(4) : "src/" + key,
47
+ key.replace(/\.[jt]sx?$/, ""),
48
+ key.startsWith("src/") ? key : "src/" + key,
49
+ key.startsWith("/") ? key : "/" + key,
50
+ ];
51
+ for (const alt of alternateKeys) {
52
+ if (registry[alt]) {
53
+ const loader = registry[alt];
54
+ return typeof loader === "function" ? await loader() : loader;
55
+ }
56
+ }
57
+ }
58
+
59
+ const absPath = path.isAbsolute(modulePath)
60
+ ? modulePath
61
+ : path.resolve(process.cwd(), modulePath);
62
+
63
+ let fileUrl = pathToFileURL(absPath).href;
64
+ if (process.env.NODE_ENV !== "production") {
65
+ fileUrl += `?mtime=${getMtimeParam(absPath)}`;
66
+ }
67
+
68
+ const mod = await import(/* webpackIgnore: true */ fileUrl);
69
+ return mod;
70
+ }
71
+
72
+ function registerRouteModules(modulesMap) {
73
+ if (typeof globalThis !== "undefined") {
74
+ globalThis.__DINOU_ROUTE_MODULES__ = {
75
+ ...(globalThis.__DINOU_ROUTE_MODULES__ || {}),
76
+ ...modulesMap,
77
+ };
78
+ }
79
+ }
80
+
81
+ importModule.register = registerRouteModules;
82
+
83
+ module.exports = importModule;
@@ -0,0 +1,181 @@
1
+ const path = require("path");
2
+ const { existsSync } = require("./vfs");
3
+ const importModule = require("./import-module");
4
+ const { resolveModulePpr } = require("./resolve-module-ppr");
5
+
6
+ const LAYOUT_FUNCTIONS_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js"];
7
+ const LAYOUT_FUNCTIONS_NAMES = ["layout_functions", "layout.functions"];
8
+
9
+ /**
10
+ * Finds the layout_functions file in the given layout directory, if it exists.
11
+ * @param {string} layoutFolderPath
12
+ * @returns {string | null}
13
+ */
14
+ function getLayoutFunctionsPath(layoutFolderPath) {
15
+ if (!layoutFolderPath) return null;
16
+ for (const name of LAYOUT_FUNCTIONS_NAMES) {
17
+ for (const ext of LAYOUT_FUNCTIONS_EXTENSIONS) {
18
+ const candidate = path.join(layoutFolderPath, `${name}${ext}`);
19
+ if (existsSync(candidate)) {
20
+ return candidate;
21
+ }
22
+ const slashCandidate = candidate.replace(/\\/g, "/");
23
+ if (existsSync(slashCandidate)) {
24
+ return slashCandidate;
25
+ }
26
+ if (typeof globalThis !== "undefined" && globalThis.__DINOU_ROUTE_MODULES__) {
27
+ const norm = slashCandidate.replace(/^file:\/\/\/?/, "").replace(/^\/+/, "");
28
+ const clean = norm.startsWith("src/") ? norm : "src/" + norm.replace(/^.*\/src\//, "");
29
+ if (globalThis.__DINOU_ROUTE_MODULES__[clean] || globalThis.__DINOU_ROUTE_MODULES__[norm]) {
30
+ return clean;
31
+ }
32
+ }
33
+ }
34
+ }
35
+ return null;
36
+ }
37
+
38
+ /**
39
+ * Executes getProps from layout_functions for a given layout, returning the props directly.
40
+ * @param {string} layoutPath
41
+ * @param {object} dParams
42
+ * @returns {Promise<object>}
43
+ */
44
+ async function getLayoutProps(layoutPath, dParams = {}) {
45
+ if (!layoutPath) return {};
46
+ const layoutFolder = /\.[a-zA-Z0-9]+$/.test(layoutPath)
47
+ ? path.dirname(layoutPath)
48
+ : layoutPath;
49
+ const layoutFunctionsPath = getLayoutFunctionsPath(layoutFolder);
50
+ if (!layoutFunctionsPath) return {};
51
+
52
+ try {
53
+ const mod = await importModule(layoutFunctionsPath);
54
+ const getPropsFn =
55
+ mod.getProps ||
56
+ mod.default?.getProps ||
57
+ (typeof mod.default === "function" ? mod.default : null);
58
+ if (typeof getPropsFn === "function") {
59
+ const res = await getPropsFn(dParams);
60
+ return res && typeof res === "object" ? res : {};
61
+ }
62
+ } catch (err) {
63
+ console.error(`[Dinou] Error executing getProps in ${layoutFunctionsPath}:`, err);
64
+ }
65
+ return {};
66
+ }
67
+
68
+ /**
69
+ * Resolves layout_functions configuration (revalidate, tags, dynamic, allowISG, validateParams).
70
+ * @param {string} layoutPath
71
+ * @param {object} dParams
72
+ * @returns {Promise<object>}
73
+ */
74
+ async function resolveLayoutFunctionsConfig(layoutPath, dParams = {}) {
75
+ const defaultConfig = {
76
+ allowISG: true,
77
+ staticPathsSet: null,
78
+ validateParams: null,
79
+ isDynamic: false,
80
+ revalidate: undefined,
81
+ tags: [],
82
+ getProps: null,
83
+ ppr: null,
84
+ };
85
+
86
+ if (!layoutPath) return defaultConfig;
87
+
88
+ const layoutFolder = /\.[a-zA-Z0-9]+$/.test(layoutPath)
89
+ ? path.dirname(layoutPath)
90
+ : layoutPath;
91
+ const layoutFunctionsPath = getLayoutFunctionsPath(layoutFolder);
92
+ if (!layoutFunctionsPath) return defaultConfig;
93
+
94
+ let layoutPpr = null;
95
+ try {
96
+ const mod = await importModule(layoutFunctionsPath);
97
+ if (mod) {
98
+ layoutPpr = await resolveModulePpr(mod);
99
+ }
100
+ if (layoutPpr === null && typeof globalThis !== "undefined" && globalThis.__DINOU_ROUTE_METADATA__) {
101
+ const normKey = layoutFunctionsPath ? layoutFunctionsPath.replace(/\\/g, "/") : "";
102
+ for (const [k, meta] of Object.entries(globalThis.__DINOU_ROUTE_METADATA__)) {
103
+ if (meta.ppr !== undefined && (normKey === k || normKey.endsWith("/" + k) || normKey.endsWith(k))) {
104
+ layoutPpr = Boolean(meta.ppr);
105
+ break;
106
+ }
107
+ }
108
+ }
109
+ const resolvedAllowISG =
110
+ typeof mod.allowISG === "function"
111
+ ? await mod.allowISG()
112
+ : typeof mod.default?.allowISG === "function"
113
+ ? await mod.default.allowISG()
114
+ : (mod.allowISG ?? mod.default?.allowISG ?? true);
115
+
116
+ const getStaticPathsFn = mod.getStaticPaths || mod.default?.getStaticPaths;
117
+ let staticPathsSet = null;
118
+ if (getStaticPathsFn) {
119
+ const paths = await (typeof getStaticPathsFn === "function" ? getStaticPathsFn() : getStaticPathsFn);
120
+ if (Array.isArray(paths)) {
121
+ staticPathsSet = new Set(
122
+ paths.map((p) => {
123
+ if (typeof p === "object" && p !== null && !Array.isArray(p)) {
124
+ const sorted = Object.entries(p).sort((a, b) => a[0].localeCompare(b[0]));
125
+ return JSON.stringify(sorted);
126
+ }
127
+ return String(p);
128
+ })
129
+ );
130
+ }
131
+ }
132
+
133
+ const revalidateVal =
134
+ typeof mod.revalidate === "function"
135
+ ? await mod.revalidate()
136
+ : typeof mod.default?.revalidate === "function"
137
+ ? await mod.default.revalidate()
138
+ : (mod.revalidate ?? mod.default?.revalidate);
139
+
140
+ const isDynamic = typeof revalidateVal === "number" && revalidateVal < 1;
141
+
142
+ const rawTags = mod.getCacheTags ?? mod.default?.getCacheTags;
143
+ let tagsVal = [];
144
+ if (typeof rawTags === "function") {
145
+ try {
146
+ tagsVal = await rawTags(dParams);
147
+ } catch (e) {
148
+ console.error("Error running getCacheTags in layout-functions:", e);
149
+ }
150
+ } else if (Array.isArray(rawTags)) {
151
+ tagsVal = rawTags;
152
+ }
153
+
154
+ const validateParamsFn = mod.validateParams || mod.default?.validateParams;
155
+ const getPropsFn =
156
+ mod.getProps ||
157
+ mod.default?.getProps ||
158
+ (typeof mod.default === "function" ? mod.default : null);
159
+
160
+ return {
161
+ allowISG: resolvedAllowISG,
162
+ staticPathsSet,
163
+ validateParams: typeof validateParamsFn === "function" ? validateParamsFn : null,
164
+ isDynamic,
165
+ revalidate: revalidateVal,
166
+ tags: Array.isArray(tagsVal) ? tagsVal : [],
167
+ getProps: typeof getPropsFn === "function" ? getPropsFn : null,
168
+ ppr: layoutPpr,
169
+ };
170
+ } catch (err) {
171
+ console.error(`[Dinou] Error resolving layout_functions config from ${layoutFunctionsPath}:`, err);
172
+ return { ...defaultConfig, ppr: layoutPpr };
173
+ }
174
+ }
175
+
176
+ module.exports = {
177
+ getLayoutFunctionsPath,
178
+ getLayoutProps,
179
+ resolveLayoutFunctionsConfig,
180
+ };
181
+
@@ -5,37 +5,49 @@ import { resolveUrl, isExternalUrl } from "./navigation-utils.js";
5
5
 
6
6
  export function Link({
7
7
  href,
8
+ to,
8
9
  children,
9
10
  prefetch = true,
10
11
  fresh = false,
12
+ onClick,
11
13
  ...props
12
14
  }) {
15
+ const targetHref = href || to || "";
13
16
  const { push } = useRouter();
14
17
  const pathname = usePathname();
15
- const resolvedHref = resolveUrl(href, pathname);
18
+ const resolvedHref = resolveUrl(targetHref, pathname);
16
19
 
17
20
  const handlePrefetch = () => {
18
- if (!prefetch || !href || fresh || isExternalUrl(href)) return;
21
+ if (!prefetch || !targetHref || fresh || isExternalUrl(targetHref)) return;
19
22
  if (window.__DINOU_PREFETCH__) {
20
23
  window.__DINOU_PREFETCH__(resolvedHref);
21
24
  }
22
25
  };
23
26
 
24
27
  const handleClick = (e) => {
25
- if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || isExternalUrl(href)) return;
28
+ if (onClick) {
29
+ onClick(e);
30
+ }
31
+
32
+ if (e.defaultPrevented) return;
33
+ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || isExternalUrl(targetHref)) return;
26
34
 
27
35
  e.preventDefault();
28
- push(href, { fresh });
36
+ if (targetHref) {
37
+ push(targetHref, { fresh });
38
+ }
29
39
  };
30
40
 
31
41
  return (
32
42
  <a
33
- href={resolvedHref}
43
+ {...props}
44
+ href={resolvedHref || undefined}
34
45
  onClick={handleClick}
35
46
  onMouseEnter={handlePrefetch}
36
- {...props}
37
47
  >
38
48
  {children}
39
49
  </a>
40
50
  );
41
51
  }
52
+
53
+ export default Link;
@@ -0,0 +1,60 @@
1
+ const path = require("path");
2
+ const { existsSync } = require("fs");
3
+ const { pathToFileURL } = require("url");
4
+
5
+ let dinouConfig = { plugins: [] };
6
+ let dinouConfigPromise = null;
7
+
8
+ async function getDinouConfig() {
9
+ if (typeof globalThis !== "undefined" && globalThis.__DINOU_CONFIG__) {
10
+ return globalThis.__DINOU_CONFIG__;
11
+ }
12
+ if (dinouConfigPromise) return dinouConfigPromise;
13
+ dinouConfigPromise = (async () => {
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"]) {
16
+ const p = path.resolve(cwd, filename);
17
+ if (existsSync(p)) {
18
+ try {
19
+ let loaded;
20
+ const isDev = process.env.NODE_ENV !== "production";
21
+ if (p.endsWith(".mjs")) {
22
+ loaded = await import(pathToFileURL(p).href + (isDev ? `?t=${Date.now()}` : ""));
23
+ } else {
24
+ const nodeReq =
25
+ typeof globalThis !== "undefined" && typeof globalThis.__dinou_require__ === "function"
26
+ ? globalThis.__dinou_require__
27
+ : typeof require === "function"
28
+ ? require
29
+ : null;
30
+ if (nodeReq) {
31
+ try {
32
+ loaded = nodeReq(p);
33
+ } catch (e) {
34
+ loaded = await import(pathToFileURL(p).href + (isDev ? `?t=${Date.now()}` : ""));
35
+ }
36
+ } else {
37
+ loaded = await import(pathToFileURL(p).href + (isDev ? `?t=${Date.now()}` : ""));
38
+ }
39
+ }
40
+ const cfg = loaded && loaded.default ? loaded.default : loaded || { plugins: [] };
41
+ dinouConfig = cfg;
42
+ return cfg;
43
+ } catch (err) {
44
+ console.error(`[Dinou] Error loading ${filename}:`, err);
45
+ }
46
+ }
47
+ }
48
+ return dinouConfig;
49
+ })();
50
+ return dinouConfigPromise;
51
+ }
52
+
53
+ // Initial sync attempt if already set on globalThis
54
+ if (typeof globalThis !== "undefined" && globalThis.__DINOU_CONFIG__) {
55
+ dinouConfig = globalThis.__DINOU_CONFIG__;
56
+ }
57
+
58
+ module.exports = {
59
+ getDinouConfig,
60
+ };