@bakery-framework/core 2.2.0 → 2.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bakery-framework/core",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Bakery framework core: handlers, router, config, session, caches, logger, compiler.",
5
5
  "keywords": [
6
6
  "bakery",
@@ -1,5 +1,21 @@
1
1
  import { Bakery } from '../core/bakery'
2
+ import { HandlerMap } from '../handlers/core/$registry'
3
+
4
+ /**
5
+ * The dev watcher's route reset, run on every change to a route file: each
6
+ * handler's own caches, and `HandlerMap.routeCache` with them.
7
+ *
8
+ * The route cache is what a created file needs cleared. It remembers which
9
+ * handler won a path, and a hit re-asks only that handler, so a winner that
10
+ * still says yes keeps the path after a file that outranks it appears.
11
+ * `StaticHandler` says yes to every path. Measured in a dev app: with a root
12
+ * catch-all deleted and `/` requested (404, as livereload does on the delete),
13
+ * `/` still answered 404 three seconds after `index.vue` was written, until
14
+ * the dev worker restarted. And `/about` kept serving `about.html` after an
15
+ * `about.vue` (VueHandler, 58, above HTMLHandler, 55) was written beside it.
16
+ */
2
17
  export function initRoutes() {
18
+ HandlerMap.routeCache.clear()
3
19
  for (const [, handlers] of Object.entries(Bakery.handlers)) {
4
20
  for (const HandlerClass of handlers.list()) {
5
21
  HandlerClass.initRoutes()
@@ -456,9 +456,8 @@ const watchIgnores = Glob.strings(
456
456
  // restart was Bun's module registry caching the imported page module, and
457
457
  // TSXHandler now busts that per request in dev exactly as ApiHandler always
458
458
  // has. Pages take the cheap path via `tsScriptGlob` above. The one thing the
459
- // restart still bought (flushing *components* a page imports) is a
460
- // documented limitation (docs/getting-started/first-app.md): touch
461
- // server.config.ts or restart when editing a shared Layout.tsx.
459
+ // restart still bought, flushing the *components* a page imports,
460
+ // `isLoadedModule` now does for exactly the files that need it.
462
461
  const prioFilesGlob = Glob.strings('server.config.ts')
463
462
 
464
463
  /**
@@ -563,6 +562,45 @@ export function isCreatedRouteModule(
563
562
  return MODULE_ROUTE_EXT.test(filePath)
564
563
  }
565
564
 
565
+ /**
566
+ * Whether `filePath` is a module this process imported through a plain
567
+ * specifier, so Bun's registry keeps serving the copy it already has and an
568
+ * edit cannot reach the running server without a restart.
569
+ *
570
+ * Routes are imported cache-busted (`bustInDev`'s `?v=<mtime>`), and the
571
+ * registry keys those with their query, so an edited route is not one: the
572
+ * next request imports it under a new key. Everything a route imports is
573
+ * keyed bare, and that is what used to go stale: a component a page imports
574
+ * (a shared `Layout.tsx`), and a helper an API route imports from outside the
575
+ * api directory, such as a top-level `server/` folder of permission checks.
576
+ * Measured in a dev app: an edit to `server/auth.ts` did not apply until a
577
+ * file under `src/api` was touched.
578
+ *
579
+ * `require.cache` is where Bun lists them, ES modules included (1.4.2: a
580
+ * static import keyed by path, `a.ts?v=1` keyed with its query). A Bun that
581
+ * stopped listing them would turn this back into the old behavior rather than
582
+ * break anything, and `dev-service.test.ts` asks the real registry, so it
583
+ * would say so.
584
+ */
585
+ export function isLoadedModule(
586
+ filePath: string,
587
+ loaded: Iterable<string> = Object.keys(require.cache),
588
+ cwd: string = fs.cwd,
589
+ ): boolean {
590
+ const target = modulePathKey(resolve(cwd, filePath))
591
+ for (const key of loaded) {
592
+ if (!key.includes('?') && modulePathKey(key) === target) return true
593
+ }
594
+ return false
595
+ }
596
+
597
+ /** One spelling of a path for comparison: forward slashes, and case-folded
598
+ * on Windows, where `Auth.ts` and `auth.ts` are one file. */
599
+ function modulePathKey(path: string): string {
600
+ const slashed = path.replaceAll('\\', '/')
601
+ return process.platform === 'win32' ? slashed.toLowerCase() : slashed
602
+ }
603
+
566
604
  async function processFileEvent(
567
605
  filePath: string,
568
606
  server: any,
@@ -588,6 +626,12 @@ async function processFileEvent(
588
626
  return process.exit(42)
589
627
  }
590
628
 
629
+ // A module the server holds a plain copy of: see `isLoadedModule`.
630
+ if (isLoadedModule(filePath)) {
631
+ serveLog.BACKEND_CHANGE({ file: filePath })
632
+ return process.exit(42)
633
+ }
634
+
591
635
  if (tsScriptGlob.match(filePath)) {
592
636
  initRoutes()
593
637
  return notifySockets(server, filePath)
@@ -35,9 +35,10 @@ const dynamicCaches = new Map<any, HandlerCache<RegExp, Route.Info>>()
35
35
  * a bare `PROD` gate flipped mid-suite and broke the reload tests in files
36
36
  * loaded after it, while passing in isolation.
37
37
  *
38
- * Only the route module itself is busted: components it imports (a shared
39
- * `Layout.tsx`, helpers) stay in Bun's registry until a restart. Documented in
40
- * `docs/getting-started/first-app.md`.
38
+ * Only the route module itself is busted: components and helpers it imports
39
+ * (a shared `Layout.tsx`) stay in Bun's registry, and the dev watcher restarts
40
+ * the worker when one of them changes (`isLoadedModule` in
41
+ * compiler/dev-service.ts).
41
42
  *
42
43
  * One implementation on purpose. `ApiHandler` and `TSXHandler`/`TSXErrorHandler`
43
44
  * both need it, and they carried a byte-identical copy each (including this