@ecopages/core 0.2.0-beta.40 → 0.2.0-beta.42

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/core",
3
- "version": "0.2.0-beta.40",
3
+ "version": "0.2.0-beta.42",
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-beta.40",
20
+ "@ecopages/file-system": "0.2.0-beta.42",
21
21
  "@ecopages/logger": "^0.2.3",
22
22
  "@ecopages/scripts-injector": "^0.1.5",
23
23
  "@oxc-project/runtime": "0.141.0",
@@ -32,7 +32,7 @@
32
32
  "@standard-schema/utils": "^0.3.0"
33
33
  },
34
34
  "peerDependencies": {
35
- "@ecopages/dev-toolbar": "0.2.0-beta.40"
35
+ "@ecopages/dev-toolbar": "0.2.0-beta.42"
36
36
  },
37
37
  "peerDependenciesMeta": {
38
38
  "@ecopages/dev-toolbar": {
@@ -1,6 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { getAppServerInvalidationState } from '../runtime-state/server-invalidation-state.service.js';
3
3
  import { appLogger } from '../../global/app-logger.js';
4
+ import { clearAppDevelopmentRouteModuleBuildCaches } from '../module-loading/route-module-build-cache-registry.js';
4
5
  /**
5
6
  * Framework-owned development invalidation service.
6
7
  *
@@ -29,6 +30,7 @@ export class DevelopmentInvalidationService {
29
30
  invalidateServerModules(changedFiles) {
30
31
  getAppServerInvalidationState(this.appConfig).invalidateServerModules(changedFiles);
31
32
  this.appConfig.runtime?.appModuleLoader?.invalidateDevelopmentGraph();
33
+ clearAppDevelopmentRouteModuleBuildCaches(this.appConfig);
32
34
  }
33
35
  /**
34
36
  * Registers an integration-owned handler for registered script entrypoint edits.
@@ -57,8 +59,8 @@ export class DevelopmentInvalidationService {
57
59
  * Resets runtime-owned graph state and invalidates server modules.
58
60
  */
59
61
  resetRuntimeState(changedFiles) {
62
+ this.invalidateServerModules(changedFiles);
60
63
  const serverInvalidationState = getAppServerInvalidationState(this.appConfig);
61
- serverInvalidationState.invalidateServerModules(changedFiles);
62
64
  serverInvalidationState.reset();
63
65
  }
64
66
  /**
@@ -33,7 +33,7 @@ Call site (route scan, renderer, SSG, API)
33
33
  2. **Disk transpile cache** (`.eco/.server-modules/.build-cache.json`) — production and stable development graphs when dependency hashes match. Manifest field `corePackageVersion` invalidates entries when the framework package changes.
34
34
  3. **Unified graph manifest** — production static export fast path only; see build layer docs.
35
35
 
36
- Development `?update=` query params use `sourceHash` plus a per-service import generation counter so Node and Bun bust ESM module cache after `invalidateDevelopmentGraph()` without a process-wide invalidation version in reuse keys.
36
+ Development import URLs use `sourceHash` plus a per-service import generation counter. Node uses that value in its `?update=` query. Bun also receives a generation-specific compiled output filename because it retains a previously imported file when only its query changes. Both paths advance after `invalidateDevelopmentGraph()` without a process-wide invalidation version in reuse keys.
37
37
 
38
38
  ## Files
39
39
 
@@ -49,4 +49,4 @@ Development `?update=` query params use `sourceHash` plus a per-service import g
49
49
 
50
50
  ## Development invalidation
51
51
 
52
- `DevelopmentInvalidationService.invalidateServerModules()` calls `appModuleLoader.invalidateDevelopmentGraph()`, which clears the in-memory import cache, bumps the per-service dev import generation used only for `?update=` URLs, and clears dependency-hash memoization. Route-module disk reuse stays content-derived: unchanged source and dependency hashes keep their entries until the edited module or its graph changes.
52
+ `DevelopmentInvalidationService.invalidateServerModules()` calls `appModuleLoader.invalidateDevelopmentGraph()`, which clears the in-memory import cache, bumps the per-service dev import generation used for runtime URLs and Bun output filenames, and clears dependency-hash memoization. It also clears persisted route-module entries: externalized generated modules can change without appearing in a route bundle's dependency graph, so retaining those entries could reload stale server HTML.
@@ -127,10 +127,7 @@ export class PageModuleImportService {
127
127
  async loadModule(options) {
128
128
  const { filePath, fileHash, importCacheKey } = options;
129
129
  const { rootDir, outdir, splitting, externalPackages, transpileErrorMessage = (details) => `Error transpiling page module: ${details}`, noOutputMessage = (targetFilePath) => `No transpiled output generated for page module: ${targetFilePath}`, } = options;
130
- const outputFileName = resolvePageModuleOutputFileName({
131
- filePath,
132
- fileHash,
133
- });
130
+ const outputFileName = createRuntimeBuildOutputFileName(resolvePageModuleOutputFileName({ filePath, fileHash }), this.developmentImportGeneration);
134
131
  const outputNamingTemplate = outputFileName.replace(/\.mjs$/u, '.[ext]');
135
132
  const preferredOutputPath = path.join(outdir, outputFileName);
136
133
  const buildOptions = this.appConfig
@@ -229,3 +226,17 @@ function createRuntimeModuleUrl(filePath, fileHash, developmentImportGeneration)
229
226
  function shouldAddRuntimeUpdateQuery() {
230
227
  return process.env.NODE_ENV === 'development';
231
228
  }
229
+ /**
230
+ * Gives Bun a distinct compiled module path after development invalidation.
231
+ *
232
+ * @remarks
233
+ * Bun does not reload an already-imported module when only its URL query changes.
234
+ * A dependency edit can leave the entrypoint source hash unchanged, so the output
235
+ * filename must include the import generation as well as the runtime query.
236
+ */
237
+ function createRuntimeBuildOutputFileName(outputFileName, developmentImportGeneration) {
238
+ if (typeof Bun === 'undefined' || !shouldAddRuntimeUpdateQuery()) {
239
+ return outputFileName;
240
+ }
241
+ return outputFileName.replace(/\.mjs$/u, `-${developmentImportGeneration}.mjs`);
242
+ }
@@ -22,3 +22,5 @@ export declare const getRouteModuleBuildCacheOutdir: typeof getServerModuleBuild
22
22
  * generation during the same build process.
23
23
  */
24
24
  export declare function getSharedRouteModuleBuildCache(outdir: string, appConfig?: EcoPagesAppConfig): RouteModuleBuildCache;
25
+ /** Clears route-module cache entries that can retain stale external server modules during development. */
26
+ export declare function clearAppDevelopmentRouteModuleBuildCaches(appConfig: EcoPagesAppConfig): void;
@@ -49,3 +49,15 @@ export function getSharedRouteModuleBuildCache(outdir, appConfig) {
49
49
  caches.set(outdir, routeModuleBuildCache);
50
50
  return routeModuleBuildCache;
51
51
  }
52
+ /** Clears route-module cache entries that can retain stale external server modules during development. */
53
+ export function clearAppDevelopmentRouteModuleBuildCaches(appConfig) {
54
+ const serverModuleCacheOutdir = getServerModuleBuildCacheOutdir(appConfig);
55
+ const caches = appConfig.runtime?.routeModuleBuildCaches;
56
+ const serverModuleCache = caches?.get(serverModuleCacheOutdir) ?? new RouteModuleBuildCache(serverModuleCacheOutdir);
57
+ serverModuleCache.clearDevelopmentEntries();
58
+ for (const cache of caches?.values() ?? []) {
59
+ if (cache !== serverModuleCache) {
60
+ cache.clearDevelopmentEntries();
61
+ }
62
+ }
63
+ }
@@ -51,6 +51,16 @@ export declare class RouteModuleBuildCache {
51
51
  context: RouteModuleStaticRenderCacheContext;
52
52
  }): void;
53
53
  resetMemory(): void;
54
+ /**
55
+ * Removes every persisted route-module entry after development invalidation.
56
+ *
57
+ * @remarks
58
+ * Server bundles may externalize generated modules whose source dependencies
59
+ * are not present in the route bundle's dependency graph. Keeping an entry
60
+ * after its server graph changes can therefore reload a route that still
61
+ * imports an obsolete external bundle.
62
+ */
63
+ clearDevelopmentEntries(): void;
54
64
  pruneStaleRenderedOutputs(activePathnames: ReadonlySet<string>): string[];
55
65
  private loadManifest;
56
66
  private persistManifest;
@@ -164,6 +164,24 @@ export class RouteModuleBuildCache {
164
164
  this.manifest = undefined;
165
165
  this.manifestLoaded = false;
166
166
  }
167
+ /**
168
+ * Removes every persisted route-module entry after development invalidation.
169
+ *
170
+ * @remarks
171
+ * Server bundles may externalize generated modules whose source dependencies
172
+ * are not present in the route bundle's dependency graph. Keeping an entry
173
+ * after its server graph changes can therefore reload a route that still
174
+ * imports an obsolete external bundle.
175
+ */
176
+ clearDevelopmentEntries() {
177
+ if (!this.manifestLoaded && !this.dependencies.exists(this.manifestPath)) {
178
+ return;
179
+ }
180
+ const manifest = createEmptyRouteModuleBuildCacheManifest();
181
+ this.manifest = manifest;
182
+ this.manifestLoaded = true;
183
+ this.persistManifest(manifest);
184
+ }
167
185
  pruneStaleRenderedOutputs(activePathnames) {
168
186
  if (process.env.NODE_ENV !== 'production') {
169
187
  return [];