dinou 3.0.0 → 3.0.2

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,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.2]
9
+
10
+ ### Fixed
11
+
12
+ - Fix esbuild development change between server and client components.
13
+ - Fix rollup development change between server and client components.
14
+ - Fix CSS entries for webpack.
15
+
16
+ ## [3.0.1]
17
+
18
+ ### Fixed
19
+
20
+ - esbuild doesn't give error when 'favicons' folder doesn't exist in root directory.
21
+
8
22
  ## [3.0.0]
9
23
 
10
24
  ### Added
@@ -5,6 +5,7 @@ const getSSGJSXOrJSX = require("./get-ssg-jsx-or-jsx.js");
5
5
  const { renderToPipeableStream } = require("react-server-dom-webpack/server");
6
6
 
7
7
  const OUT_DIR = path.resolve("dist2");
8
+ const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
8
9
 
9
10
  async function generateStaticRSC(reqPath) {
10
11
  const finalReqPath = reqPath.endsWith("/") ? reqPath : reqPath + "/";
@@ -16,7 +17,14 @@ async function generateStaticRSC(reqPath) {
16
17
  // console.log("✅ JSX retrieved for:", finalReqPath);
17
18
 
18
19
  const manifest = JSON.parse(
19
- fs.readFileSync(path.resolve("dist3/react-client-manifest.json"), "utf8")
20
+ fs.readFileSync(
21
+ path.resolve(
22
+ isWebpack
23
+ ? "dist3/react-client-manifest.json"
24
+ : "react_client_manifest/react-client-manifest.json"
25
+ ),
26
+ "utf8"
27
+ )
20
28
  );
21
29
 
22
30
  fs.mkdirSync(path.dirname(payloadPath), { recursive: true });
@@ -5,10 +5,18 @@ const getSSGJSXOrJSX = require("./get-ssg-jsx-or-jsx.js");
5
5
  const { renderToPipeableStream } = require("react-server-dom-webpack/server");
6
6
 
7
7
  const OUT_DIR = path.resolve("dist2");
8
+ const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
8
9
 
9
10
  async function generateStaticRSCs(routes) {
10
11
  const manifest = JSON.parse(
11
- fs.readFileSync(path.resolve("dist3/react-client-manifest.json"), "utf8")
12
+ fs.readFileSync(
13
+ path.resolve(
14
+ isWebpack
15
+ ? "dist3/react-client-manifest.json"
16
+ : "react_client_manifest/react-client-manifest.json"
17
+ ),
18
+ "utf8"
19
+ )
12
20
  );
13
21
 
14
22
  for (const route of routes) {
@@ -40,68 +40,120 @@ const isDevelopment = process.env.NODE_ENV !== "production";
40
40
  const outputFolder = isDevelopment ? "public" : "dist3";
41
41
  const chokidar = require("chokidar");
42
42
  const { fileURLToPath } = require("url");
43
+ const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
43
44
  if (isDevelopment) {
44
45
  const manifestPath = path.resolve(
45
46
  process.cwd(),
46
- `${outputFolder}/react-client-manifest.json`
47
+ isWebpack
48
+ ? `${outputFolder}/react-client-manifest.json`
49
+ : `react_client_manifest/react-client-manifest.json`
50
+ );
51
+ const manifestFolderPath = path.resolve(
52
+ process.cwd(),
53
+ isWebpack ? outputFolder : "react_client_manifest"
47
54
  );
48
- let currentManifest = {};
49
-
50
- const watcher = chokidar.watch(manifestPath, { persistent: true });
51
- let isInitial = true;
52
55
 
53
- watcher.on("add", () => {
54
- if (Object.keys(currentManifest).length === 0 && isInitial) {
55
- // console.log("Initial manifest loaded.");
56
- currentManifest = JSON.parse(readFileSync(manifestPath, "utf8"));
57
- isInitial = false;
58
- return;
56
+ let manifestWatcher = null;
57
+
58
+ function startManifestWatcher() {
59
+ let currentManifest = {};
60
+ let isInitial = true;
61
+ // Si ya existe un watcher viejo, ciérralo primero
62
+ if (manifestWatcher) {
63
+ try {
64
+ manifestWatcher.close();
65
+ } catch (e) {
66
+ console.warn("Failed closing old watcher:", e);
67
+ }
59
68
  }
60
- });
61
69
 
62
- function getParents(resolvedPath) {
63
- const parents = [];
64
- Object.values(require.cache).forEach((mod) => {
65
- if (
66
- mod.children &&
67
- mod.children.some((child) => child.id === resolvedPath)
68
- ) {
69
- parents.push(mod.id);
70
- }
70
+ // console.log("[Watcher] Starting watcher");
71
+
72
+ manifestWatcher = chokidar.watch(manifestFolderPath, {
73
+ persistent: true,
74
+ ignored: /node_modules/,
71
75
  });
72
- return parents;
73
- }
74
76
 
75
- function clearRequireCache(modulePath, visited = new Set()) {
76
- try {
77
- const resolved = require.resolve(modulePath);
78
- if (visited.has(resolved)) return;
79
- visited.add(resolved);
80
-
81
- if (require.cache[resolved]) {
82
- delete require.cache[resolved];
83
- // console.log(`[Server HMR] Cleared cache for ${resolved}`);
84
-
85
- const parents = getParents(resolved);
86
- for (const parent of parents) {
87
- // Optional: Skip if parent not in src/ (safety)
88
- if (parent.startsWith(path.resolve(process.cwd(), "src"))) {
89
- clearRequireCache(parent, visited);
77
+ async function loadManifestWithRetry({
78
+ manifestPath,
79
+ maxRetries = 10,
80
+ delayMs = 100,
81
+ } = {}) {
82
+ let attempts = 0;
83
+ while (attempts < maxRetries) {
84
+ try {
85
+ // console.log(`Attempting to load manifest (try ${attempts + 1})...`);
86
+ return JSON.parse(readFileSync(manifestPath, "utf8"));
87
+ } catch (err) {
88
+ if (err.code !== "ENOENT") {
89
+ throw err; // Rethrow if it's not a file not found error
90
90
  }
91
+ attempts++;
92
+ if (attempts >= maxRetries) {
93
+ throw err; // Rethrow after max retries
94
+ }
95
+ // Wait for the specified delay before retrying
96
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
91
97
  }
92
98
  }
93
- } catch (err) {
94
- console.warn(
95
- `[Server HMR] Could not resolve or clear ${modulePath}: ${err.message}`
96
- );
97
99
  }
98
- }
99
100
 
100
- watcher.on("change", () => {
101
- try {
102
- const newManifest = JSON.parse(readFileSync(manifestPath, "utf8"));
101
+ function getParents(resolvedPath) {
102
+ const parents = [];
103
+ Object.values(require.cache).forEach((mod) => {
104
+ if (
105
+ mod.children &&
106
+ mod.children.some((child) => child.id === resolvedPath)
107
+ ) {
108
+ parents.push(mod.id);
109
+ }
110
+ });
111
+ return parents;
112
+ }
113
+
114
+ function clearRequireCache(modulePath, visited = new Set()) {
115
+ try {
116
+ const resolved = require.resolve(modulePath);
117
+ if (visited.has(resolved)) return;
118
+ visited.add(resolved);
119
+
120
+ if (require.cache[resolved]) {
121
+ delete require.cache[resolved];
122
+ // console.log(`[Server HMR] Cleared cache for ${resolved}`);
123
+
124
+ const parents = getParents(resolved);
125
+ for (const parent of parents) {
126
+ // Optional: Skip if parent not in src/ (safety)
127
+ if (parent.startsWith(path.resolve(process.cwd(), "src"))) {
128
+ clearRequireCache(parent, visited);
129
+ }
130
+ }
131
+ }
132
+ } catch (err) {
133
+ console.warn(
134
+ `[Server HMR] Could not resolve or clear ${modulePath}: ${err.message}`
135
+ );
136
+ }
137
+ }
138
+
139
+ let manifestTimeout;
140
+
141
+ function readJSONWithRetry(pathToRead, retries = 4, delay = 10) {
142
+ for (let i = 0; i < retries; i++) {
143
+ try {
144
+ const text = readFileSync(pathToRead, "utf8");
145
+ if (!text.trim()) throw new Error("Empty JSON");
146
+ return JSON.parse(text);
147
+ } catch (err) {
148
+ if (i === retries - 1) throw err;
149
+ // tiny sleep
150
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delay);
151
+ }
152
+ }
153
+ }
103
154
 
104
- // Handle removed entries: client -> server switch
155
+ function handleManifestUpdate(newManifest) {
156
+ // console.log("handle manifest update", newManifest, currentManifest);
105
157
  for (const key in currentManifest) {
106
158
  if (!(key in newManifest)) {
107
159
  const absPath = fileURLToPath(key);
@@ -120,30 +172,50 @@ if (isDevelopment) {
120
172
  }
121
173
 
122
174
  currentManifest = newManifest;
123
- } catch (err) {
124
- console.error("Error handling manifest change:", err);
125
175
  }
126
- });
127
-
128
- const srcWatcher = chokidar.watch(path.resolve(process.cwd(), "src"), {
129
- persistent: true,
130
- ignored: /node_modules/,
131
- });
132
-
133
- srcWatcher.on("change", (changedPath) => {
134
- const posixPath = changedPath.split(path.sep).join(path.posix.sep);
135
176
 
136
- const isClientComponent = Object.keys(currentManifest).some((key) =>
137
- key.includes(posixPath)
138
- );
139
-
140
- if (!isClientComponent) {
141
- clearRequireCache(changedPath);
142
- // console.log(
143
- // `[Server HMR] Cleared cache for ${changedPath} in srcWatcher`
144
- // );
177
+ async function onManifestChange(chokidarPath, stats, delayMs = 100) {
178
+ if (isWebpack && chokidarPath !== manifestPath) return;
179
+ if (Object.keys(currentManifest).length === 0 && isInitial) {
180
+ try {
181
+ currentManifest = await loadManifestWithRetry({
182
+ manifestPath,
183
+ delayMs,
184
+ });
185
+ // console.log("Loaded initial manifest for HMR.", currentManifest);
186
+ isInitial = false;
187
+ } catch (err) {
188
+ console.error("Failed to load initial manifest after retries:", err);
189
+ }
190
+ return;
191
+ }
192
+ try {
193
+ // console.log("change event");
194
+ clearTimeout(manifestTimeout);
195
+
196
+ manifestTimeout = setTimeout(() => {
197
+ try {
198
+ const newManifest = readJSONWithRetry(manifestPath);
199
+ handleManifestUpdate(newManifest);
200
+ } catch (err) {
201
+ console.error("Manifest not ready:", err.message);
202
+ }
203
+ }, 50);
204
+ } catch (err) {
205
+ console.error("Error handling manifest change:", err);
206
+ }
145
207
  }
146
- });
208
+
209
+ manifestWatcher.on("add", onManifestChange);
210
+ manifestWatcher.on("unlinkDir", startManifestWatcher);
211
+ manifestWatcher.on("unlink", () => {
212
+ isInitial = true;
213
+ currentManifest = {};
214
+ });
215
+ // manifestWatcher.on("all", (event) => console.log("event", event));
216
+ manifestWatcher.on("change", onManifestChange);
217
+ }
218
+ startManifestWatcher();
147
219
  }
148
220
  const cookieParser = require("cookie-parser");
149
221
  const appUseCookieParser = cookieParser();
@@ -189,7 +261,11 @@ app.get(/^\/____rsc_payload____\/.*\/?$/, async (req, res) => {
189
261
  );
190
262
  const manifest = JSON.parse(
191
263
  readFileSync(
192
- path.resolve(`${outputFolder}/react-client-manifest.json`),
264
+ path.resolve(
265
+ isWebpack
266
+ ? `${outputFolder}/react-client-manifest.json`
267
+ : `react_client_manifest/react-client-manifest.json`
268
+ ),
193
269
  "utf8"
194
270
  )
195
271
  );
@@ -209,7 +285,12 @@ app.post(/^\/____rsc_payload_error____\/.*\/?$/, async (req, res) => {
209
285
  ).replace("/____rsc_payload_error____", "");
210
286
  const jsx = await getErrorJSX(reqPath, { ...req.query }, req.body.error);
211
287
  const manifest = readFileSync(
212
- path.resolve(process.cwd(), `${outputFolder}/react-client-manifest.json`),
288
+ path.resolve(
289
+ process.cwd(),
290
+ isWebpack
291
+ ? `${outputFolder}/react-client-manifest.json`
292
+ : `react_client_manifest/react-client-manifest.json`
293
+ ),
213
294
  "utf8"
214
295
  );
215
296
  const moduleMap = JSON.parse(manifest);
@@ -282,7 +363,9 @@ app.post("/____server_function____", async (req, res) => {
282
363
  const manifest = readFileSync(
283
364
  path.resolve(
284
365
  process.cwd(),
285
- `${outputFolder}/react-client-manifest.json`
366
+ isWebpack
367
+ ? `${outputFolder}/react-client-manifest.json`
368
+ : `react_client_manifest/react-client-manifest.json`
286
369
  ),
287
370
  "utf8"
288
371
  );
@@ -10,6 +10,7 @@ const __dirname = path.dirname(__filename);
10
10
 
11
11
  const outdir = "dist3";
12
12
  await fs.rm(outdir, { recursive: true, force: true });
13
+ await fs.rm("react_client_manifest", { recursive: true, force: true });
13
14
 
14
15
  const frameworkEntryPoints = {
15
16
  main: path.resolve(__dirname, "../core/client.jsx"),
@@ -6,17 +6,19 @@ import chokidar from "chokidar";
6
6
  import path from "node:path";
7
7
  import { regex as assetRegex } from "../core/asset-extensions.js";
8
8
  import normalizePath from "./helpers-esbuild/normalize-path.mjs";
9
- import { fileURLToPath } from "url";
9
+ import { fileURLToPath, pathToFileURL } from "url";
10
10
 
11
11
  const __filename = fileURLToPath(import.meta.url);
12
12
  const __dirname = path.dirname(__filename);
13
13
 
14
14
  const outdir = "public";
15
15
  await fs.rm(outdir, { recursive: true, force: true });
16
+ await fs.rm("react_client_manifest", { recursive: true, force: true });
16
17
 
17
18
  let currentCtx = null; // Track the active esbuild context
18
19
  let debounceTimer = null; // For debouncing recreations
19
20
  let clientComponentsPaths = [];
21
+ let currentServerFiles = new Set();
20
22
 
21
23
  const frameworkEntryPoints = {
22
24
  main: path.resolve(__dirname, "../core/client.jsx"),
@@ -42,44 +44,57 @@ const watcher = chokidar.watch("src", {
42
44
 
43
45
  const codeCssRegex = /.(js|jsx|ts|tsx|css|scss|less)$/i;
44
46
 
47
+ let manifest = {};
48
+ let entryPoints = {};
49
+
50
+ async function updateEntriesAndComponents() {
51
+ manifest = {};
52
+ const [
53
+ esbuildEntries,
54
+ detectedCSSEntries,
55
+ detectedAssetEntries,
56
+ serverFiles,
57
+ ] = await getEsbuildEntries({ manifest });
58
+
59
+ currentServerFiles = new Set(
60
+ serverFiles.map((f) => normalizePath(path.resolve(f)))
61
+ );
62
+
63
+ const componentEntryPoints = [...esbuildEntries].reduce(
64
+ (acc, dCE) => ({ ...acc, [dCE.outfileName]: dCE.absPath }),
65
+ {}
66
+ );
67
+
68
+ clientComponentsPaths = Object.values(componentEntryPoints);
69
+
70
+ const cssEntryPoints = [...detectedCSSEntries].reduce(
71
+ (acc, dCSSE) => ({ ...acc, [dCSSE.outfileName]: dCSSE.absPath }),
72
+ {}
73
+ );
74
+
75
+ const assetEntryPoints = [...detectedAssetEntries].reduce(
76
+ (acc, dAE) => ({ ...acc, [dAE.outfileName]: dAE.absPath }),
77
+ {}
78
+ );
79
+
80
+ entryPoints = {
81
+ ...frameworkEntryPoints,
82
+ ...componentEntryPoints,
83
+ ...cssEntryPoints,
84
+ ...assetEntryPoints,
85
+ };
86
+ }
87
+
45
88
  // Function to (re)create esbuild context with current entries
46
89
  async function createEsbuildContext() {
47
90
  try {
48
91
  if (currentCtx) {
49
92
  await currentCtx.dispose(); // Clean up old context
50
- console.log("Disposed old esbuild context");
93
+ // console.log("Disposed old esbuild context");
51
94
  }
52
95
 
53
96
  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
-
97
+ // console.log("manifest before creating context:", manifest);
83
98
  currentCtx = await esbuild.context(
84
99
  getConfigEsbuild({
85
100
  entryPoints,
@@ -98,6 +113,7 @@ async function createEsbuildContext() {
98
113
 
99
114
  // Initial setup on ready
100
115
  watcher.on("ready", async () => {
116
+ await updateEntriesAndComponents();
101
117
  await createEsbuildContext();
102
118
  });
103
119
 
@@ -116,11 +132,12 @@ const debounceRecreateAndReload = () => {
116
132
  }, 300);
117
133
  };
118
134
 
119
- watcher.on("add", (file) => {
135
+ watcher.on("add", async (file) => {
120
136
  const ext = path.extname(file);
121
137
  if (codeCssRegex.test(ext) || assetRegex.test(ext)) {
122
138
  // console.log(`New relevant file detected: ${file}. Recreating context...`);
123
- debounceRecreateAndReload(file);
139
+ await updateEntriesAndComponents();
140
+ debounceRecreateAndReload();
124
141
  }
125
142
  });
126
143
 
@@ -128,33 +145,59 @@ watcher.on("unlink", async (file) => {
128
145
  const ext = path.extname(file);
129
146
  if (codeCssRegex.test(ext) || assetRegex.test(ext)) {
130
147
  // console.log(`File deleted: ${file}. Recreating context...`);
148
+ await updateEntriesAndComponents();
131
149
  if (currentCtx) {
132
150
  await currentCtx.dispose();
133
151
  currentCtx = null;
134
152
  }
135
- debounceRecreate(file);
153
+ debounceRecreate();
136
154
  }
137
155
  });
138
156
 
139
- watcher.on("addDir", (dir) => {
157
+ watcher.on("addDir", async () => {
140
158
  // console.log(`New directory: ${dir}. Recreating context...`);
141
- debounceRecreateAndReload(dir);
159
+ await updateEntriesAndComponents();
160
+ debounceRecreateAndReload();
142
161
  });
143
162
 
144
- watcher.on("unlinkDir", async (dir) => {
163
+ watcher.on("unlinkDir", async () => {
145
164
  // console.log(`Directory deleted: ${dir}. Recreating context...`);
165
+ await updateEntriesAndComponents();
146
166
  if (currentCtx) {
147
167
  await currentCtx.dispose();
148
168
  currentCtx = null;
149
169
  }
150
- debounceRecreate(dir);
170
+ debounceRecreate();
151
171
  });
152
172
 
153
- watcher.on("change", (file) => {
173
+ function existsInManifest(resolvedFile, manifest) {
174
+ const manifestKey = pathToFileURL(resolvedFile).href;
175
+ // console.log("Checking manifest for key:", manifestKey);
176
+ for (const key of Object.keys(manifest)) {
177
+ if (key === manifestKey) {
178
+ return true;
179
+ }
180
+ }
181
+ return false;
182
+ }
183
+
184
+ watcher.on("change", async (file) => {
154
185
  const resolvedFile = normalizePath(path.resolve(file));
186
+ const oldManifest = { ...manifest };
187
+ await updateEntriesAndComponents();
155
188
  // Check if changed file is a client component
156
189
  const isClientModule = clientComponentsPaths.includes(resolvedFile);
157
- if (isClientModule) {
190
+ const isServerModule = currentServerFiles.has(resolvedFile);
191
+ // console.log(
192
+ // `File changed: ${resolvedFile} | Client Module: ${isClientModule} | Server Module: ${isServerModule}`
193
+ // );
194
+ // console.log("currentServerFiles:", currentServerFiles);
195
+
196
+ if (
197
+ isClientModule &&
198
+ !isServerModule &&
199
+ existsInManifest(resolvedFile, oldManifest)
200
+ ) {
158
201
  changedIds.add(resolvedFile);
159
202
  return;
160
203
  }
@@ -6,6 +6,7 @@ import assetsPlugin from "../plugins-esbuild/assets-plugin.mjs";
6
6
  import copyStaticFiles from "esbuild-copy-static-files";
7
7
  import manifestGeneratorPlugin from "../plugins-esbuild/manifest-generator-plugin.mjs";
8
8
  import writePlugin from "../plugins-esbuild/write-plugin.mjs";
9
+ import { existsSync } from "node:fs";
9
10
 
10
11
  const manifestData = {};
11
12
 
@@ -14,6 +15,29 @@ export default function getConfigEsbuildProd({
14
15
  outdir = "dist3",
15
16
  manifest = {},
16
17
  }) {
18
+ let plugins = [
19
+ TsconfigPathsPlugin({}),
20
+ cssProcessorPlugin({ outdir }),
21
+ reactClientManifestPlugin({
22
+ manifest,
23
+ manifestPath: `react_client_manifest/react-client-manifest.json`,
24
+ }),
25
+ assetsPlugin(),
26
+ manifestGeneratorPlugin(manifestData),
27
+ serverFunctionsPlugin(manifestData),
28
+ writePlugin(),
29
+ ];
30
+
31
+ if (existsSync("favicons")) {
32
+ plugins = [
33
+ copyStaticFiles({
34
+ src: "favicons",
35
+ dest: outdir,
36
+ }),
37
+ ...plugins,
38
+ ];
39
+ }
40
+
17
41
  return {
18
42
  entryPoints,
19
43
  outdir,
@@ -36,21 +60,6 @@ export default function getConfigEsbuildProd({
36
60
  "/__hmr_client__.js",
37
61
  "/react-refresh-entry.js",
38
62
  ],
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
- ],
63
+ plugins,
55
64
  };
56
65
  }
@@ -7,6 +7,7 @@ import stableChunkNamesAndMapsPlugin from "../plugins-esbuild/stable-chunk-names
7
7
  import assetsPlugin from "../plugins-esbuild/assets-plugin.mjs";
8
8
  import skipMissingEntryPointsPlugin from "../plugins-esbuild/skip-missing-entry-points-plugin.mjs";
9
9
  import copyStaticFiles from "esbuild-copy-static-files";
10
+ import { existsSync } from "node:fs";
10
11
 
11
12
  export default function getConfigEsbuild({
12
13
  entryPoints,
@@ -15,6 +16,27 @@ export default function getConfigEsbuild({
15
16
  changedIds,
16
17
  hmrEngine,
17
18
  }) {
19
+ let plugins = [
20
+ skipMissingEntryPointsPlugin(),
21
+ TsconfigPathsPlugin({}),
22
+ cssProcessorPlugin(),
23
+ reactClientManifestPlugin({ manifest }),
24
+ assetsPlugin(),
25
+ stableChunkNamesAndMapsPlugin(),
26
+ serverFunctionsPlugin(),
27
+ esmHmrPlugin({ entryName: "main", changedIds, hmrEngine }),
28
+ ];
29
+
30
+ if (existsSync("favicons")) {
31
+ plugins = [
32
+ copyStaticFiles({
33
+ src: "favicons",
34
+ dest: outdir,
35
+ }),
36
+ ...plugins,
37
+ ];
38
+ }
39
+
18
40
  return {
19
41
  entryPoints,
20
42
  outdir,
@@ -37,19 +59,6 @@ export default function getConfigEsbuild({
37
59
  "/__hmr_client__.js",
38
60
  "/react-refresh-entry.js",
39
61
  ],
40
- plugins: [
41
- copyStaticFiles({
42
- src: "favicons",
43
- dest: outdir,
44
- }),
45
- skipMissingEntryPointsPlugin(),
46
- TsconfigPathsPlugin({}),
47
- cssProcessorPlugin(),
48
- reactClientManifestPlugin({ manifest }),
49
- assetsPlugin(),
50
- stableChunkNamesAndMapsPlugin(),
51
- serverFunctionsPlugin(),
52
- esmHmrPlugin({ entryName: "main", changedIds, hmrEngine }),
53
- ],
62
+ plugins,
54
63
  };
55
64
  }