Skip to content

fix: inject import.meta.hot into every module using import.meta - #20

Merged
Julien-R44 merged 1 commit into
mainfrom
fix/import-meta-hot-injection
Nov 6, 2026
Merged

Julien-R44 merged 1 commit into
mainfrom
fix/import-meta-hot-injection

Conversation

@Julien-R44

Copy link
Copy Markdown
Owner

Fixes a regression from ed28a8c (#15), which is on main but not released yet.

Why

ed28a8c only injects the import.meta.hot helpers into modules whose source contains import.meta.hot. @adonisjs/fold (and @adonisjs/auth) decide whether to re-import a controller with "hot" in import.meta, which doesn't match, so on main AdonisJS keeps serving the cached controller after an edit. Reproduced in a fresh Adonis 7 app: controller edit stays at v1 on main, v1 → v2 with this PR. Any other way of reading hot (minified code, import.meta\n.hot, const { hot } = import.meta, import.meta['hot']) is missed the same way.

What changes

  • Inject into every module whose source contains import.meta. A module can only reach its import.meta by writing it in its own source (eval and new Function throw Cannot use 'import.meta' outside a module), so modules without it still load unchanged, and no access to hot can be missed.
  • For Add option to skip code injection for import.meta.hot #15: injected modules keep exact positions when they have an inline source map (tsx, ts-node, @poppinss/ts-exec). The injected code is on line 1 only, so hot-hook shifts the first generated column of the map by its length (source_map.ts, a single VLQ value). Costs 0.03 ms for a controller, 0.25 ms for a 26 KB file.
  • source is decoded with TextDecoder: toString() on a Uint8Array (returned when an async loader is in the chain) gives comma-separated bytes.

Limitations

  • JS modules that use import.meta and have no source map (or an external .map file) still get line 1 shifted, as before ed28a8c. A per-column identity map would be ~5x the file size.
  • With the current off-thread hooks (module.register()), a sync TS loader such as @poppinss/ts-exec reads the source through hot-hook, so hot-hook sees untranspiled TS without a map and can't shift it (same as the published 1.0.0). With in-thread hooks (feat: use module.registerHooks() instead of deprecated module.register() #19), hot-hook wraps the loader output and the map is shifted.
  • Native TypeScript (node file.ts without a loader, format: 'module-typescript') still doesn't get import.meta.hot, as before. Injecting there would shift TS loaders' maps in the case above, since hot-hook can't tell whether a transpiler runs after it.

Verification

  • 3 tests, each failing without its part of the fix: "hot" in import.meta gets the helpers (fails with ed28a8c's filter), an inline map keeps original.ts:1:11 (fails without the shift), a module without import.meta keeps its positions (fails without a filter).
  • pnpm quick:test 29/29 on Node 22.14, 22.15, 22.22.3, 24.22.2, 26.12.1.
  • Fresh create-adonisjs apps driving node ace serve --hmr (boot, controller and service HMR, runtime error and recovery, full reload on routes, restart on .env, HMR after restart): 9/9 for Adonis 7 on Node 26, Adonis 6 on Node 26 and 22.15.
  • Line-1 error positions with --enable-source-maps, without hot-hook vs with: tsx 1:51 / 1:51, ts-node 1:51 / 1:51.

ed28a8c only injected the helpers into modules containing
`import.meta.hot`. @adonisjs/fold re-imports controllers only when
`"hot" in import.meta`, so AdonisJS stopped hot reloading controllers.

A module can only reach its `import.meta` by writing it in its own
source, so inject into every module containing `import.meta` and leave
the others unchanged. To keep positions exact in injected modules,
shift their inline source map (tsx, ts-node, @poppinss/ts-exec) by the
length of the injected code.

Also decode `source` with TextDecoder: `toString()` on a Uint8Array
returns its comma-separated bytes.

Refs #15

Amp-Thread-ID: https://ampcode.com/threads/T-01a1b212-dc0a-7380-bad5-624903b77b96
@Julien-R44
Julien-R44 merged commit e4b4602 into main Nov 6, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant