Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ESM support: Roadmap to 5.0 #841

@jdmarshall

Description

@jdmarshall

Summary

Some initial brainstorming ideas around migrating node-config to ESM and potentially eliminating the singleton entirely.
Given that all of the LTS supported NodeJS versions now have a way to require ESM modules, and we now support strict one or both of these seemed reasonable, even overdue.

Subsequently, I ran into a number of roadblocks, particularly to do with how idiomatic use of require inside of the config directly is very much at odds with how ESM modules want to handle imports. This leads to issues with createRequire even before we get into the tests, which themselves are also actively antagonistic to use of import, createRequire, or any mixture of both.

In the interests of regaining some momentum, I am now cutting scope a bit and altering the strategy. I have a route forward that I believe gives us more choices going forward for the features that are being cut, while giving me a straighter path to a 5.0 release that I can ship.

This work is still informed by #569, with some continued additions, alterations, and substitutions, informed by other feature requests and bug reports.

The Plan

For 4.3 I am working toward the following goals:

[x] reduce reliance on requireUncached in the tests on this function - previously 80 uses.
[x] introduce defer and util to executable config files by supporting export of a callback function to evaluate the file
[x] deprecate async.js
[x] relocate deferConfig's implementation into lib, and deprecate node-config/defer, which doesn't work great with imports anyway
[x] modify defer to accept async functions
[ ] deprecate raw.js
[ ] expose raw through the same callback mechanism as defer

For 5.0 then,

  • Utilize a cache busting workaround for the remaining uses of requireUncached
  • Remove async.js, defer.js, raw.js
  • Remove config.util.makeImmutable()
  • Fix immutability bugs in lib/util.js
  • Switch to ESM
  • use createRequire to replace require in loadFiles() and elsewhere
  • Create prerelease builds of 5.0.0 for public commentary.

Blocking Issues

Bootstrapping issues

The require() thunking layers don't like exporting a module that is still being loaded.

The most straightforward way to fix this also addresses #740, which is to modify node-config to allow executable config
files to return a function instead of the data. The function can then receive defer and util as arguments instead of having to load them. This is a small but

Imports are Immutable

If you look at the requireCache for createRequire you would think that there's a way to reset ESM modules the same way you reset CJS modules for testing purposes. You would be wrong. So we need a way to completely reset the state of node-config for different test suites.

After the work I did in the spring, there are only a couple of variables that are still file scope. FIRST_LOAD, config, and some error handling variables that really are only used in one spot and so could be tucked into a function hanging off of the init process.

The problem is that the one use of 'FIRST_LOAD' happens inside Config.get, which looks like everything works properly until you try to do a Config.get.get and you find out that this has changed and so you cannot just hang the load object off of it to allow for state reload across multiple calls.

Not that we probably want reload across multiple calls in the production version. The simplest solution might be to set a flag that forces reload on every invocation.

Resolved Issues

  • vows is CJS, and seems to be causing issues during loading vows needs to go #807 Remove vows from tests. #854
  • Coffeescript only blows up if we set the module type in package.json. It causes all the tests to break if set. There are other ways to delay doing that.

Vows

In particular, since vows plays games with module.exports, if you try to convert the tests to cjs to attempt to continue to make the code work, then the tests end up require()ing the code, and then parser.js freaks out because createRequire doesn't work with an error:

Cannot set properties of undefined (setting 'id')

Sub-issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions