Skip to content

Support .noderc or similar file-based initialization configurations? #53787

Description

@joyeecheung

In #52219 it was mentioned that for APM use cases it would be nice to have a way to register loaders without using command line flags. It occurred to me maybe what we need is an initialization config similar to the rc files out there for various applications.

For example in my .lldbinit I have these that extend LLDB to help me debug

plugin load /path/to/llnode.dylib
command script import /path/to/lldb_commands.py

It seems this is serving the same use case as the Node.js loader registration - run some scripts or register some plugin to Node.js before you actually start running it.

For loading loaders, I guess we can support something like a .noderc that is discovered when users start running Node.js, or make that part of package.json, which allows registering certain hooks before the actual application code is run. For example in the case of package.json, I guess the project pacakge.json would include something like this:

{
  "preload": [
     "apm-package/register"
   ],
  "dependencies": {
    "apm-package": "1.1.1"
  }
}

If we want something more flexible than "just loading some scripts" though, I guess a dedicated rc file would be easier to use than package.json.

cc @timfish @nodejs/loaders

Activity

  1. GeoffreyBooth commented on Jul 9, 2024

    @GeoffreyBooth
    Member

    I’ve proposed this in the past, such as in #49148 (comment) and https://github.com/orgs/nodejs/discussions/44975#discussioncomment-3868855. The short version is that I think we need a config file, such as node.config.json or possibly a section within package.json, and I think it needs to include all of the same options that NODE_OPTIONS includes. This will handle the use case of the test runner config file and any other features that have configuration that’s complicated enough to want to specify in a file, or have the configuration be updated by automation such as in the hooks case.

    The recent .env file support was meant as a bridge to this; that effort got us the ability to parse JSON files without needing to start V8, so therefore we can parse a config file that includes V8 options early enough for them to take effect. The work toward supporting NODE_OPTIONS within .env files was meant to lay the groundwork for this.

    So yes, I do think we need a config file, and it should support all of Node’s configuration (or at least as much as is in NODE_OPTIONS). That probably means it needs to be in JSON or some other format that we can parse without V8. A simple schema could probably be something like:

    {
      "options": {
        // Keys can be any flag available to NODE_OPTIONS, camelcased
        "import": "tsx",
        "experimentalRequireModule": true
      }
    }

    Or the same within a nodejs field in package.json if that’s preferable. Ideally this file is loaded by default if it exists, and we could have a flag like --config to backport it for older lines (or maybe this is a reason to use a key in package.json, if adding this new key isn’t considered a breaking change). I think it might be slightly preferable to have a dedicated file for this rather than package.json field because a project might have multiple package.jsons and we could separately define the configuration file and then Node knows where to look for it, but I don’t feel strongly if others can explain why package.json is preferable and won’t have issues.

    Another consideration is conditions; should a single file support multiple values based on condition, so you could do something like node --config=node.config.json --condition=development entry.js and it loads certain config values related to the development condition.

  2. added
    cliIssues and PRs related to the Node.js command-line interface.
    on Jul 9, 2024
  3. targos commented on Jul 10, 2024

    @targos
    Member

    +1, but I am very much against JSON for the format, mainly because it doesn't support comments.

  4. ShogunPanda commented on Jul 10, 2024

    @ShogunPanda
    Contributor

    +1 here as well. And I agree with @targos.
    If we don't create a custom format, I would choose YAML or TOML.

  5. GeoffreyBooth commented on Jul 10, 2024

    @GeoffreyBooth
    Member

    It's important that the format be easily editable by other tools, such as those registering hooks. Anything other than comment-less JSON means that those tools need a dependency, unless we create a new builtin for the format.

    We've had package.json for years and it's been acceptable. A bit annoying that it doesn't support comments or trailing commas, for sure, but it's easily editable both by hand and by tools. See the recent strongly negative reaction to Bun allowing comments in package.json; that's why interoperability is important.

  6. ShogunPanda commented on Jul 10, 2024

    @ShogunPanda
    Contributor

    That was my idea. Have a new builtin format.
    TBH, I always thought Node should support YAML natively since it's quite pervasive.

  7. joyeecheung commented on Jul 10, 2024

    @joyeecheung
    MemberAuthor

    If we want to be aligned with npmrc, https://www.npmjs.com/package/ini can be an option too (there are other native INI parsers too, like https://github.com/benhoyt/inih)

    and it should support all of Node’s configuration (or at least as much as is in NODE_OPTIONS

    I think we should consider supporting a subset and evaluate them on a case by case basis. NODE_OPTIONS are about “flat” command line options, some of them per-process, some of them per-isolate, some of them per-environment. I think for the file based config we need something better than a flat structure to avoid the NODE_OPTIONS and execArgv inheritance validity problem again, and it will take time to address the hierarchical problem for all the possible configs (I also think the current internal classification of options may still contain quite some errors to be surfaced to the config directly)

  8. jsumners-nr commented on Jul 10, 2024

    @jsumners-nr

    I am in favor of a standalone file with the following opinions:

    1. I have never enjoyed adding configuration to package.json. It just feels wrong to me. It's a package manifest, not program configuration.
    2. JSON is the native format for the language we are working in, but if we are parsing prior to V8, no need to stick to that as a limitation.
    3. Comments are a necessary possibility for configuration files.
    4. INI, and in particular npm's wacko variant of it, is not ideal.
    5. Supporting JSON, YAML, and maybe TOML would be my preference.
    6. Config files should require a proper extension so that tooling (and the implementation) can easily detect the format: .noderc.json, .noderc.yaml, .noderc.yml, and .noderc.toml.
  9. Qard commented on Jul 10, 2024

    @Qard
    Member

    I'm a fan of KDL.

    I feel like the need for a config file format parser if we go with non-json (which we should because comments) suggests it might be of value to also have something built in for generalized parsing, which could also be helpful for stream processing in many cases. I wonder if we should take that into consideration when building whatever format parser we might need. 🤔

  10. GeoffreyBooth commented on Jul 10, 2024

    @GeoffreyBooth
    Member

    Whatever format we read needs to be parseable in C++. We added simdjson to be able to parse JSON in C++, so that’s an option. The addition of --env-file gave us the ability to parse essentially the INI format.

    Another option is to expand out .env files into essentially configuration files, by creating new environment variables for every option. So my example above could become an .env file like:

    # node.config.env
    NODE_OPTION_IMPORT=tsx
    NODE_OPTION_EXPERIMENTAL_REQUIRE_MODULE=true

    And it gets loaded via node --env-file=node.config.env app.js just like any other .env file, and the options are read from these new environment variables. This has the benefit of automatically being inherited into child processes whenever env is passed down. We already have util.parseEnv to parse this into an object, and we could add a corresponding setter method to help convert such objects back into INI format strings.

  11. joyeecheung commented on Jul 10, 2024

    @joyeecheung
    MemberAuthor

    I think making this opt-in would bring us back to square one, especially if the surface is limited to a flat list, then it’s not really too different than .env; but that’s also inconsistent with the broader software convention of loading a rc file by default.

  12. joyeecheung commented on Jul 10, 2024

    @joyeecheung
    MemberAuthor

    this has the benefit of automatically being inherited into child processes whenever env is passed down.

    I think this is actually a flaw, not a benefit, because a flat list of environment variables is not a great way for inherited configurations, see #41103 - child workers and contexts need granular control of configurations.

  13. GeoffreyBooth commented on Jul 10, 2024

    @GeoffreyBooth
    Member

    I was thinking that whatever config file we choose would be loaded automatically as a semver-major change, and a flag could specify it for older Node versions. A flag might be used for the latest version too in case the user wants to specify a file that’s not the default filename or location.

    I’m not sure what about #41103 involves any of this. We should just filter out options that don’t make sense to inherit, both currently to resolve that issue and in the future when we add a proper config file. We should do that filtering however the problematic options are defined, such as via environment variables or NODE_OPTIONS or via a file. Users already have ways to exert granular control of what flags and/or environment variables are passed into child processes or workers, via execArgv or env.

  14. isaacs commented on Jul 11, 2024

    @isaacs
    Contributor

    We all hate json. No comments, excessive quoting, no multi line strings, no trailing commas, etc.

    But:

    • it's specified very clearly (unlike ini, which is not specified at all)
    • it's built into the language
    • It's FAST, like, omg wow, much faster than yaml or toml, not even close. Even jsonc is much slower.
    • vim and vscode can auto format a js object into json with a keyboard shortcut
    • We could reserve the "//" key to be explicitly ignored (like npm has done) and use that for comments.
    • Every node program already has a json file just sitting in the root, which node already loads for a bunch of stuff, with very clear and unambiguous semantics.
    • it can never change and has one version (unlike yaml and toml)

    A nodejs section in package.json is ideal. Make it so that if it's a string, then that's the name of a file to load, like many other tools do. Then we don't have to agree on the perfect spelling for the filename.

    The beauty of json is that no one has to agree on very much.

  15. joyeecheung commented on Jul 11, 2024

    @joyeecheung
    MemberAuthor

    A nodejs section in package.json is ideal. Make it so that if it's a string, then that's the name of a file to load, like many other tools do. Then we don't have to agree on the perfect spelling for the filename.

    I am leaning towards "a field specified in package.json pointing to a file" too primarily because to load this by default, it adds overhead to the startup to probe the file system; And since we already probe the file system for package.json, the overhead of probing another field in it would be the smallest.

    But I do like to see a fixed (at least base) name of the file, because I like the concept of universally recognizable manifest of projects, just like when I check out a JS project/package, if I want to see dependency information I go to package.json, if I want to see TypeScript information I go to tsconfig.json, if I want to see their eslint configs I go to any file that contains the word eslint etc. Maybe something we can do is to support multiple formats (like what eslint does), and JSON would be the first to support, but they all have the same basename, and people just pick the format they like, provided that we do add support for other formats later on.

  16. 61 remaining items

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

    cliIssues and PRs related to the Node.js command-line interface.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions