Skip to content

babel-register doesn't save modules to cache properly #9320

Description

@jbasrai

Bug Report

Current Behavior
For scripts that don't exit the event loop (e.g. an express server), modules that are loaded outside of the original script don't get cached.

Input Code
Minimal repro here: https://github.com/jbasrai/babel-register-cache

  1. yarn
  2. node index.js
  3. Ctrl-C to end process

Expected behavior/code
Expected world.js to be cached, but inspecting the cache reveals that it isn't.

Babel Configuration (.babelrc, package.json, cli command)

module.exports = function (api) {
	api.cache.forever()

	return {
		presets: [ '@babel/preset-env' ]
	}
}

Environment

  • Babel version(s): core v7.2.2, register v7.0.0
  • Node/npm version: Node v8.12, npm v5.8
  • OS: OSX 10.12.6
  • Monorepo: no
  • How you are using Babel: register

Possible Solution
The process.on('exit') listener is not being used correctly. The docs say that it is only called when process.exit() is called explicitly OR when Node's event loop no longer has work to perform. In the above case, none of these conditions are met, and so a required module is never cached.

Instead, I think a more comprehensive exit handler should be used to save the cache.

Activity

  1. babel-bot commented on Jan 11, 2019

    @babel-bot
    Collaborator

    Hey @jbasrai! We really appreciate you taking the time to report an issue. The collaborators
    on this project attempt to help as many people as possible, but we're a limited number of volunteers,
    so it's possible this won't be addressed swiftly.

    If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack
    community
    that typically always has someone willing to help. You can sign-up here
    for an invite.

  2. chrisisler commented on Jan 15, 2019

    @chrisisler

    This does the trick for me:

    process.on('SIGINT', () => {
      console.log('Received Control-C')
    })

    I'm on macOS 10.12.3 with Node v11.6.0

    The relevant Node v11.x docs say that the signal event given upon Control-C is SIGINT which is non-Windows. The necessary solution may include something like this for Windows and non-Windows operating systems:

    function handle(signal: string): void {
      console.log(signal) //=> Either 'SIGINT' or 'SIGTERM'
    }
    
    process.on('SIGINT', handle)
    process.on('SIGTERM', handle)

    The process.on('exit', fn) only seems to be good for when the script is exited on it's own accord.

  3. chrisisler commented on Jan 15, 2019

    @chrisisler

    Okay, @jbasrai, I cloned your repo, ran yarn install, and added the following lines to node_modules/@babel/register/lib/cache.js:

    process.on("exit", save);
    process.on("SIGINT", save); // here
    process.on("SIGTERM", save); // and here

    Then I ran $ node index.js and got the following output:

    hello
    world
    .
    .
    .
    

    And there's a bunch of ^C and ^D's in my output from spamming Control-C and Control-D to try and exit the script (then you have to press Control-Z to move the script to the background and regain control of the terminal, then use $ kill %% or close the terminal to actually stop the script from running).

    I think the problem is that the save() function in @babel/register doesn't listen for the signal provided by the nodejs process signal events API.

    Node v8.x Process Signal Events Docs.

    What I've found solves the problem is that ifsave() accepted a signal argument and checked for signal === "SIGINT" then we can process.exit(2) just like the native behavior (at least on macOS).

    function save(signal) {
      if (signal === "SIGINT") {
        console.log("Received Control-C");
        process.exit(2);
      }
    
      let serialised = "{}";
      // Rest of body code, etc...
    }

    Someone should test this on a Windows box to see if we need to check for SIGTERM as well (signal === "SIGINT" || singal === "SIGTERM" may be necessary).

    Hope this is helpful.

  4. JLHwung commented on Oct 16, 2019

    @JLHwung
    Contributor

    @chrisisler Could you open up a PR to have save() listen for SIGINT and SIGTERM?

  5. jedwards1211 commented on May 6, 2025

    @jedwards1211
    Contributor

    @JLHwung I think it would also be good to call a debounced save function anytime the in-memory cache is updated.

    But beyond this, I think it would be better to save each input file's transformed code and source maps in individual files within a cache directory so that tasks which only load a small number of modules in a large codebase don't have to load one giant JSON file into memory

  6. liuxingbaoyu commented on May 6, 2025

    @liuxingbaoyu
    Member

    @jedwards1211 #17229 should fix it.

  7. added
    outdatedA closed issue/PR that is archived due to age. Recommended to make a new issue
    on May 2, 2026
  8. locked as resolved and limited conversation to collaborators on May 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    outdatedA closed issue/PR that is archived due to age. Recommended to make a new issuepkg: register

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions