@ecopages/react 0.2.0-alpha.52 → 0.2.0-alpha.53

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecopages/react",
3
- "version": "0.2.0-alpha.52",
3
+ "version": "0.2.0-alpha.53",
4
4
  "description": "React integration for Ecopages",
5
5
  "keywords": [
6
6
  "ecopages",
@@ -69,14 +69,14 @@
69
69
  "directory": "packages/integrations/react"
70
70
  },
71
71
  "peerDependencies": {
72
- "@ecopages/core": "0.2.0-alpha.52",
72
+ "@ecopages/core": "0.2.0-alpha.53",
73
73
  "@types/react": "^19",
74
74
  "@types/react-dom": "^19",
75
75
  "react": "^19",
76
76
  "react-dom": "^19"
77
77
  },
78
78
  "dependencies": {
79
- "@ecopages/file-system": "0.2.0-alpha.52",
79
+ "@ecopages/file-system": "0.2.0-alpha.53",
80
80
  "@ecopages/logger": "^0.2.3",
81
81
  "@mdx-js/esbuild": "^3.1.1",
82
82
  "@mdx-js/mdx": "^3.1.1",
@@ -100,6 +100,9 @@ export declare class ReactHmrStrategy extends HmrStrategy {
100
100
  private isRouteTemplate;
101
101
  private resolveTemplateExtension;
102
102
  private ownsWatchedEntrypoint;
103
+ private configContainsFile;
104
+ private pageModuleRequiresLayoutRefresh;
105
+ private hasLayoutOwnedDependencyTarget;
103
106
  /**
104
107
  * Determines if the file is a React/MDX entrypoint that's registered for HMR.
105
108
  *
@@ -7,6 +7,7 @@ import { Logger } from "@ecopages/logger";
7
7
  import { injectHmrHandler } from "./utils/hmr-scripts.js";
8
8
  import { createClientGraphBoundaryPlugin } from "./utils/client-graph-boundary-plugin.js";
9
9
  import { collectPageDeclaredModules, collectPageDeclaredModulesFromModule } from "./utils/declared-modules.js";
10
+ import { someInConfigTree } from "./utils/component-config-traversal.js";
10
11
  import { createReactMdxLoaderPlugin } from "./utils/react-mdx-loader-plugin.js";
11
12
  import { getReactClientGraphAllowSpecifiers } from "./utils/react-runtime-alias-map.js";
12
13
  const appLogger = new Logger("[ReactHmrStrategy]");
@@ -103,6 +104,32 @@ class ReactHmrStrategy extends HmrStrategy {
103
104
  ownsWatchedEntrypoint(filePath) {
104
105
  return this.pageMetadataCache.ownsEntrypoint(filePath);
105
106
  }
107
+ configContainsFile(config, filePath) {
108
+ const resolvedFilePath = path.resolve(filePath);
109
+ return someInConfigTree(config, (node) => {
110
+ if (!node.__eco?.file) {
111
+ return false;
112
+ }
113
+ return path.resolve(node.__eco.file) === resolvedFilePath;
114
+ });
115
+ }
116
+ pageModuleRequiresLayoutRefresh(pageModule, filePath) {
117
+ return [pageModule.default?.config, pageModule.config].some((config) => {
118
+ return this.configContainsFile(config?.layout?.config, filePath);
119
+ });
120
+ }
121
+ async hasLayoutOwnedDependencyTarget(changedFilePath, requestedTargets) {
122
+ for (const target of requestedTargets) {
123
+ if (!this.isPageEntrypoint(target.entrypointPath)) {
124
+ continue;
125
+ }
126
+ const pageModule = await this.importNodePageModule(target.entrypointPath);
127
+ if (this.pageModuleRequiresLayoutRefresh(pageModule, changedFilePath)) {
128
+ return true;
129
+ }
130
+ }
131
+ return false;
132
+ }
106
133
  /**
107
134
  * Determines if the file is a React/MDX entrypoint that's registered for HMR.
108
135
  *
@@ -257,6 +284,7 @@ class ReactHmrStrategy extends HmrStrategy {
257
284
  return { type: "none" };
258
285
  }
259
286
  const isLayout = this.isLayoutFile(_filePath);
287
+ const isChangedPageEntrypoint = this.isPageEntrypoint(_filePath);
260
288
  if (isLayout) {
261
289
  appLogger.debug(`Detected layout file change: ${_filePath}`);
262
290
  }
@@ -268,21 +296,39 @@ class ReactHmrStrategy extends HmrStrategy {
268
296
  const dependencyHits = this.context.getEntrypointDependencyGraph().getDependencyEntrypoints(_filePath);
269
297
  const hasDependencyHits = dependencyHits.size > 0;
270
298
  const affectedEntrypoints = /* @__PURE__ */ new Map();
299
+ let hasOwnedLayoutDependencyHit = false;
300
+ let layoutOwnedPageTargets = [];
301
+ let hasLayoutOwnedRequestedTarget = false;
271
302
  if (hasDependencyHits && !changedEntrypointOutput) {
272
303
  for (const entrypoint of dependencyHits) {
273
304
  const outputUrl = watchedFiles.get(entrypoint);
274
305
  if (outputUrl && this.ownsWatchedEntrypoint(entrypoint)) {
275
306
  affectedEntrypoints.set(entrypoint, outputUrl);
307
+ continue;
308
+ }
309
+ if (this.isLayoutFile(entrypoint) && this.ownsWatchedEntrypoint(entrypoint)) {
310
+ hasOwnedLayoutDependencyHit = true;
276
311
  }
277
312
  }
278
- if (affectedEntrypoints.size === 0) {
313
+ if (affectedEntrypoints.size === 0 && !hasOwnedLayoutDependencyHit) {
279
314
  appLogger.debug(`Dependency hits found but none map to React-owned watched entrypoints`);
280
315
  return { type: "none" };
281
316
  }
282
317
  }
283
- const requestedTargets = changedEntrypointOutput ? [{ entrypointPath: _filePath, outputUrl: changedEntrypointOutput }] : hasDependencyHits ? Array.from(affectedEntrypoints, ([entrypointPath, outputUrl]) => ({ entrypointPath, outputUrl })) : Array.from(watchedFiles, ([entrypointPath, outputUrl]) => ({ entrypointPath, outputUrl }));
318
+ if (changedEntrypointOutput && !isLayout && !isChangedPageEntrypoint) {
319
+ layoutOwnedPageTargets = await this.collectReactPageBuildTargets();
320
+ hasLayoutOwnedRequestedTarget = await this.hasLayoutOwnedDependencyTarget(
321
+ _filePath,
322
+ layoutOwnedPageTargets
323
+ );
324
+ }
325
+ const requestedTargets = changedEntrypointOutput ? hasLayoutOwnedRequestedTarget ? [{ entrypointPath: _filePath, outputUrl: changedEntrypointOutput }, ...layoutOwnedPageTargets] : [{ entrypointPath: _filePath, outputUrl: changedEntrypointOutput }] : hasOwnedLayoutDependencyHit ? await this.collectReactPageBuildTargets() : hasDependencyHits ? Array.from(affectedEntrypoints, ([entrypointPath, outputUrl]) => ({ entrypointPath, outputUrl })) : Array.from(watchedFiles, ([entrypointPath, outputUrl]) => ({ entrypointPath, outputUrl }));
284
326
  const groupedPageTargets = await this.resolveBuildTargets(requestedTargets, _filePath);
285
327
  const { pageTargets, nonPageTargets } = this.partitionBuildTargets(requestedTargets, groupedPageTargets);
328
+ if (!changedEntrypointOutput) {
329
+ hasLayoutOwnedRequestedTarget = await this.hasLayoutOwnedDependencyTarget(_filePath, requestedTargets);
330
+ }
331
+ const requiresLayoutRefresh = isLayout || hasOwnedLayoutDependencyHit || hasLayoutOwnedRequestedTarget;
286
332
  const updates = [];
287
333
  const requestedOutputUrls = new Set(requestedTargets.map((target) => target.outputUrl));
288
334
  if (pageTargets.length > 1) {
@@ -313,7 +359,7 @@ class ReactHmrStrategy extends HmrStrategy {
313
359
  }
314
360
  }
315
361
  if (updates.length > 0) {
316
- if (isLayout) {
362
+ if (requiresLayoutRefresh) {
317
363
  appLogger.debug(`Layout update detected, sending layout-update event`);
318
364
  return {
319
365
  type: "broadcast",