Repository: nodejs/node
Pull request: fs: runtime deprecate `fs.F_OK`, `fs.R_OK`, `fs.W_OK`, `fs.X_OK`

Follow-up from: https://github.com/nodejs/node/pull/49683 (doc-only deprecation)

Opening this ahead of time to point out that the getters here are non-enumerable.

cc @nodejs/fs

Changed files:
filename: doc/api/deprecations.md
patch: @@ -3577,12 +3577,15 @@ The [`util.toUSVString()`][] API is deprecated. Please use
 
 <!-- YAML
 changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/49686
+    description: Runtime deprecation.
   - version: v20.8.0
     pr-url: https://github.com/nodejs/node/pull/49683
     description: Documentation-only deprecation.
 -->
 
-Type: Documentation-only
+Type: Runtime
 
 `F_OK`, `R_OK`, `W_OK` and `X_OK` getters exposed directly on `node:fs` are
 deprecated. Get them from `fs.constants` or `fs.promises.constants` instead.

filename: lib/fs.js
patch: @@ -87,6 +87,7 @@ const {
 const { toPathIfFileURL } = require('internal/url');
 const {
   customPromisifyArgs: kCustomPromisifyArgsSymbol,
+  deprecate,
   emitExperimentalWarning,
   getLazy,
   kEmptyObject,
@@ -3274,10 +3275,50 @@ defineLazyProperties(
 );
 
 ObjectDefineProperties(fs, {
-  F_OK: { __proto__: null, enumerable: true, value: F_OK || 0 },
-  R_OK: { __proto__: null, enumerable: true, value: R_OK || 0 },
-  W_OK: { __proto__: null, enumerable: true, value: W_OK || 0 },
-  X_OK: { __proto__: null, enumerable: true, value: X_OK || 0 },
+  F_OK: {
+    __proto__: null,
+    enumerable: false,
+    get: deprecate(
+      function get() {
+        return F_OK || 0;
+      },
+      'fs.F_OK is deprecated, use fs.constants.F_OK instead',
+      'DEP0176',
+    ),
+  },
+  R_OK: {
+    __proto__: null,
+    enumerable: false,
+    get: deprecate(
+      function get() {
+        return R_OK || 0;
+      },
+      'fs.R_OK is deprecated, use fs.constants.R_OK instead',
+      'DEP0176',
+    ),
+  },
+  W_OK: {
+    __proto__: null,
+    enumerable: false,
+    get: deprecate(
+      function get() {
+        return W_OK || 0;
+      },
+      'fs.W_OK is deprecated, use fs.constants.W_OK instead',
+      'DEP0176',
+    ),
+  },
+  X_OK: {
+    __proto__: null,
+    enumerable: false,
+    get: deprecate(
+      function get() {
+        return X_OK || 0;
+      },
+      'fs.X_OK is deprecated, use fs.constants.X_OK instead',
+      'DEP0176',
+    ),
+  },
   constants: {
     __proto__: null,
     configurable: false,

filename: test/parallel/test-fs-constants.js
patch: @@ -1,8 +1,27 @@
 'use strict';
-require('../common');
+const { expectWarning } = require('../common');
 const fs = require('fs');
 const assert = require('assert');
 
 // Check if the two constants accepted by chmod() on Windows are defined.
 assert.notStrictEqual(fs.constants.S_IRUSR, undefined);
 assert.notStrictEqual(fs.constants.S_IWUSR, undefined);
+
+// Check for runtime deprecation warning, there should be no setter
+const { F_OK, R_OK, W_OK, X_OK } = fs.constants;
+
+assert.throws(() => { fs.F_OK = 'overwritten'; }, { name: 'TypeError' });
+assert.throws(() => { fs.R_OK = 'overwritten'; }, { name: 'TypeError' });
+assert.throws(() => { fs.W_OK = 'overwritten'; }, { name: 'TypeError' });
+assert.throws(() => { fs.X_OK = 'overwritten'; }, { name: 'TypeError' });
+
+expectWarning(
+  'DeprecationWarning',
+  'fs.F_OK is deprecated, use fs.constants.F_OK instead',
+  'DEP0176'
+);
+
+assert.strictEqual(fs.F_OK, F_OK);
+assert.strictEqual(fs.R_OK, R_OK);
+assert.strictEqual(fs.W_OK, W_OK);
+assert.strictEqual(fs.X_OK, X_OK);