fix(ext/node): validate fs.watch options.ignore#33574
fix(ext/node): validate fs.watch options.ignore#33574nathanwhitbot wants to merge 12 commits intodenoland:mainfrom
Conversation
Match Node's `internal/validators.js#validateIgnoreOption` /
`validateIgnoreOptionElement`, used by `fs.watch` and
`fs/promises.watch`:
- Element must be string, RegExp, or function — otherwise
`ERR_INVALID_ARG_TYPE`.
- An empty string raises `ERR_INVALID_ARG_VALUE` ("must be a non-empty
string").
- Arrays validate each element; null/undefined are accepted as no-op.
Previously `fs.watch` silently accepted any value (numbers, empty
strings) and only later quietly ignored them.
Enables `parallel/test-fs-watch-ignore-invalid.js`.
nathanwhitbot
left a comment
There was a problem hiding this comment.
1:1 port of Node's validateIgnoreOption + validateIgnoreOptionElement
from lib/internal/validators.js (L580–609). Allowed forms (string,
RegExp, function), the empty-string rejection, and the array-recursion
all match. Locking down fs.watch's contract here is the right move.
Three minor differences vs. upstream — none blocking, flagging for
follow-up:
-
Validation order: in Node,
validateIgnoreOptionruns after
getValidatedPath(filename)(seelib/internal/fs/watchers.js
L306–309). This PR runs it beforegetValidatedPath(filename)in
watch(), so a call with both an invalid path and an invalidignore
surfaces the ignore error first. Tiny user-visible divergence. -
value instanceof RegExpvs Node'sisRegExp(value)(the latter
isObject.prototype.toString.call(...) === '[object RegExp]'and is
realm-agnostic). A RegExp coming from another realm (e.g., across
worker_threads/vm contexts) would pass on Node and throw here. Edge
case, butisRegExpfrominternal/util/typesis already imported
elsewhere in the file. -
hideStackFramesisn't replicated. Errors thrown from
validateIgnoreOptionwill include the validator function in the
stack trace where Node would point at the user'sfs.watchcall.
Minor UX nit; happy to leave as-is unless the test asserts on stack
shape (it doesn't).
LGTM.
|
This fails |
fibibot
left a comment
There was a problem hiding this comment.
The validation logic itself is right — validateIgnoreOption / validateIgnoreOptionElement cleanly mirror Node's lib/internal/validators.js: string-or-RegExp-or-function gets ERR_INVALID_ARG_TYPE, empty string gets ERR_INVALID_ARG_VALUE, array recurses, null/undefined are accepted as no-op. The body's caveat that the actual ignore-matching is still a no-op (separate work) is honest — this PR is just landing the validation contract.
But CI is red on lint across linux/macos/windows debug — prefer-primordials rejects value instanceof RegExp and Array.isArray(value) in the new validators. Inline fix below.
Once lint is green I'll re-confirm.
fibibot
left a comment
There was a problem hiding this comment.
LGTM after 65088778. Both prefer-primordials lint hits are fixed exactly as suggested:
value instanceof RegExp→ObjectPrototypeIsPrototypeOf(RegExpPrototype, value)Array.isArray(value)→ArrayIsArray(value)
RegExpPrototype and ArrayIsArray correctly added to the existing primordials destructure. Validation logic and the upstream-Node mapping (validateIgnoreOption / validateIgnoreOptionElement) is unchanged from the prior review.
CI now green on all the relevant areas — lint debug clean across linux/macos/windows, test node_compat debug clean across all four x86_64/aarch64 platforms (windows-aarch64 still pending but the other 5 + windows-x86_64 finished green), test unit_node debug clean on all 6 platforms. Nothing red, no fs.ts-relevant pending blockers.
Body's caveat that the actual ignore-matching is a no-op (separate work) is still accurate — this just lands the validation contract that test-fs-watch-ignore-invalid.js exercises.
|
|
|
@nathanwhit this needs a merge and conflict resolution |
# Conflicts: # ext/node/polyfills/fs.ts # tests/node_compat/config.jsonc
Summary
fs.watchaccepted any value foroptions.ignoreand silently dropped invalid input. Node'sinternal/validators.js(validateIgnoreOption/validateIgnoreOptionElement, called frominternal/fs/watchers.js) enforces:ERR_INVALID_ARG_TYPE.ERR_INVALID_ARG_VALUE("must be a non-empty string").null/undefinedare accepted as no-op.This patch ports those checks into
ext/node/polyfills/fs.ts#watch. The actual ignore-matching is still a no-op (a separate piece of work) — the test only covers the validation contract.Enables
parallel/test-fs-watch-ignore-invalid.js.Test plan
cargo test --test node_compat -- test-fs-watch-ignore-invalid(was failing on main, passes here)cargo build --bin denocleantools/format.jsandtools/lint.js --jscleantest-fs-watch,test-fs-watch-recursive-add-file,test-fs-watch-close-when-destroyed,test-fs-watch-recursive-add-file-with-url,test-fs-watch-recursive-add-file-to-existing-subfolder.