dinou 2.4.0 → 3.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 (53) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/cli.js +110 -6
  3. package/dinou/core/asset-extensions.js +3 -1
  4. package/dinou/core/client-error-webpack.jsx +36 -0
  5. package/dinou/core/client-webpack.jsx +23 -0
  6. package/dinou/core/get-asset-from-manifest.js +25 -0
  7. package/dinou/core/render-html.js +8 -4
  8. package/dinou/core/server-function-proxy-webpack.js +23 -0
  9. package/dinou/core/server.js +7 -10
  10. package/dinou/esbuild/build.mjs +65 -0
  11. package/dinou/esbuild/dev.mjs +163 -0
  12. package/dinou/esbuild/helpers-esbuild/get-config-esbuild-prod.mjs +56 -0
  13. package/dinou/esbuild/helpers-esbuild/get-config-esbuild.mjs +55 -0
  14. package/dinou/esbuild/helpers-esbuild/get-esbuild-entries.mjs +366 -0
  15. package/dinou/esbuild/helpers-esbuild/normalize-path.mjs +5 -0
  16. package/dinou/esbuild/helpers-esbuild/write.mjs +59 -0
  17. package/dinou/esbuild/plugins-esbuild/assets-plugin.mjs +241 -0
  18. package/dinou/esbuild/plugins-esbuild/css-processor-plugin.mjs +82 -0
  19. package/dinou/esbuild/plugins-esbuild/manifest-generator-plugin.mjs +55 -0
  20. package/dinou/esbuild/plugins-esbuild/react-client-manifest-plugin.mjs +59 -0
  21. package/dinou/esbuild/plugins-esbuild/server-functions-plugin.mjs +110 -0
  22. package/dinou/esbuild/plugins-esbuild/skip-missing-entry-points-plugin.mjs +34 -0
  23. package/dinou/esbuild/plugins-esbuild/stable-chunk-names-and-maps-plugin.mjs +155 -0
  24. package/dinou/esbuild/plugins-esbuild/write-metafile-plugin.mjs +16 -0
  25. package/dinou/esbuild/plugins-esbuild/write-plugin.mjs +10 -0
  26. package/dinou/esbuild/plugins-postcss/postcss-extract-plugin.js +47 -0
  27. package/dinou/esbuild/react-refresh/babel-config.js +13 -0
  28. package/dinou/esbuild/react-refresh/esm-hmr/client.mjs +159 -0
  29. package/dinou/esbuild/react-refresh/esm-hmr/server.js +134 -0
  30. package/dinou/esbuild/react-refresh/esm-hmr-plugin.mjs +224 -0
  31. package/dinou/esbuild/react-refresh/react-refresh-runtime.mjs +31 -0
  32. package/dinou/rollup/react-refresh/is-react-refresh-boundary.js +25 -0
  33. package/dinou/rollup/react-refresh/react-refresh-entry.js +3 -0
  34. package/dinou/{rollup-plugins → rollup/rollup-plugins}/dinou-asset-plugin.js +2 -2
  35. package/dinou/rollup/rollup-plugins/manifest-generator-plugin.js +25 -0
  36. package/dinou/{rollup-plugins → rollup/rollup-plugins}/rollup-plugin-react-client-manifest.js +3 -3
  37. package/dinou/{rollup-plugins → rollup/rollup-plugins}/rollup-plugin-server-functions.js +19 -1
  38. package/dinou/{rollup.config.js → rollup/rollup.config.js} +17 -11
  39. package/dinou/webpack/loaders/server-functions-loader.js +69 -0
  40. package/dinou/webpack/plugins/manifest-generator-plugin.js +45 -0
  41. package/dinou/webpack/plugins/server-functions-plugin.js +44 -0
  42. package/dinou/webpack/postcss.config.js +6 -0
  43. package/dinou/webpack/webpack.config.js +228 -0
  44. package/eject.js +27 -8
  45. package/package.json +21 -3
  46. /package/dinou/{react-refresh/is-react-refresh-boundary.js → esbuild/react-refresh/is-react-refresh-boundary.mjs} +0 -0
  47. /package/dinou/{react-refresh → esbuild/react-refresh}/react-refresh-entry.js +0 -0
  48. /package/dinou/{postcss.config.js → rollup/postcss.config.js} +0 -0
  49. /package/dinou/{react-refresh → rollup/react-refresh}/esm-hmr/client.js +0 -0
  50. /package/dinou/{react-refresh → rollup/react-refresh}/esm-hmr/server.js +0 -0
  51. /package/dinou/{react-refresh → rollup/react-refresh}/react-refresh-runtime.js +0 -0
  52. /package/dinou/{react-refresh → rollup/react-refresh}/react-refresh-wrap-modules.js +0 -0
  53. /package/dinou/{react-refresh → rollup/react-refresh}/rollup-plugin-esm-hmr.js +0 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ 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
+ ## [3.0.0]
9
+
10
+ ### Added
11
+
12
+ - esbuild integration (the one used by default).
13
+ - rollup integration (optionally used).
14
+ - webpack integration (optionally used).
15
+
16
+ ## [2.4.1]
17
+
18
+ ### Fixed
19
+
20
+ - Use hashed names for bundled files in production.
21
+
8
22
  ## [2.4.0]
9
23
 
10
24
  ### Fixed
package/cli.js CHANGED
@@ -26,24 +26,90 @@ program
26
26
  const startExpress = `node --conditions react-server --import ${
27
27
  pathToFileURL(path.join(corePath, "register-loader.mjs")).href
28
28
  } ${path.join(corePath, "server.js")}`;
29
- const startDevServer = `cross-env NODE_ENV=development rollup -c ${path.join(
29
+ const startDevServer = `node ${path.join(dinouPath, "esbuild/dev.mjs")}`;
30
+ runCommand(`npx concurrently "${startExpress}" "${startDevServer}"`);
31
+ });
32
+
33
+ program
34
+ .command("build")
35
+ .description("Builds the app for production")
36
+ .action(() => {
37
+ console.log("Building the app...");
38
+ const esbuildPath = path.join(dinouPath, "esbuild/build.mjs");
39
+ runCommand(`cross-env NODE_ENV=production node ${esbuildPath}`);
40
+ });
41
+
42
+ program
43
+ .command("start")
44
+ .description("Start the app in production mode")
45
+ .action(() => {
46
+ console.log("Starting the app...");
47
+ runCommand(
48
+ `cross-env NODE_ENV=production node --conditions react-server --import ${
49
+ pathToFileURL(path.join(corePath, "register-loader.mjs")).href
50
+ } ${path.join(corePath, "server.js")}`
51
+ );
52
+ });
53
+
54
+ program
55
+ .command("dev:esbuild")
56
+ .description("Starts")
57
+ .action(() => {
58
+ console.log("Starting...");
59
+ const startExpress = `node --conditions react-server --import ${
60
+ pathToFileURL(path.join(corePath, "register-loader.mjs")).href
61
+ } ${path.join(corePath, "server.js")}`;
62
+ const startDevServer = `node ${path.join(dinouPath, "esbuild/dev.mjs")}`;
63
+ runCommand(`npx concurrently "${startExpress}" "${startDevServer}"`);
64
+ });
65
+
66
+ program
67
+ .command("build:esbuild")
68
+ .description("Builds the app for production")
69
+ .action(() => {
70
+ console.log("Building the app...");
71
+ const esbuildPath = path.join(dinouPath, "esbuild/build.mjs");
72
+ runCommand(`cross-env NODE_ENV=production node ${esbuildPath}`);
73
+ });
74
+
75
+ program
76
+ .command("start:esbuild")
77
+ .description("Start the app in production mode")
78
+ .action(() => {
79
+ console.log("Starting the app...");
80
+ runCommand(
81
+ `cross-env NODE_ENV=production node --conditions react-server --import ${
82
+ pathToFileURL(path.join(corePath, "register-loader.mjs")).href
83
+ } ${path.join(corePath, "server.js")}`
84
+ );
85
+ });
86
+
87
+ program
88
+ .command("dev:rollup")
89
+ .description("Starts")
90
+ .action(() => {
91
+ console.log("Starting...");
92
+ const startExpress = `node --conditions react-server --import ${
93
+ pathToFileURL(path.join(corePath, "register-loader.mjs")).href
94
+ } ${path.join(corePath, "server.js")}`;
95
+ const startDevServer = `rollup -c ${path.join(
30
96
  dinouPath,
31
- "rollup.config.js"
97
+ "rollup/rollup.config.js"
32
98
  )} -w`;
33
99
  runCommand(`npx concurrently "${startExpress}" "${startDevServer}"`);
34
100
  });
35
101
 
36
102
  program
37
- .command("build")
103
+ .command("build:rollup")
38
104
  .description("Builds the app for production")
39
105
  .action(() => {
40
106
  console.log("Building the app...");
41
- const configPath = path.join(dinouPath, "rollup.config.js");
107
+ const configPath = path.join(dinouPath, "rollup/rollup.config.js");
42
108
  runCommand(`cross-env NODE_ENV=production npx rollup -c ${configPath}`);
43
109
  });
44
110
 
45
111
  program
46
- .command("start")
112
+ .command("start:rollup")
47
113
  .description("Start the app in production mode")
48
114
  .action(() => {
49
115
  console.log("Starting the app...");
@@ -54,9 +120,47 @@ program
54
120
  );
55
121
  });
56
122
 
123
+ program
124
+ .command("dev:webpack")
125
+ .description("Starts")
126
+ .action(() => {
127
+ console.log("Starting...");
128
+ const startExpress = `cross-env DINOU_BUILD_TOOL=webpack node --conditions react-server --import ${
129
+ pathToFileURL(path.join(corePath, "register-loader.mjs")).href
130
+ } ${path.join(corePath, "server.js")}`;
131
+ const startDevServer = `webpack serve --config ${path.join(
132
+ dinouPath,
133
+ "webpack/webpack.config.js"
134
+ )}`;
135
+ runCommand(`npx concurrently "${startExpress}" "${startDevServer}"`);
136
+ });
137
+
138
+ program
139
+ .command("build:webpack")
140
+ .description("Builds the app for production")
141
+ .action(() => {
142
+ console.log("Building the app...");
143
+ const configPath = path.join(dinouPath, "webpack/webpack.config.js");
144
+ runCommand(
145
+ `cross-env NODE_ENV=production npx webpack --config ${configPath}`
146
+ );
147
+ });
148
+
149
+ program
150
+ .command("start:webpack")
151
+ .description("Start the app in production mode")
152
+ .action(() => {
153
+ console.log("Starting the app...");
154
+ runCommand(
155
+ `cross-env NODE_ENV=production DINOU_BUILD_TOOL=webpack node --conditions react-server --import ${
156
+ pathToFileURL(path.join(corePath, "register-loader.mjs")).href
157
+ } ${path.join(corePath, "server.js")}`
158
+ );
159
+ });
160
+
57
161
  program
58
162
  .command("eject")
59
- .description("Copy the framework config to the root of the project")
163
+ .description("Copy the framework to the root of the project")
60
164
  .action(() => {
61
165
  console.log("Executing eject...");
62
166
  require("./eject");
@@ -22,10 +22,12 @@ const extensions = [
22
22
  "mjpg",
23
23
  ];
24
24
 
25
+ const globPattern = `**/*.{${extensions.join(",")}}`;
26
+
25
27
  // 🔹 regex útil para plugins tipo Rollup/PostCSS
26
28
  const regex = new RegExp(`\\.(${extensions.join("|")})$`, "i");
27
29
 
28
30
  // 🔹 versión con punto para comparaciones directas
29
31
  const extensionsWithDot = extensions.map((ext) => `.${ext}`);
30
32
 
31
- module.exports = { extensions, extensionsWithDot, regex };
33
+ module.exports = { extensions, extensionsWithDot, regex, globPattern };
@@ -0,0 +1,36 @@
1
+ import { use } from "react";
2
+ import { createFromFetch } from "react-server-dom-webpack/client";
3
+ import { hydrateRoot } from "react-dom/client";
4
+
5
+ const cache = new Map();
6
+ const route = window.location.href.replace(window.location.origin, "");
7
+
8
+ function Root() {
9
+ let content = cache.get(route);
10
+ if (!content) {
11
+ content = createFromFetch(
12
+ fetch("/____rsc_payload_error____" + route, {
13
+ method: "POST",
14
+ headers: {
15
+ "Content-Type": "application/json",
16
+ },
17
+ body: JSON.stringify({
18
+ error: {
19
+ message: window.__DINOU_ERROR_MESSAGE__ || "Unknown error",
20
+ stack: window.__DINOU_ERROR_STACK__ || "No stack trace available",
21
+ },
22
+ }),
23
+ })
24
+ );
25
+ cache.set(route, content);
26
+ }
27
+
28
+ return use(content);
29
+ }
30
+
31
+ hydrateRoot(document, <Root />);
32
+
33
+ // HMR
34
+ if (import.meta.hot) {
35
+ import.meta.hot.accept();
36
+ }
@@ -0,0 +1,23 @@
1
+ import { use } from "react";
2
+ import { createFromFetch } from "react-server-dom-webpack/client";
3
+ import { hydrateRoot } from "react-dom/client";
4
+
5
+ const cache = new Map();
6
+ const route = window.location.href.replace(window.location.origin, "");
7
+
8
+ function Root() {
9
+ let content = cache.get(route);
10
+ if (!content) {
11
+ content = createFromFetch(fetch("/____rsc_payload____" + route));
12
+ cache.set(route, content);
13
+ }
14
+
15
+ return use(content);
16
+ }
17
+
18
+ hydrateRoot(document, <Root />);
19
+
20
+ // HMR
21
+ if (import.meta.hot) {
22
+ import.meta.hot.accept();
23
+ }
@@ -0,0 +1,25 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ let manifest = {};
5
+ let read = false;
6
+ const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
7
+
8
+ function getAssetFromManifest(name) {
9
+ if (process.env.NODE_ENV === "production" && !read) {
10
+ const manifestPath = path.resolve(process.cwd(), "dist3/manifest.json");
11
+ if (fs.existsSync(manifestPath)) {
12
+ manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
13
+ read = true;
14
+ }
15
+ } else if (isWebpack && !read) {
16
+ const manifestPath = path.resolve(process.cwd(), "public/manifest.json");
17
+ if (fs.existsSync(manifestPath)) {
18
+ manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
19
+ read = true;
20
+ }
21
+ }
22
+ return "/" + (manifest[name] || name);
23
+ }
24
+
25
+ module.exports = getAssetFromManifest;
@@ -23,13 +23,14 @@ addHook({
23
23
  },
24
24
  publicPath: "/assets/",
25
25
  });
26
-
26
+ const getAssetFromManifest = require("./get-asset-from-manifest.js");
27
27
  const { renderToPipeableStream } = require("react-dom/server");
28
28
  const getJSX = require("./get-jsx");
29
29
  const getSSGJSX = require("./get-ssg-jsx.js");
30
30
  const { getErrorJSX } = require("./get-error-jsx");
31
31
  const { renderJSXToClientJSX } = require("./render-jsx-to-client-jsx");
32
32
  const isDevelopment = process.env.NODE_ENV !== "production";
33
+ const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
33
34
 
34
35
  function formatErrorHtml(error) {
35
36
  const message = error.message || "Unknown error";
@@ -157,7 +158,7 @@ async function renderToStream(reqPath, query, cookies = {}) {
157
158
  writeErrorOutput(error, isProd);
158
159
  process.exit(1);
159
160
  },
160
- bootstrapModules: ["/error.js"],
161
+ bootstrapModules: [getAssetFromManifest("error.js")],
161
162
  bootstrapScriptContent: `window.__DINOU_ERROR_MESSAGE__=${JSON.stringify(
162
163
  error.message || "Unknown error"
163
164
  )};window.__DINOU_ERROR_STACK__=${JSON.stringify(
@@ -175,8 +176,11 @@ async function renderToStream(reqPath, query, cookies = {}) {
175
176
  stream.pipe(process.stdout);
176
177
  },
177
178
  bootstrapModules: isDevelopment
178
- ? ["/main.js", "/runtime.js"]
179
- : ["/main.js"],
179
+ ? [
180
+ getAssetFromManifest("main.js"),
181
+ isWebpack ? undefined : getAssetFromManifest("runtime.js"),
182
+ ].filter(Boolean)
183
+ : [getAssetFromManifest("main.js")],
180
184
  ...(isDevelopment
181
185
  ? {
182
186
  bootstrapScriptContent: `window.HMR_WEBSOCKET_URL="ws://localhost:3001";`,
@@ -0,0 +1,23 @@
1
+ // public/server-function-proxy.js
2
+ import { createFromFetch } from "react-server-dom-webpack/client";
3
+
4
+ export function createServerFunctionProxy(id) {
5
+ return new Proxy(() => {}, {
6
+ apply: async (_target, _thisArg, args) => {
7
+ const res = await fetch("/____server_function____", {
8
+ method: "POST",
9
+ headers: { "Content-Type": "application/json" },
10
+ body: JSON.stringify({ id, args }),
11
+ });
12
+ if (!res.ok) throw new Error("Server function failed");
13
+
14
+ const contentType = res.headers.get("content-type") || "";
15
+
16
+ if (contentType.includes("text/x-component")) {
17
+ return createFromFetch(Promise.resolve(res));
18
+ } else {
19
+ return res.json();
20
+ }
21
+ },
22
+ });
23
+ }
@@ -37,13 +37,13 @@ const generateStatic = require("./generate-static.js");
37
37
  const renderAppToHtml = require("./render-app-to-html.js");
38
38
  const revalidating = require("./revalidating.js");
39
39
  const isDevelopment = process.env.NODE_ENV !== "production";
40
- const webpackFolder = isDevelopment ? "public" : "dist3";
40
+ const outputFolder = isDevelopment ? "public" : "dist3";
41
41
  const chokidar = require("chokidar");
42
42
  const { fileURLToPath } = require("url");
43
43
  if (isDevelopment) {
44
44
  const manifestPath = path.resolve(
45
45
  process.cwd(),
46
- `${webpackFolder}/react-client-manifest.json`
46
+ `${outputFolder}/react-client-manifest.json`
47
47
  );
48
48
  let currentManifest = {};
49
49
 
@@ -151,7 +151,7 @@ const app = express();
151
151
  app.use(appUseCookieParser);
152
152
  app.use(express.json());
153
153
 
154
- app.use(express.static(path.resolve(process.cwd(), webpackFolder)));
154
+ app.use(express.static(path.resolve(process.cwd(), outputFolder)));
155
155
 
156
156
  app.get("/.well-known/appspecific/com.chrome.devtools.json", (req, res) => {
157
157
  res.setHeader("Content-Type", "application/json");
@@ -159,7 +159,7 @@ app.get("/.well-known/appspecific/com.chrome.devtools.json", (req, res) => {
159
159
  name: "Dinou DevTools",
160
160
  description: "Dinou DevTools for Chrome",
161
161
  version: "1.0.0",
162
- devtools_page: `/${webpackFolder}/devtools.html`,
162
+ devtools_page: `/${outputFolder}/devtools.html`,
163
163
  });
164
164
  });
165
165
 
@@ -189,7 +189,7 @@ app.get(/^\/____rsc_payload____\/.*\/?$/, async (req, res) => {
189
189
  );
190
190
  const manifest = JSON.parse(
191
191
  readFileSync(
192
- path.resolve(`${webpackFolder}/react-client-manifest.json`),
192
+ path.resolve(`${outputFolder}/react-client-manifest.json`),
193
193
  "utf8"
194
194
  )
195
195
  );
@@ -209,10 +209,7 @@ app.post(/^\/____rsc_payload_error____\/.*\/?$/, async (req, res) => {
209
209
  ).replace("/____rsc_payload_error____", "");
210
210
  const jsx = await getErrorJSX(reqPath, { ...req.query }, req.body.error);
211
211
  const manifest = readFileSync(
212
- path.resolve(
213
- process.cwd(),
214
- `${webpackFolder}/react-client-manifest.json`
215
- ),
212
+ path.resolve(process.cwd(), `${outputFolder}/react-client-manifest.json`),
216
213
  "utf8"
217
214
  );
218
215
  const moduleMap = JSON.parse(manifest);
@@ -285,7 +282,7 @@ app.post("/____server_function____", async (req, res) => {
285
282
  const manifest = readFileSync(
286
283
  path.resolve(
287
284
  process.cwd(),
288
- `${webpackFolder}/react-client-manifest.json`
285
+ `${outputFolder}/react-client-manifest.json`
289
286
  ),
290
287
  "utf8"
291
288
  );
@@ -0,0 +1,65 @@
1
+ import esbuild from "esbuild";
2
+ import fs from "node:fs/promises";
3
+ import getConfigEsbuildProd from "./helpers-esbuild/get-config-esbuild-prod.mjs";
4
+ import getEsbuildEntries from "./helpers-esbuild/get-esbuild-entries.mjs";
5
+ import { fileURLToPath } from "url";
6
+ import path from "node:path";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ const outdir = "dist3";
12
+ await fs.rm(outdir, { recursive: true, force: true });
13
+
14
+ const frameworkEntryPoints = {
15
+ main: path.resolve(__dirname, "../core/client.jsx"),
16
+ error: path.resolve(__dirname, "../core/client-error.jsx"),
17
+ serverFunctionProxy: path.resolve(
18
+ __dirname,
19
+ "../core/server-function-proxy.js"
20
+ ),
21
+ runtime: path.resolve(__dirname, "react-refresh/react-refresh-runtime.mjs"),
22
+ "react-refresh-entry": path.resolve(
23
+ __dirname,
24
+ "react-refresh/react-refresh-entry.js"
25
+ ),
26
+ };
27
+
28
+ try {
29
+ const manifest = {};
30
+
31
+ const [esbuildEntries, detectedCSSEntries, detectedAssetEntries] =
32
+ await getEsbuildEntries({ manifest });
33
+
34
+ const componentEntryPoints = [...esbuildEntries].reduce(
35
+ (acc, dCE) => ({ ...acc, [dCE.outfileName]: dCE.absPath }),
36
+ {}
37
+ );
38
+
39
+ const cssEntryPoints = [...detectedCSSEntries].reduce(
40
+ (acc, dCSSE) => ({ ...acc, [dCSSE.outfileName]: dCSSE.absPath }),
41
+ {}
42
+ );
43
+
44
+ const assetEntryPoints = [...detectedAssetEntries].reduce(
45
+ (acc, dAE) => ({ ...acc, [dAE.outfileName]: dAE.absPath }),
46
+ {}
47
+ );
48
+
49
+ const entryPoints = {
50
+ ...frameworkEntryPoints,
51
+ ...componentEntryPoints,
52
+ ...cssEntryPoints,
53
+ ...assetEntryPoints,
54
+ };
55
+
56
+ await esbuild.build(
57
+ getConfigEsbuildProd({
58
+ entryPoints,
59
+ manifest,
60
+ outdir,
61
+ })
62
+ );
63
+ } catch (err) {
64
+ console.error("Error in build:", err);
65
+ }
@@ -0,0 +1,163 @@
1
+ import esbuild from "esbuild";
2
+ import fs from "node:fs/promises";
3
+ import getConfigEsbuild from "./helpers-esbuild/get-config-esbuild.mjs";
4
+ import getEsbuildEntries from "./helpers-esbuild/get-esbuild-entries.mjs";
5
+ import chokidar from "chokidar";
6
+ import path from "node:path";
7
+ import { regex as assetRegex } from "../core/asset-extensions.js";
8
+ import normalizePath from "./helpers-esbuild/normalize-path.mjs";
9
+ import { fileURLToPath } from "url";
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ const outdir = "public";
15
+ await fs.rm(outdir, { recursive: true, force: true });
16
+
17
+ let currentCtx = null; // Track the active esbuild context
18
+ let debounceTimer = null; // For debouncing recreations
19
+ let clientComponentsPaths = [];
20
+
21
+ const frameworkEntryPoints = {
22
+ main: path.resolve(__dirname, "../core/client.jsx"),
23
+ error: path.resolve(__dirname, "../core/client-error.jsx"),
24
+ serverFunctionProxy: path.resolve(
25
+ __dirname,
26
+ "../core/server-function-proxy.js"
27
+ ),
28
+ runtime: path.resolve(__dirname, "react-refresh/react-refresh-runtime.mjs"),
29
+ "react-refresh-entry": path.resolve(
30
+ __dirname,
31
+ "react-refresh/react-refresh-entry.js"
32
+ ),
33
+ };
34
+
35
+ const changedIds = new Set();
36
+ const hmrEngine = { value: null };
37
+
38
+ const watcher = chokidar.watch("src", {
39
+ ignoreInitial: true,
40
+ ignored: /node_modules|dist/,
41
+ });
42
+
43
+ const codeCssRegex = /.(js|jsx|ts|tsx|css|scss|less)$/i;
44
+
45
+ // Function to (re)create esbuild context with current entries
46
+ async function createEsbuildContext() {
47
+ try {
48
+ if (currentCtx) {
49
+ await currentCtx.dispose(); // Clean up old context
50
+ console.log("Disposed old esbuild context");
51
+ }
52
+
53
+ await fs.rm(outdir, { recursive: true, force: true });
54
+
55
+ const manifest = {};
56
+ const [esbuildEntries, detectedCSSEntries, detectedAssetEntries] =
57
+ await getEsbuildEntries({ manifest });
58
+
59
+ const componentEntryPoints = [...esbuildEntries].reduce(
60
+ (acc, dCE) => ({ ...acc, [dCE.outfileName]: dCE.absPath }),
61
+ {}
62
+ );
63
+
64
+ clientComponentsPaths = Object.values(componentEntryPoints);
65
+
66
+ const cssEntryPoints = [...detectedCSSEntries].reduce(
67
+ (acc, dCSSE) => ({ ...acc, [dCSSE.outfileName]: dCSSE.absPath }),
68
+ {}
69
+ );
70
+
71
+ const assetEntryPoints = [...detectedAssetEntries].reduce(
72
+ (acc, dAE) => ({ ...acc, [dAE.outfileName]: dAE.absPath }),
73
+ {}
74
+ );
75
+
76
+ const entryPoints = {
77
+ ...frameworkEntryPoints,
78
+ ...componentEntryPoints,
79
+ ...cssEntryPoints,
80
+ ...assetEntryPoints,
81
+ };
82
+
83
+ currentCtx = await esbuild.context(
84
+ getConfigEsbuild({
85
+ entryPoints,
86
+ manifest,
87
+ changedIds,
88
+ hmrEngine,
89
+ })
90
+ );
91
+
92
+ await currentCtx.watch();
93
+ // console.log("✓ Watching (changes will trigger rebuild)");
94
+ } catch (err) {
95
+ console.error("Error recreating context:", err);
96
+ }
97
+ }
98
+
99
+ // Initial setup on ready
100
+ watcher.on("ready", async () => {
101
+ await createEsbuildContext();
102
+ });
103
+
104
+ const debounceRecreate = () => {
105
+ if (debounceTimer) clearTimeout(debounceTimer);
106
+ debounceTimer = setTimeout(async () => {
107
+ await createEsbuildContext();
108
+ }, 300); // 300ms debounce — adjust as needed
109
+ };
110
+
111
+ const debounceRecreateAndReload = () => {
112
+ if (debounceTimer) clearTimeout(debounceTimer);
113
+ debounceTimer = setTimeout(async () => {
114
+ await createEsbuildContext();
115
+ hmrEngine.value.broadcastMessage({ type: "reload" });
116
+ }, 300);
117
+ };
118
+
119
+ watcher.on("add", (file) => {
120
+ const ext = path.extname(file);
121
+ if (codeCssRegex.test(ext) || assetRegex.test(ext)) {
122
+ // console.log(`New relevant file detected: ${file}. Recreating context...`);
123
+ debounceRecreateAndReload(file);
124
+ }
125
+ });
126
+
127
+ watcher.on("unlink", async (file) => {
128
+ const ext = path.extname(file);
129
+ if (codeCssRegex.test(ext) || assetRegex.test(ext)) {
130
+ // console.log(`File deleted: ${file}. Recreating context...`);
131
+ if (currentCtx) {
132
+ await currentCtx.dispose();
133
+ currentCtx = null;
134
+ }
135
+ debounceRecreate(file);
136
+ }
137
+ });
138
+
139
+ watcher.on("addDir", (dir) => {
140
+ // console.log(`New directory: ${dir}. Recreating context...`);
141
+ debounceRecreateAndReload(dir);
142
+ });
143
+
144
+ watcher.on("unlinkDir", async (dir) => {
145
+ // console.log(`Directory deleted: ${dir}. Recreating context...`);
146
+ if (currentCtx) {
147
+ await currentCtx.dispose();
148
+ currentCtx = null;
149
+ }
150
+ debounceRecreate(dir);
151
+ });
152
+
153
+ watcher.on("change", (file) => {
154
+ const resolvedFile = normalizePath(path.resolve(file));
155
+ // Check if changed file is a client component
156
+ const isClientModule = clientComponentsPaths.includes(resolvedFile);
157
+ if (isClientModule) {
158
+ changedIds.add(resolvedFile);
159
+ return;
160
+ }
161
+ // Server module, css module, or other file changed
162
+ debounceRecreateAndReload();
163
+ });
@@ -0,0 +1,56 @@
1
+ import { TsconfigPathsPlugin } from "@esbuild-plugins/tsconfig-paths";
2
+ import reactClientManifestPlugin from "../plugins-esbuild/react-client-manifest-plugin.mjs";
3
+ import serverFunctionsPlugin from "../plugins-esbuild/server-functions-plugin.mjs";
4
+ import cssProcessorPlugin from "../plugins-esbuild/css-processor-plugin.mjs";
5
+ import assetsPlugin from "../plugins-esbuild/assets-plugin.mjs";
6
+ import copyStaticFiles from "esbuild-copy-static-files";
7
+ import manifestGeneratorPlugin from "../plugins-esbuild/manifest-generator-plugin.mjs";
8
+ import writePlugin from "../plugins-esbuild/write-plugin.mjs";
9
+
10
+ const manifestData = {};
11
+
12
+ export default function getConfigEsbuildProd({
13
+ entryPoints,
14
+ outdir = "dist3",
15
+ manifest = {},
16
+ }) {
17
+ return {
18
+ entryPoints,
19
+ outdir,
20
+ format: "esm",
21
+ bundle: true,
22
+ splitting: true,
23
+ sourcemap: false,
24
+ chunkNames: "[name]-[hash]",
25
+ entryNames: "[name]-[hash]",
26
+ jsx: "automatic",
27
+ target: "es2022",
28
+ write: false,
29
+ conditions: ["style"],
30
+ metafile: true,
31
+ logLevel: "warning",
32
+ minify: true,
33
+ external: [
34
+ "/__SERVER_FUNCTION_PROXY__",
35
+ "/serverFunctionProxy.js",
36
+ "/__hmr_client__.js",
37
+ "/react-refresh-entry.js",
38
+ ],
39
+ plugins: [
40
+ copyStaticFiles({
41
+ src: "favicons",
42
+ dest: outdir,
43
+ }),
44
+ TsconfigPathsPlugin({}),
45
+ cssProcessorPlugin({ outdir }),
46
+ reactClientManifestPlugin({
47
+ manifest,
48
+ manifestPath: `${outdir}/react-client-manifest.json`,
49
+ }),
50
+ assetsPlugin(),
51
+ manifestGeneratorPlugin(manifestData),
52
+ serverFunctionsPlugin(manifestData),
53
+ writePlugin(),
54
+ ],
55
+ };
56
+ }