From f9dd8fd9f400c01638ef46cf9e53517417080672 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Mon, 13 Aug 2018 17:53:59 -0400 Subject: [PATCH 1/9] fs: feature detection for recursive mkdir[Sync] This commit adds a non-enumerable and non-writable property `Symbol('recursive')` to fs.mkdir and fs.mkdirSync to allow developers to feature detect How it can be used: ```js Object.getOwnPropertySymbols(fs.mkdir).map((sym) => { return sym.toString(); }).includes('Symbol(recursive)'); // true ``` --- doc/api/fs.md | 20 ++++++++++++++++++++ lib/fs.js | 16 ++++++++++++++++ test/parallel/test-fs-mkdir.js | 16 ++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/doc/api/fs.md b/doc/api/fs.md index 1f71a537e3eb46..9b4c0232c4b15b 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -2086,6 +2086,16 @@ fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => { }); ``` +`fs.mkdir` has non-enumerable non-writable property `Symbol('recursive')` that +can be used to detect if the recursive feature is available + +```js +Object.getOwnPropertySymbols(fs.mkdir).map((sym) => { + return sym.toString(); +}).includes('Symbol(recursive)'); +// true +``` + See also: mkdir(2). ## fs.mkdirSync(path[, options]) @@ -2106,6 +2116,16 @@ changes: Synchronously creates a directory. Returns `undefined`. This is the synchronous version of [`fs.mkdir()`][]. +`fs.mkdirSync` has non-enumerable non-writable property `Symbol('recursive')` +that can be used to detect if the recursive feature is available + +```js +Object.getOwnPropertySymbols(fs.mkdirSync).map((sym) => { + return sym.toString(); +}).includes('Symbol(recursive)'); +// true +``` + See also: mkdir(2). ## fs.mkdtemp(prefix[, options], callback) diff --git a/lib/fs.js b/lib/fs.js index bb8c17bbbb5436..74ce52a5f92159 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -745,6 +745,14 @@ function mkdir(path, options, callback) { validateMode(mode, 'mode', 0o777), recursive, req); } +Object.defineProperties(mkdir, { + [Symbol('recursive')]: { + value: true, + writable: false, + enumerable: false + } +}); + function mkdirSync(path, options) { if (typeof options === 'number' || typeof options === 'string') { options = { mode: options }; @@ -766,6 +774,14 @@ function mkdirSync(path, options) { handleErrorFromBinding(ctx); } +Object.defineProperties(mkdirSync, { + [Symbol('recursive')]: { + value: true, + writable: false, + enumerable: false + } +}); + function readdir(path, options, callback) { callback = makeCallback(typeof options === 'function' ? options : callback); options = getOptions(options, {}); diff --git a/test/parallel/test-fs-mkdir.js b/test/parallel/test-fs-mkdir.js index 024fdced9c44ae..1df74bff9e626f 100644 --- a/test/parallel/test-fs-mkdir.js +++ b/test/parallel/test-fs-mkdir.js @@ -172,6 +172,22 @@ if (common.isMainThread && (common.isLinux || common.isOSX)) { }); } +// mkdirp and mkdirSyncp feature detection +{ + assert.strictEqual(Object.getOwnPropertySymbols(fs.mkdir).map((sym) => { + return sym.toString(); + }).includes('Symbol(recursive)'), true); + assert.strictEqual(Object.getOwnPropertySymbols(fs.mkdirSync).map((sym) => { + return sym.toString(); + }).includes('Symbol(recursive)'), true); + assert.strictEqual(Object.getOwnPropertySymbols(fs.mkdir).map((sym) => { + return sym.toString(); + }).includes('Symbol(fhwdgads)'), false); + assert.strictEqual(Object.getOwnPropertySymbols(fs.mkdirSync).map((sym) => { + return sym.toString(); + }).includes('Symbol(fhwdgads)'), false); +} + // Keep the event loop alive so the async mkdir() requests // have a chance to run (since they don't ref the event loop). process.nextTick(() => {}); From 84c1d4565e146e23ae02f864bb6b73d9ea4785bf Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Mon, 13 Aug 2018 18:24:55 -0400 Subject: [PATCH 2/9] fixup: util.features --- doc/api/fs.md | 16 ++++++---------- doc/api/util.md | 7 +++++++ lib/fs.js | 22 +++++++++------------- lib/util.js | 1 + test/parallel/test-fs-mkdir.js | 17 +++++------------ 5 files changed, 28 insertions(+), 35 deletions(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index 9b4c0232c4b15b..d22cc9eb57ce47 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -2086,13 +2086,11 @@ fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => { }); ``` -`fs.mkdir` has non-enumerable non-writable property `Symbol('recursive')` that -can be used to detect if the recursive feature is available +The `util.features` symbol can be used to feature detect if +recusion is available. ```js -Object.getOwnPropertySymbols(fs.mkdir).map((sym) => { - return sym.toString(); -}).includes('Symbol(recursive)'); +fs.mkdir[util.features].includes('recursive'); // true ``` @@ -2116,13 +2114,11 @@ changes: Synchronously creates a directory. Returns `undefined`. This is the synchronous version of [`fs.mkdir()`][]. -`fs.mkdirSync` has non-enumerable non-writable property `Symbol('recursive')` -that can be used to detect if the recursive feature is available +The `util.features` symbol can be used to feature detect if +recusion is available. ```js -Object.getOwnPropertySymbols(fs.mkdirSync).map((sym) => { - return sym.toString(); -}).includes('Symbol(recursive)'); +fs.mkdirSync[util.features].includes('recursive'); // true ``` diff --git a/doc/api/util.md b/doc/api/util.md index 1c7d2393a143dd..3f125b856f1147 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -179,6 +179,13 @@ The `--throw-deprecation` command line flag and `process.throwDeprecation` property take precedence over `--trace-deprecation` and `process.traceDeprecation`. +### util.features + + +* {symbol} that can be used to do feature detection. + ## util.format(format[, ...args]) * {symbol} that can be used to do feature detection. From 7e4ab5dae5cd8ab90a297c4f1956cba7a24184f8 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Tue, 14 Aug 2018 17:37:22 -0400 Subject: [PATCH 6/9] fixup: move functionality to internal/util --- lib/fs.js | 19 ++++++------------ lib/internal/util.js | 15 ++++++++++++++ lib/util.js | 3 ++- ...est-internal-util-add-feature-detection.js | 20 +++++++++++++++++++ 4 files changed, 43 insertions(+), 14 deletions(-) create mode 100644 test/parallel/test-internal-util-add-feature-detection.js diff --git a/lib/fs.js b/lib/fs.js index fcefc2e560582e..bb2a33b0a65cc9 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -39,8 +39,9 @@ const { O_SYMLINK } = constants; -const { _extend, features } = require('util'); +const { _extend } = require('util'); const pathModule = require('path'); +const { addFeatureDetection } = require('internal/util'); const { isUint8Array } = require('internal/util/types'); const binding = process.binding('fs'); const { Buffer, kMaxLength } = require('buffer'); @@ -745,12 +746,8 @@ function mkdir(path, options, callback) { validateMode(mode, 'mode', 0o777), recursive, req); } -Object.defineProperty(mkdir, features, { - value: { - recursive: true - }, - writable: false, - enumerable: false +addFeatureDetection(mkdir, { + recursive: true }); function mkdirSync(path, options) { @@ -774,12 +771,8 @@ function mkdirSync(path, options) { handleErrorFromBinding(ctx); } -Object.defineProperty(mkdirSync, features, { - value: { - recursive: true - }, - writable: false, - enumerable: false +addFeatureDetection(mkdirSync, { + recursive: true }); function readdir(path, options, callback) { diff --git a/lib/internal/util.js b/lib/internal/util.js index bf8f9c26cf6e9a..ec61c082808c57 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -366,8 +366,20 @@ function isInsideNodeModules() { return false; } +const kCustomFeatureDetectionSymbol = Symbol('node.util.features'); + +function addFeatureDetection(obj, value) { + Object.defineProperty(obj, kCustomFeatureDetectionSymbol, { + value, + writable: false, + enumerable: false, + configurable: true + }); +} + module.exports = { + addFeatureDetection, assertCrypto, cachedResult, convertToValidSignal, @@ -394,6 +406,9 @@ module.exports = { // alternative to using 'inspect' customInspectSymbol: Symbol('util.inspect.custom'), + // Symbol used to do feature detection + featureDetectionSymbol: kCustomFeatureDetectionSymbol, + // Used by the buffer module to capture an internal reference to the // default isEncoding implementation, just in case userland overrides it. kIsEncodingSymbol: Symbol('kIsEncodingSymbol'), diff --git a/lib/util.js b/lib/util.js index db3460f74275de..bfc95a936d7288 100644 --- a/lib/util.js +++ b/lib/util.js @@ -79,6 +79,7 @@ const { customInspectSymbol, deprecate, getSystemErrorName: internalErrorName, + featureDetectionSymbol, isError, promisify, join, @@ -1472,7 +1473,7 @@ module.exports = exports = { callbackify, debuglog, deprecate, - features: Symbol('features'), + features: featureDetectionSymbol, format, formatWithOptions, getSystemErrorName, diff --git a/test/parallel/test-internal-util-add-feature-detection.js b/test/parallel/test-internal-util-add-feature-detection.js new file mode 100644 index 00000000000000..5b59ad1ae75cf0 --- /dev/null +++ b/test/parallel/test-internal-util-add-feature-detection.js @@ -0,0 +1,20 @@ +// Flags: --expose-internals +'use strict'; + +require('../common'); +const assert = require('assert'); +const { addFeatureDetection } = require('internal/util'); +const { features } = require('util'); + +function myFunction() { + return true; +} + +addFeatureDetection(myFunction, { + works: true, +}); + +assert.strictEqual(typeof myFunction, 'function'); +assert.strictEqual(myFunction(), true); +assert.strictEqual(myFunction[features].works, true); +assert.strictEqual(myFunction[features].doesNotWork, undefined); From efc9adff66e99053583bb44698d49844f219aacd Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Tue, 14 Aug 2018 17:47:14 -0400 Subject: [PATCH 7/9] fixup: add fs.promises --- doc/api/fs.md | 10 +++++++++- lib/internal/fs/promises.js | 5 +++++ test/parallel/test-fs-promises.js | 7 +++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index a8e37600611e43..0807c5b398f293 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -2086,7 +2086,7 @@ fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => { }); ``` -The `util.features` symbol can be used to feature detect if +The [`util.features`][] symbol can be used to feature detect if recursion is available. ```js @@ -4028,6 +4028,14 @@ The optional `options` argument can be an integer specifying mode (permission and sticky bits), or an object with a `mode` property and a `recursive` property indicating whether parent folders should be created. +The [`util.features`][] symbol can be used to feature detect if +recursion is available. + +```js +fs.mkdir[util.features].recursive; +// true +``` + ### fsPromises.mkdtemp(prefix[, options])