@ecopages/core 0.2.0-alpha.19 → 0.2.0-alpha.20

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
@@ -24,6 +24,8 @@ All notable changes to `@ecopages/core` are documented here.
24
24
 
25
25
  - Fixed mixed-integration page, layout, document, and component rendering to resolve foreign boundaries inside their owning renderer across the built-in integrations.
26
26
  - Fixed host/runtime module loading, published build-helper exports, asset output normalization, explicit render flows, and static or preview build stability across Bun, Node, Vite, and Nitro.
27
+ - Fixed development project watcher setup to register chokidar paths and handlers only once per app runtime.
28
+ - Fixed development script-entry registration to build only the requested HMR entrypoint instead of fanning out across all watched script entrypoints during startup.
27
29
  - Fixed Node bootstrap runtime package linking to refresh dangling `.eco/node_modules` symlinks instead of failing with `EEXIST` during page transpilation.
28
30
  - Fixed request-time and static-generation page inspection to preserve integration-specific page loading without reusing the normal render module identity.
29
31
  - Fixed Node preview and static-generation React runtime resolution so app-owned page modules and server rendering share one React module identity.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecopages/core",
3
- "version": "0.2.0-alpha.19",
3
+ "version": "0.2.0-alpha.20",
4
4
  "description": "Core package for Ecopages",
5
5
  "keywords": [
6
6
  "ecopages",
@@ -17,7 +17,7 @@
17
17
  "directory": "packages/core"
18
18
  },
19
19
  "dependencies": {
20
- "@ecopages/file-system": "0.2.0-alpha.19",
20
+ "@ecopages/file-system": "0.2.0-alpha.20",
21
21
  "@ecopages/logger": "^0.2.3",
22
22
  "@ecopages/scripts-injector": "^0.1.3",
23
23
  "@worker-tools/html-rewriter": "0.1.0-pre.19",
@@ -125,9 +125,9 @@ export declare class HmrManager implements IHmrManager {
125
125
  * Performs registration for a generic script asset.
126
126
  *
127
127
  * @remarks
128
- * Strategies get the first chance to emit output. If no output exists after
129
- * that pass, Bun falls back to the generic browser build for this explicit
130
- * script-only path.
128
+ * This path performs a targeted browser bundle for the requested script
129
+ * entrypoint only. The resulting dependency graph is retained so later file
130
+ * changes can invalidate just the affected script entrypoints.
131
131
  */
132
132
  private emitScriptEntrypoint;
133
133
  /**
@@ -285,28 +285,27 @@ class HmrManager {
285
285
  * Performs registration for a generic script asset.
286
286
  *
287
287
  * @remarks
288
- * Strategies get the first chance to emit output. If no output exists after
289
- * that pass, Bun falls back to the generic browser build for this explicit
290
- * script-only path.
288
+ * This path performs a targeted browser bundle for the requested script
289
+ * entrypoint only. The resulting dependency graph is retained so later file
290
+ * changes can invalidate just the affected script entrypoints.
291
291
  */
292
292
  async emitScriptEntrypoint(entrypointPath, outputPath) {
293
293
  const naming = path.relative(this.distDir, outputPath).split(path.sep).join("/");
294
- await this.handleFileChange(entrypointPath, { broadcast: false });
295
- if (!fileSystem.exists(outputPath)) {
296
- const buildResult = await this.browserBundleService.bundle({
297
- profile: "hmr-entrypoint",
298
- entrypoints: [entrypointPath],
299
- outdir: this.distDir,
300
- naming,
301
- minify: false,
302
- plugins: this.plugins
303
- });
304
- if (!buildResult.success) {
305
- appLogger.error(
306
- `[HMR] Generic script entrypoint build failed for ${entrypointPath}:`,
307
- buildResult.logs
308
- );
309
- }
294
+ const buildResult = await this.browserBundleService.bundle({
295
+ profile: "hmr-entrypoint",
296
+ entrypoints: [entrypointPath],
297
+ outdir: this.distDir,
298
+ naming,
299
+ minify: false,
300
+ plugins: this.plugins
301
+ });
302
+ if (!buildResult.success) {
303
+ appLogger.error(`[HMR] Generic script entrypoint build failed for ${entrypointPath}:`, buildResult.logs);
304
+ return;
305
+ }
306
+ const entrypointDependencies = buildResult.dependencyGraph?.entrypoints?.[entrypointPath];
307
+ if (entrypointDependencies) {
308
+ this.entrypointDependencyGraph.setEntrypointDependencies(entrypointPath, entrypointDependencies);
310
309
  }
311
310
  }
312
311
  /**
@@ -114,9 +114,9 @@ export declare class NodeHmrManager implements IHmrManager {
114
114
  * Performs registration for a generic script asset.
115
115
  *
116
116
  * @remarks
117
- * The manager first gives registered strategies a chance to emit the file so
118
- * processor-owned or integration-owned behavior can still participate. Only
119
- * when no output exists does it issue the generic build.
117
+ * This path performs a targeted browser bundle for the requested script
118
+ * entrypoint only. The resulting dependency graph is retained so later file
119
+ * changes can invalidate just the affected script entrypoints.
120
120
  */
121
121
  private emitScriptEntrypoint;
122
122
  /**
@@ -262,28 +262,27 @@ class NodeHmrManager {
262
262
  * Performs registration for a generic script asset.
263
263
  *
264
264
  * @remarks
265
- * The manager first gives registered strategies a chance to emit the file so
266
- * processor-owned or integration-owned behavior can still participate. Only
267
- * when no output exists does it issue the generic build.
265
+ * This path performs a targeted browser bundle for the requested script
266
+ * entrypoint only. The resulting dependency graph is retained so later file
267
+ * changes can invalidate just the affected script entrypoints.
268
268
  */
269
269
  async emitScriptEntrypoint(entrypointPath, outputPath) {
270
270
  const naming = path.relative(this.distDir, outputPath).split(path.sep).join("/");
271
- await this.handleFileChange(entrypointPath, { broadcast: false });
272
- if (!fileSystem.exists(outputPath)) {
273
- const buildResult = await this.browserBundleService.bundle({
274
- profile: "hmr-entrypoint",
275
- entrypoints: [entrypointPath],
276
- outdir: this.distDir,
277
- naming,
278
- minify: false,
279
- plugins: this.plugins
280
- });
281
- if (!buildResult.success) {
282
- appLogger.error(
283
- `[HMR] Generic script entrypoint build failed for ${entrypointPath}:`,
284
- buildResult.logs
285
- );
286
- }
271
+ const buildResult = await this.browserBundleService.bundle({
272
+ profile: "hmr-entrypoint",
273
+ entrypoints: [entrypointPath],
274
+ outdir: this.distDir,
275
+ naming,
276
+ minify: false,
277
+ plugins: this.plugins
278
+ });
279
+ if (!buildResult.success) {
280
+ appLogger.error(`[HMR] Generic script entrypoint build failed for ${entrypointPath}:`, buildResult.logs);
281
+ return;
282
+ }
283
+ const entrypointDependencies = buildResult.dependencyGraph?.entrypoints?.[entrypointPath];
284
+ if (entrypointDependencies) {
285
+ this.entrypointDependencyGraph.setEntrypointDependencies(entrypointPath, entrypointDependencies);
287
286
  }
288
287
  }
289
288
  /**
@@ -228,38 +228,38 @@ class ProjectWatcher {
228
228
  * rapid file changes efficiently.
229
229
  */
230
230
  async createWatcherSubscription() {
231
- if (!this.watcher) {
232
- const processorPaths = [];
233
- for (const processor of this.appConfig.processors.values()) {
234
- const watchConfig = processor.getWatchConfig();
235
- if (!watchConfig) continue;
236
- processorPaths.push(...watchConfig.paths);
237
- }
238
- if (fileSystem.exists(this.appConfig.absolutePaths.includesDir)) {
239
- processorPaths.push(this.appConfig.absolutePaths.includesDir);
240
- }
241
- if (fileSystem.exists(this.appConfig.absolutePaths.srcDir)) {
242
- processorPaths.push(this.appConfig.absolutePaths.srcDir);
243
- }
244
- if (fileSystem.exists(this.appConfig.absolutePaths.pagesDir)) {
245
- processorPaths.push(this.appConfig.absolutePaths.pagesDir);
246
- }
247
- if (fileSystem.exists(this.appConfig.absolutePaths.publicDir)) {
248
- processorPaths.push(this.appConfig.absolutePaths.publicDir);
249
- }
250
- if (this.appConfig.additionalWatchPaths.length) {
251
- processorPaths.push(...this.appConfig.additionalWatchPaths);
252
- }
253
- this.watcher = chokidar.watch(processorPaths, {
254
- ignoreInitial: true,
255
- ignorePermissionErrors: true,
256
- awaitWriteFinish: {
257
- stabilityThreshold: 50,
258
- pollInterval: 50
259
- }
260
- });
231
+ if (this.watcher) {
232
+ return this.watcher;
233
+ }
234
+ const processorPaths = [];
235
+ for (const processor of this.appConfig.processors.values()) {
236
+ const watchConfig = processor.getWatchConfig();
237
+ if (!watchConfig) continue;
238
+ processorPaths.push(...watchConfig.paths);
239
+ }
240
+ if (fileSystem.exists(this.appConfig.absolutePaths.includesDir)) {
241
+ processorPaths.push(this.appConfig.absolutePaths.includesDir);
242
+ }
243
+ if (fileSystem.exists(this.appConfig.absolutePaths.srcDir)) {
244
+ processorPaths.push(this.appConfig.absolutePaths.srcDir);
261
245
  }
262
- this.watcher.add(this.appConfig.absolutePaths.srcDir);
246
+ if (fileSystem.exists(this.appConfig.absolutePaths.pagesDir)) {
247
+ processorPaths.push(this.appConfig.absolutePaths.pagesDir);
248
+ }
249
+ if (fileSystem.exists(this.appConfig.absolutePaths.publicDir)) {
250
+ processorPaths.push(this.appConfig.absolutePaths.publicDir);
251
+ }
252
+ if (this.appConfig.additionalWatchPaths.length) {
253
+ processorPaths.push(...this.appConfig.additionalWatchPaths);
254
+ }
255
+ this.watcher = chokidar.watch(processorPaths, {
256
+ ignoreInitial: true,
257
+ ignorePermissionErrors: true,
258
+ awaitWriteFinish: {
259
+ stabilityThreshold: 50,
260
+ pollInterval: 50
261
+ }
262
+ });
263
263
  this.watcher.on("change", (p) => this.enqueueChange(() => this.handleFileChange(p, "change"))).on("add", (p) => this.enqueueChange(() => this.handleFileChange(p, "add"))).on("addDir", (p) => this.enqueueChange(() => this.triggerRouterRefresh(p))).on("unlink", (p) => this.enqueueChange(() => this.handleFileChange(p, "unlink"))).on("unlinkDir", (p) => this.enqueueChange(() => this.triggerRouterRefresh(p))).on("error", (error) => this.handleError(error));
264
264
  for (const processor of this.appConfig.processors.values()) {
265
265
  const watchConfig = processor.getWatchConfig();