Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarification Needed: require('http') and require('node:http') with existing require.cache entries #52970

Closed
gitspeaks opened this issue May 13, 2024 · 20 comments
Labels
doc Issues and PRs related to the documentations. loaders Issues and PRs related to ES module loaders question Issues that look for answers.

Comments

@gitspeaks
Copy link

Affected URL(s)

https://nodejs.org/docs/latest-v20.x/api/modules.html#core-modules

Description of the problem

According to the documentation:

Core modules can be identified using the node: prefix, in which case it bypasses the require cache. For instance, require('node:http') will always return the built in HTTP module, even if there is require.cache entry by that name"

And:

Some core modules are always preferentially loaded if their identifier is passed to require(). For instance, require('http') will always return the built-in HTTP module, even if there is a file by that name."

Given this, if require('http') always returns the built-in HTTP module, under what circumstances would a require.cache entry by that name exist, making it necessary to use require('node:http')?

@gitspeaks gitspeaks added the doc Issues and PRs related to the documentations. label May 13, 2024
@RedYetiDev RedYetiDev added question Issues that look for answers. loaders Issues and PRs related to ES module loaders labels May 13, 2024
@RedYetiDev
Copy link
Member

@nodejs/loaders

@aduh95
Copy link
Contributor

aduh95 commented May 14, 2024

If you populate require.cache.http manually, e.g. node -e 'require("./test/fixtures/empty.js");require.cache.http = require.cache[require.resolve("./test/fixtures/empty.js")];console.log(require("http") === require("./test/fixtures/empty"));'

@gitspeaks
Copy link
Author

@aduh95 why is that allowed for preferentially loaded modules ?

@aduh95
Copy link
Contributor

aduh95 commented May 14, 2024

I don’t understand the question, what is allowed?

@gitspeaks
Copy link
Author

As your example demonstrates, manually modifying the cache entry for preferentially loaded core modules bypasses the intended preferential loading characteristics. This seems counterproductive to the purpose of preferential loading.

@RedYetiDev
Copy link
Member

RedYetiDev commented May 14, 2024

IIUC if you have a dependency named http, the internal module will be preferred, and overriding the cache will override everything.

@aduh95
Copy link
Contributor

aduh95 commented May 14, 2024

I don’t know the reason, and it doesn’t really matter, that behavior cannot be changed without breaking the ecosystem. If you don’t like that behavior, you can add the node: prefix.
The only thing we can do is improving the docs, if you have some suggestion, please share and/or open a PR.

@gitspeaks
Copy link
Author

@RedYetiDev

and overriding the cache will override everything.

IIUC, the goal of preferential loading is to 'protect' core modules from being overwritten. Allowing manual manipulation of their cache entries seems to defeat this purpose.

@gitspeaks
Copy link
Author

@aduh95

that behavior cannot be changed without breaking the ecosystem.

Is manually manipulating cache entries for core modules a common scenario?

@aduh95
Copy link
Contributor

aduh95 commented May 14, 2024

IIUC, the goal of preferential loading is to 'protect' core modules from being overwritten. Allowing manual manipulation of their cache entries seems to defeat this purpose.

Like I said, the “goal” does not really matter, it is the way it is, and changing it is simply not worth it at this point. As someone who wasn’t involved at all in the development of CJS, I can’t tell you what was the rationale behind this design choice – or maybe it was an oversight, again, I have no idea – but as a maintainer I can tell you that whatever it is, it won’t change the fact that we want maximum stability on that area of the code base, and any PR that would try to amend the behavior would likely get rejected.
CJS has not the ideal API surface, and we’re trying to not repeat that with ESM (that’s one of the reasons we don’t expose the ESM module cache, for example); for CJS there’s not much we can do but improving the docs.

@gitspeaks
Copy link
Author

gitspeaks commented May 14, 2024

Then, at the very least, I suggest amending the documentation to clarify the behavior, with something like:

"Some core modules are always preferentially loaded if their identifier is passed to require(). For instance, require('http') will return the built-in HTTP module, even if there is a file by that name. However, note that core modules imported without the node: prefix are served from the cache if an entry for the module exists. Since cache entries of core modules can be manually manipulated, this allows replacing the implementation for the dependent module. If you want to ensure obtaining the original implementation, use the node: prefix."

@aduh95
Copy link
Contributor

aduh95 commented May 14, 2024

I’m not sure “the original implementation” is the correct phrasing, as it’s still possible to overwrite built-in modules as well (e.g. you could have require("node:http").createServer = function myCustomImplentation(){} somewhere in your code). In any case, clarifying the docs SGTM, would you like to send a PR?

@gitspeaks
Copy link
Author

I’m not sure “the original implementation” is the correct phrasing.

read: "If you want to ensure obtaining the original implementation"

Alternatively: "If you want to ensure require returns the original implementation"

I'm fine with either. What do you prefer?

In any case, clarifying the docs SGTM, would you like to send a PR?

Sure. I can give it a try :)

@gitspeaks
Copy link
Author

gitspeaks commented May 14, 2024

However, since core modules are also "imported" I think "obtaining" is better.

@aduh95
Copy link
Contributor

aduh95 commented May 14, 2024

If you use import, there are no differences between using the node: prefix or not, the difference of behavior only happens with require.
I think it would be clearer to phrase it the other way around, if you will, the docs are already used the node: prefix everywhere, so I think it would make more sense to phrase it “if you are using require() to load a built-in module without the node: prefix, you might get a different module if the require cache was tampered in user-land”

@gitspeaks
Copy link
Author

If you use import, there are no differences between using the node: prefix or not, the difference of behavior only happens with require.

Aren't core modules considered CommonJS modules? Isn't the CommonJS cache still accessible from an ES module?

@aduh95
Copy link
Contributor

aduh95 commented May 14, 2024

No they considered… built-in modules. You can certainly access the require cache from ESM, but that has no effect on the matter.

@RedYetiDev
Copy link
Member

IMO this should be converted to a discussion, am I okay to do that?

@gitspeaks
Copy link
Author

I don't understand. Are you saying that the require cache can't be manipulated in an ES module in the same way shown in the code you posted?

@gitspeaks
Copy link
Author

IMO this should be converted to a discussion, am I okay to do that?

@RedYetiDev I have no objection.

@RedYetiDev RedYetiDev converted this issue into discussion #52985 May 14, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
doc Issues and PRs related to the documentations. loaders Issues and PRs related to ES module loaders question Issues that look for answers.
Projects
None yet
Development

No branches or pull requests

3 participants