Closed lamweili closed 2 years ago
I can see both sides of the argument here. On one hand, defensive coding is good. On the other hand, monkey-patching core modules is a terrible practice, and if you do it anyway, but then fail to do it correctly, I find it hard to be sympathetic.
Agreed but sometimes, these monkey patches come from other embedded dependencies (such as the Angular case), which puts users in a difficult situation between the dependency and fs-extra
. The async-listeners
(https://github.com/jprichardson/node-fs-extra/pull/936#issuecomment-1022044794) that monkey-patches the core modules (yes, agreed that it is a terrible practice and still failed to do it correctly) was discovered after 5-levels of dependencies chains that's a little beyond the users' control. 🤯
I'm coming as a contributor of log4js which uses streamroller that uses fs-extra@^10.0.1
.
I'm posting this on behalf of my user @myuniverse8 for his issue https://github.com/log4js-node/log4js-node/issues/1225. He uses log4js
(which implicitly uses fs-extra
) on Angular SSR. Yet, due to Angular's patching, fs.realpath.native
broke and became undefined
, resulting in errors when he attempts to import log4js
(universalify
throws an error when called by fs-extra
's initialisation). We do not not even use fs-extra.realpath.native
.
There's nothing much I can do for him, except to plead my case to both Angular (https://github.com/angular/angular/issues/45546) and to you, @RyanZim.
I believe many out there are affected as well, when they use dependencies that poorly monkey-patches fs
, together with fs-extra
as a direct or indirect dependency.
Some, including from Angular/zone.js/node
and memfs
, are:
Angular/zone.js/node
https://github.com/log4js-node/log4js-node/issues/1225 by @myuniverse8async-listener
https://github.com/jprichardson/node-fs-extra/pull/936#issuecomment-1022044794 by @matanarbelhomebridge
https://github.com/jprichardson/node-fs-extra/issues/926#issuecomment-945014637 by @hepcat72homebridge
https://github.com/jprichardson/node-fs-extra/issues/926#issuecomment-961816850 by @roman-teamsystemsmemfs
https://github.com/streamich/memfs/issues/803 by @Phillip9587nexe
https://github.com/jprichardson/node-fs-extra/issues/909 by @donthijackthegit
And it can be seen immensely difficult to trace to the underlying culprit, up to 5 levels of dependencies for async-listener
. I'm sure there are more to the list from the collateral damage.
In addition, the fs-extra.realpath.native
API can be optional if not used in runtime (pre-#887) so it should not be an initialisation showstopper from #887; it seems like fs-extra
issue when it's not!
fs-extra
's compatibility.Apart from reverting, we can add in warnings to flag out if fs.realpath.native
is undefined
, for people to pay attention to fix their dependencies, and so fs-extra.realpath.native
will not available. Let me know and I'll add a commit for it for this PR.
The pros outweighs the cons I hope that changes your mind. 🙏
https://github.com/jprichardson/node-fs-extra/pull/887 isn't documented in the changelog (should be in 10.0.0).
Yeah, it was just implicitly assumed in the Node version requirement bump.
I do sort of like the idea of a warning flag, but would like to get some input from the other maintainers here. @JPeer264 @manidlou @jprichardson
@RyanZim Sounds great, I shall start on the works for the warning flags in parallel to expedite while waiting for the inputs.
I found some existing codes using console.warn
(refer to end of comment).
https://github.com/jprichardson/node-fs-extra/blob/e0d298d297ce4998b02dd86748f68b302d590bff/lib/copy/copy.js#L24-L28 https://github.com/jprichardson/node-fs-extra/blob/e0d298d297ce4998b02dd86748f68b302d590bff/lib/copy/copy-sync.js#L18-L22
I like the idea about the warning flag as well. On top, we can also add the JSDoc's After some thoughts the deprecated warning does not make sense in this case.@deprecated
(https://usejsdoc.org/tags-deprecated.html) flag, so IDE's, which supports this, display it as deprecated and people can react earlier.
@JPeer264 What are your thoughts on the warning flag implementation?
Should we continue to use console.warn
(referencing existing codes), or should we migrate all to process.emitWarning?
Yes process.emitWarning
sounds also like a great option to add (just my opinion on it). However, a separate PR would be nice if you want to go the extra mile as we squash our PRs. @manidlou @RyanZim @jprichardson any objections?
@JPeer264 I created a separate PR (https://github.com/jprichardson/node-fs-extra/pull/954) to migrate existing console.warn
to process.emitWarning
for existing codes.
For the current issue, the warnings when fs.realpath.native
is undefined
looks like:
I have also added the following if fs.realpath.native = undefined
for some backward compatibility support:
fs-extra.realpath.native
to use u(fs.realpath)
(they have the same method signature) for older NodeJS (warned anyway)fs.realpath
) is working correctlyI hope this looks good to be merged, together with #954.
I like the warning stuff so far; however, there's no reason to do a fallback or Node version checking. Our engines
field already takes care of old Node versions at the package manager level.
Strange, the newly added tests ran successfully when I npm test
locally. 😩
I am going to set it to draft while I try to fix the mocha tests for the CI issues. It might be due to require.cache
. I will set to ready for review once completed.
I like the warning stuff so far; however, there's no reason to do a fallback or Node version checking. Our
engines
field already takes care of old Node versions at the package manager level.
That would require .npmrc
to be configured with engine-strict=true
if I remember correctly. IMHO, it would be more holistic should people somehow skips the engines enforcement at package.json
, so that its clear if its NodeJS versioning issue or from random dependencies altering fs
.
The added fallback mitigates cases where fs-extra.realpath.native
is actually used in code, as it implicitly falls back to u(fs.realpath)
as a safety net so it's more forgiving towards runtime error. Should the behaviour be undesirable (it shouldn't be since it is asserted in the test case), we already have an initialisation warning about the fallback.
Any thoughts?
I hope I have fixed the CI issues resulting from the added tests. I am unable to replicate them on my machines now.
Would need a maintainer's approval to run the workflow to determine if it truly resolves.
npm will still warn about incompatible engines on install, even without engine-strict
.
Fallback is unnecessary; if you actually used fs.realpath.native
in your code, you'd have a problem without fs-extra
; it's not within scope for fs-extra
to fix other people's failed monkey-patches.
@RyanZim After some thoughts, you are right. Good call!👍
It's not within scope to fix others' failed monkey-patch and should not be exported by fs-extra
if not available.
It follows the consistent principle of fs-extra
(such as fs.writev
); if not available/problematic, fs-extra
does not export.
https://github.com/jprichardson/node-fs-extra/blob/e0d298d297ce4998b02dd86748f68b302d590bff/lib/fs/index.js#L102-L107
I shall remove the fallback and amend the warning message.
At the very least, now fs-extra
does not fail on initialisation due to the fault of others' poor monkey-patches on fs
.
@RyanZim @JPeer264 Thanks for bearing with my verbosity. Learnt a great deal from you guys. It should look good now?
Thank you, @RyanZim @JPeer264 for your patience and review. Sounds good to go!
887 causes a few regressions, not because of
fs-extra
, but due to other dependencies who are usingfs-extra
.The general gist is that others' improper monkey-patches of
fs
should not impact the import and initialisation offs-extra
.When
fs.realpath.native
isundefined
caused by the improper handling by other packages, it inevitably affected the import and initialisation offs-extra
as theuniversalify
would not be able to parse this line properly. https://github.com/jprichardson/node-fs-extra/blob/7edcb16a06e041826af3303f961866bf3b243dae/lib/fs/index.js#L57It results in
fs-extra
throwing errors on import, even if thefs-extra.realpath.native
API is not being used during runtime.And the stacktrace shows:
(src: https://github.com/RyanZim/universalify/blob/a853a4aedc63c69fcdc62b77643d75b0d162a098/index.js#L15)
While it is up to the responsibility of the underlying packages to fix their root cause of poor monkey-patches to
fs
, it might be wise forfs-extra
to keep the check forfs.realpath.native
during initialisation so thatfs-extra
does not throw errors on import.To name a few:
To summarise from the 3 listed issues above, it does seem like most dependencies did not factor in
fs
third-level functions. From the NodeJS fs API documentation, most are second-level functions (i.e. fs.xxx). But there are 2 exceptions since NodeJS 9.2.0 that are in third-level (i.e. fs.xxx.yyy), which arefs.realpath.native
andfs.realpathSync.native
. So if any the packages did some wrapping around, which usually naively only handles direct members (second-level), the third-level functions breaks and becomesundefined
.Even in Angular (traced from the 3rd issue above, https://github.com/log4js-node/log4js-node/issues/1225), they, too, did not factor in the 2 special functions. https://github.com/angular/angular/blob/1c11a5715576632a4fb7170202395cf95dfbce09/packages/zone.js/lib/node/fs.ts#L20-L26
I have filed an issue to zone.js/node separately here: https://github.com/angular/angular/issues/45546
For your kind consideration, please.