mirror of
https://github.com/nodejs/node.git
synced 2024-11-24 03:07:54 +01:00
lib: prefer optional chaining
PR-URL: https://github.com/nodejs/node/pull/55045 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
This commit is contained in:
parent
04750afb1e
commit
574f2dd517
@ -313,6 +313,7 @@ export default [
|
||||
'node-core/no-unescaped-regexp-dot': 'error',
|
||||
'node-core/no-duplicate-requires': 'error',
|
||||
'node-core/prefer-proto': 'error',
|
||||
'node-core/prefer-optional-chaining': 'error',
|
||||
},
|
||||
},
|
||||
// #endregion
|
||||
|
@ -128,7 +128,7 @@ function Agent(options) {
|
||||
}
|
||||
|
||||
const requests = this.requests[name];
|
||||
if (requests && requests.length) {
|
||||
if (requests?.length) {
|
||||
const req = requests.shift();
|
||||
const reqAsyncRes = req[kRequestAsyncResource];
|
||||
if (reqAsyncRes) {
|
||||
@ -437,7 +437,7 @@ Agent.prototype.removeSocket = function removeSocket(s, options) {
|
||||
}
|
||||
|
||||
let req;
|
||||
if (this.requests[name] && this.requests[name].length) {
|
||||
if (this.requests[name]?.length) {
|
||||
debug('removeSocket, have a request, make a socket');
|
||||
req = this.requests[name][0];
|
||||
} else {
|
||||
@ -449,7 +449,7 @@ Agent.prototype.removeSocket = function removeSocket(s, options) {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const prop = keys[i];
|
||||
// Check whether this specific origin is already at maxSockets
|
||||
if (this.sockets[prop] && this.sockets[prop].length) break;
|
||||
if (this.sockets[prop]?.length) break;
|
||||
debug('removeSocket, have a request with different origin,' +
|
||||
' make a socket');
|
||||
req = this.requests[prop][0];
|
||||
|
@ -174,7 +174,7 @@ function ClientRequest(input, options, cb) {
|
||||
|
||||
const protocol = options.protocol || defaultAgent.protocol;
|
||||
let expectedProtocol = defaultAgent.protocol;
|
||||
if (this.agent && this.agent.protocol)
|
||||
if (this.agent?.protocol)
|
||||
expectedProtocol = this.agent.protocol;
|
||||
|
||||
if (options.path) {
|
||||
@ -190,7 +190,7 @@ function ClientRequest(input, options, cb) {
|
||||
}
|
||||
|
||||
const defaultPort = options.defaultPort ||
|
||||
(this.agent && this.agent.defaultPort);
|
||||
(this.agent?.defaultPort);
|
||||
|
||||
const optsWithoutSignal = { __proto__: null, ...options };
|
||||
|
||||
@ -553,7 +553,7 @@ function socketOnData(d) {
|
||||
socket.destroy();
|
||||
req.socket._hadError = true;
|
||||
emitErrorEvent(req, ret);
|
||||
} else if (parser.incoming && parser.incoming.upgrade) {
|
||||
} else if (parser.incoming?.upgrade) {
|
||||
// Upgrade (if status code 101) or CONNECT
|
||||
const bytesParsed = ret;
|
||||
const res = parser.incoming;
|
||||
@ -591,7 +591,7 @@ function socketOnData(d) {
|
||||
// Requested Upgrade or used CONNECT method, but have no handler.
|
||||
socket.destroy();
|
||||
}
|
||||
} else if (parser.incoming && parser.incoming.complete &&
|
||||
} else if (parser.incoming?.complete &&
|
||||
// When the status code is informational (100, 102-199),
|
||||
// the server will send a final response after this client
|
||||
// sends a request body, so we must not free the parser.
|
||||
@ -838,7 +838,7 @@ function tickOnSocket(req, socket) {
|
||||
|
||||
if (
|
||||
req.timeout !== undefined ||
|
||||
(req.agent && req.agent.options && req.agent.options.timeout)
|
||||
(req.agent?.options?.timeout)
|
||||
) {
|
||||
listenSocketTimeout(req);
|
||||
}
|
||||
|
@ -85,8 +85,7 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
|
||||
}
|
||||
|
||||
// Parser is also used by http client
|
||||
const ParserIncomingMessage = (socket && socket.server &&
|
||||
socket.server[kIncomingMessage]) ||
|
||||
const ParserIncomingMessage = (socket?.server?.[kIncomingMessage]) ||
|
||||
IncomingMessage;
|
||||
|
||||
const incoming = parser.incoming = new ParserIncomingMessage(socket);
|
||||
|
@ -242,7 +242,7 @@ IncomingMessage.prototype._destroy = function _destroy(err, cb) {
|
||||
|
||||
IncomingMessage.prototype._addHeaderLines = _addHeaderLines;
|
||||
function _addHeaderLines(headers, n) {
|
||||
if (headers && headers.length) {
|
||||
if (headers?.length) {
|
||||
let dest;
|
||||
if (this.complete) {
|
||||
this.rawTrailers = headers;
|
||||
|
@ -423,7 +423,7 @@ OutgoingMessage.prototype._send = function _send(data, encoding, callback, byteL
|
||||
OutgoingMessage.prototype._writeRaw = _writeRaw;
|
||||
function _writeRaw(data, encoding, callback, size) {
|
||||
const conn = this[kSocket];
|
||||
if (conn && conn.destroyed) {
|
||||
if (conn?.destroyed) {
|
||||
// The socket was destroyed. If we're still trying to write to it,
|
||||
// then we haven't gotten the 'close' event yet.
|
||||
return false;
|
||||
@ -789,7 +789,7 @@ OutgoingMessage.prototype.getHeader = function getHeader(name) {
|
||||
return;
|
||||
|
||||
const entry = headers[name.toLowerCase()];
|
||||
return entry && entry[1];
|
||||
return entry?.[1];
|
||||
};
|
||||
|
||||
|
||||
@ -1073,7 +1073,7 @@ OutgoingMessage.prototype.addTrailers = function addTrailers(headers) {
|
||||
};
|
||||
|
||||
function onFinish(outmsg) {
|
||||
if (outmsg && outmsg.socket && outmsg.socket._hadError) return;
|
||||
if (outmsg?.socket?._hadError) return;
|
||||
outmsg.emit('finish');
|
||||
}
|
||||
|
||||
@ -1188,7 +1188,7 @@ OutgoingMessage.prototype._finish = function _finish() {
|
||||
OutgoingMessage.prototype._flush = function _flush() {
|
||||
const socket = this[kSocket];
|
||||
|
||||
if (socket && socket.writable) {
|
||||
if (socket?.writable) {
|
||||
// There might be remaining data in this.output; write it out
|
||||
const ret = this._flushOutput(socket);
|
||||
|
||||
|
@ -738,7 +738,7 @@ function connectionListenerInternal(server, socket) {
|
||||
socket.setEncoding = socketSetEncoding;
|
||||
|
||||
// We only consume the socket if it has never been consumed before.
|
||||
if (socket._handle && socket._handle.isStreamBase &&
|
||||
if (socket._handle?.isStreamBase &&
|
||||
!socket._handle._consumed) {
|
||||
parser._consumed = true;
|
||||
socket._handle._consumed = true;
|
||||
@ -783,7 +783,7 @@ function socketOnDrain(socket, state) {
|
||||
}
|
||||
|
||||
function socketOnTimeout() {
|
||||
const req = this.parser && this.parser.incoming;
|
||||
const req = this.parser?.incoming;
|
||||
const reqTimeout = req && !req.complete && req.emit('timeout', this);
|
||||
const res = this._httpMessage;
|
||||
const resTimeout = res && res.emit('timeout', this);
|
||||
@ -918,7 +918,7 @@ function onParserExecuteCommon(server, socket, parser, state, ret, d) {
|
||||
prepareError(ret, parser, d);
|
||||
debug('parse error', ret);
|
||||
socketOnError.call(socket, ret);
|
||||
} else if (parser.incoming && parser.incoming.upgrade) {
|
||||
} else if (parser.incoming?.upgrade) {
|
||||
// Upgrade or CONNECT
|
||||
const req = parser.incoming;
|
||||
debug('SERVER upgrade or connect', req.method);
|
||||
@ -963,7 +963,7 @@ function onParserExecuteCommon(server, socket, parser, state, ret, d) {
|
||||
|
||||
function clearIncoming(req) {
|
||||
req = req || this;
|
||||
const parser = req.socket && req.socket.parser;
|
||||
const parser = req.socket?.parser;
|
||||
// Reset the .incoming property so that the request object can be gc'ed.
|
||||
if (parser && parser.incoming === req) {
|
||||
if (req.readableEnded) {
|
||||
@ -1180,7 +1180,7 @@ function onSocketResume() {
|
||||
}
|
||||
|
||||
function onSocketPause() {
|
||||
if (this._handle && this._handle.reading) {
|
||||
if (this._handle?.reading) {
|
||||
this._handle.reading = false;
|
||||
this._handle.readStop();
|
||||
}
|
||||
|
@ -489,7 +489,7 @@ function initRead(tlsSocket, socket) {
|
||||
return;
|
||||
|
||||
// Socket already has some buffered data - emulate receiving it
|
||||
if (socket && socket.readableLength) {
|
||||
if (socket?.readableLength) {
|
||||
let buf;
|
||||
while ((buf = socket.read()) !== null)
|
||||
tlsSocket._handle.receive(buf);
|
||||
@ -1683,7 +1683,7 @@ function onConnectSecure() {
|
||||
if (!verifyError && !this.isSessionReused()) {
|
||||
const hostname = options.servername ||
|
||||
options.host ||
|
||||
(options.socket && options.socket._host) ||
|
||||
(options.socket?._host) ||
|
||||
'localhost';
|
||||
const cert = this.getPeerCertificate(true);
|
||||
verifyError = options.checkServerIdentity(hostname, cert);
|
||||
|
@ -446,7 +446,7 @@ function expectedException(actual, expected, message, fn) {
|
||||
message = 'The error is expected to be an instance of ' +
|
||||
`"${expected.name}". Received `;
|
||||
if (isError(actual)) {
|
||||
const name = (actual.constructor && actual.constructor.name) ||
|
||||
const name = (actual.constructor?.name) ||
|
||||
actual.name;
|
||||
if (expected.name === name) {
|
||||
message += 'an error with identical name but a different prototype.';
|
||||
@ -569,7 +569,7 @@ function expectsError(stackStartFn, actual, error, message) {
|
||||
|
||||
if (actual === NO_EXCEPTION_SENTINEL) {
|
||||
let details = '';
|
||||
if (error && error.name) {
|
||||
if (error?.name) {
|
||||
details += ` (${error.name})`;
|
||||
}
|
||||
details += message ? `: ${message}` : '.';
|
||||
@ -627,7 +627,7 @@ function expectsNoError(stackStartFn, actual, error, message) {
|
||||
expected: error,
|
||||
operator: stackStartFn.name,
|
||||
message: `Got unwanted ${fnType}${details}\n` +
|
||||
`Actual message: "${actual && actual.message}"`,
|
||||
`Actual message: "${actual?.message}"`,
|
||||
stackStartFn,
|
||||
});
|
||||
}
|
||||
|
@ -392,8 +392,7 @@ function execFile(file, args, options, callback) {
|
||||
let stderr;
|
||||
if (encoding ||
|
||||
(
|
||||
child.stdout &&
|
||||
child.stdout.readableEncoding
|
||||
child.stdout?.readableEncoding
|
||||
)) {
|
||||
stdout = ArrayPrototypeJoin(_stdout, '');
|
||||
} else {
|
||||
@ -401,8 +400,7 @@ function execFile(file, args, options, callback) {
|
||||
}
|
||||
if (encoding ||
|
||||
(
|
||||
child.stderr &&
|
||||
child.stderr.readableEncoding
|
||||
child.stderr?.readableEncoding
|
||||
)) {
|
||||
stderr = ArrayPrototypeJoin(_stderr, '');
|
||||
} else {
|
||||
@ -855,7 +853,7 @@ function spawnSync(file, args, options) {
|
||||
|
||||
// We may want to pass data in on any given fd, ensure it is a valid buffer
|
||||
for (let i = 0; i < options.stdio.length; i++) {
|
||||
const input = options.stdio[i] && options.stdio[i].input;
|
||||
const input = options.stdio[i]?.input;
|
||||
if (input != null) {
|
||||
const pipe = options.stdio[i] = { ...options.stdio[i] };
|
||||
if (isArrayBufferView(input)) {
|
||||
|
@ -128,8 +128,8 @@ function Socket(type, listener) {
|
||||
bindState: BIND_STATE_UNBOUND,
|
||||
connectState: CONNECT_STATE_DISCONNECTED,
|
||||
queue: undefined,
|
||||
reuseAddr: options && options.reuseAddr, // Use UV_UDP_REUSEADDR if true.
|
||||
ipv6Only: options && options.ipv6Only,
|
||||
reuseAddr: options?.reuseAddr, // Use UV_UDP_REUSEADDR if true.
|
||||
ipv6Only: options?.ipv6Only,
|
||||
recvBufferSize,
|
||||
sendBufferSize,
|
||||
};
|
||||
|
@ -251,7 +251,7 @@ function getStdin() {
|
||||
// `stdin` starts out life in a paused state, but node doesn't
|
||||
// know yet. Explicitly to readStop() it to put it in the
|
||||
// not-reading state.
|
||||
if (stdin._handle && stdin._handle.readStop) {
|
||||
if (stdin._handle?.readStop) {
|
||||
stdin._handle.reading = false;
|
||||
stdin._readableState.reading = false;
|
||||
stdin._handle.readStop();
|
||||
|
@ -854,7 +854,7 @@ function setupChannel(target, channel, serializationMode) {
|
||||
if (handle) {
|
||||
if (!this._handleQueue)
|
||||
this._handleQueue = [];
|
||||
if (obj && obj.postSend)
|
||||
if (obj?.postSend)
|
||||
obj.postSend(message, handle, options, callback, target);
|
||||
}
|
||||
|
||||
@ -870,7 +870,7 @@ function setupChannel(target, channel, serializationMode) {
|
||||
}
|
||||
} else {
|
||||
// Cleanup handle on error
|
||||
if (obj && obj.postSend)
|
||||
if (obj?.postSend)
|
||||
obj.postSend(message, handle, options, callback);
|
||||
|
||||
if (!options.swallowErrors) {
|
||||
@ -1115,8 +1115,8 @@ function spawnSync(options) {
|
||||
}
|
||||
}
|
||||
|
||||
result.stdout = result.output && result.output[1];
|
||||
result.stderr = result.output && result.output[2];
|
||||
result.stdout = result.output?.[1];
|
||||
result.stderr = result.output?.[2];
|
||||
|
||||
if (result.error) {
|
||||
result.error = new ErrnoException(result.error, 'spawnSync ' + options.file);
|
||||
|
@ -122,7 +122,7 @@ cluster._getServer = function(obj, options, cb) {
|
||||
cluster.worker.state = 'listening';
|
||||
const address = obj.address();
|
||||
message.act = 'listening';
|
||||
message.port = (address && address.port) || options.port;
|
||||
message.port = (address?.port) || options.port;
|
||||
send(message);
|
||||
});
|
||||
};
|
||||
|
@ -70,7 +70,7 @@ function normalizeHashName(name, context = kHashContextNode) {
|
||||
if (typeof name !== 'string')
|
||||
return name;
|
||||
name = StringPrototypeToLowerCase(name);
|
||||
const alias = kHashNames[name] && kHashNames[name][context];
|
||||
const alias = kHashNames[name]?.[context];
|
||||
return alias || name;
|
||||
}
|
||||
|
||||
|
@ -694,7 +694,7 @@ function createRepl(inspector) {
|
||||
|
||||
function handleBreakpointResolved({ breakpointId, location }) {
|
||||
const script = knownScripts[location.scriptId];
|
||||
const scriptUrl = script && script.url;
|
||||
const scriptUrl = script?.url;
|
||||
if (scriptUrl) {
|
||||
ObjectAssign(location, { scriptUrl });
|
||||
}
|
||||
@ -733,7 +733,7 @@ function createRepl(inspector) {
|
||||
function setBreakpoint(script, line, condition, silent) {
|
||||
function registerBreakpoint({ breakpointId, actualLocation }) {
|
||||
handleBreakpointResolved({ breakpointId, location: actualLocation });
|
||||
if (actualLocation && actualLocation.scriptId) {
|
||||
if (actualLocation?.scriptId) {
|
||||
if (!silent) return getSourceSnippet(actualLocation, 5);
|
||||
} else {
|
||||
print(`Warning: script '${script}' was not loaded yet.`);
|
||||
|
@ -67,7 +67,7 @@ function resolver(bindingName) {
|
||||
req.callback = callback;
|
||||
req.hostname = name;
|
||||
req.oncomplete = onresolve;
|
||||
req.ttl = !!(options && options.ttl);
|
||||
req.ttl = !!(options?.ttl);
|
||||
const err = this._handle[bindingName](req, name);
|
||||
if (err) throw new DNSException(err, bindingName, name);
|
||||
if (hasObserver('dns')) {
|
||||
|
@ -336,7 +336,7 @@ function resolver(bindingName) {
|
||||
function query(name, options) {
|
||||
validateString(name, 'name');
|
||||
|
||||
const ttl = !!(options && options.ttl);
|
||||
const ttl = !!(options?.ttl);
|
||||
return createResolverPromise(this, bindingName, name, ttl);
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ function GetConstructors(object) {
|
||||
current !== null;
|
||||
current = ObjectGetPrototypeOf(current)) {
|
||||
const desc = ObjectGetOwnPropertyDescriptor(current, 'constructor');
|
||||
if (desc && desc.value) {
|
||||
if (desc?.value) {
|
||||
ObjectDefineProperty(constructors, constructors.length, {
|
||||
__proto__: null,
|
||||
value: desc.value, enumerable: true,
|
||||
@ -98,7 +98,7 @@ function GetConstructors(object) {
|
||||
|
||||
function GetName(object) {
|
||||
const desc = ObjectGetOwnPropertyDescriptor(object, 'name');
|
||||
return desc && desc.value;
|
||||
return desc?.value;
|
||||
}
|
||||
|
||||
let internalUtilInspect;
|
||||
|
@ -393,7 +393,7 @@ class ModuleLoader {
|
||||
if (cjsModule) {
|
||||
assert(finalFormat === 'commonjs-sync');
|
||||
// Check if the ESM initiating import CJS is being required by the same CJS module.
|
||||
if (cjsModule && cjsModule[kIsExecuting]) {
|
||||
if (cjsModule?.[kIsExecuting]) {
|
||||
const parentFilename = urlToFilename(parentURL);
|
||||
let message = `Cannot import CommonJS Module ${specifier} in a cycle.`;
|
||||
if (parentFilename) {
|
||||
|
@ -375,7 +375,7 @@ translators.set('json', function jsonStrategy(url, source) {
|
||||
modulePath = isWindows ?
|
||||
StringPrototypeReplaceAll(pathname, '/', '\\') : pathname;
|
||||
module = CJSModule._cache[modulePath];
|
||||
if (module && module.loaded) {
|
||||
if (module?.loaded) {
|
||||
const exports = module.exports;
|
||||
return new ModuleWrap(url, undefined, ['default'], function() {
|
||||
this.setExport('default', exports);
|
||||
@ -389,7 +389,7 @@ translators.set('json', function jsonStrategy(url, source) {
|
||||
// export, we have to check again if the module already exists or not.
|
||||
// TODO: remove CJS loader from here as well.
|
||||
module = CJSModule._cache[modulePath];
|
||||
if (module && module.loaded) {
|
||||
if (module?.loaded) {
|
||||
const exports = module.exports;
|
||||
return new ModuleWrap(url, undefined, ['default'], function() {
|
||||
this.setExport('default', exports);
|
||||
|
@ -17,24 +17,15 @@ let embedderOptions;
|
||||
// complete so that we don't accidentally include runtime-dependent
|
||||
// states into a runtime-independent snapshot.
|
||||
function getCLIOptionsFromBinding() {
|
||||
if (!optionsDict) {
|
||||
optionsDict = getCLIOptionsValues();
|
||||
}
|
||||
return optionsDict;
|
||||
return optionsDict ??= getCLIOptionsValues();
|
||||
}
|
||||
|
||||
function getCLIOptionsInfoFromBinding() {
|
||||
if (!cliInfo) {
|
||||
cliInfo = getCLIOptionsInfo();
|
||||
}
|
||||
return cliInfo;
|
||||
return cliInfo ??= getCLIOptionsInfo();
|
||||
}
|
||||
|
||||
function getEmbedderOptions() {
|
||||
if (!embedderOptions) {
|
||||
embedderOptions = getEmbedderOptionsFromBinding();
|
||||
}
|
||||
return embedderOptions;
|
||||
return embedderOptions ??= getEmbedderOptionsFromBinding();
|
||||
}
|
||||
|
||||
function refreshOptions() {
|
||||
@ -42,8 +33,7 @@ function refreshOptions() {
|
||||
}
|
||||
|
||||
function getOptionValue(optionName) {
|
||||
const optionsDict = getCLIOptionsFromBinding();
|
||||
return optionsDict[optionName];
|
||||
return getCLIOptionsFromBinding()[optionName];
|
||||
}
|
||||
|
||||
function getAllowUnauthorized() {
|
||||
|
@ -261,7 +261,7 @@ function InterfaceConstructor(input, output, completer, terminal) {
|
||||
|
||||
function onkeypress(s, key) {
|
||||
self[kTtyWrite](s, key);
|
||||
if (key && key.sequence) {
|
||||
if (key?.sequence) {
|
||||
// If the key.sequence is half of a surrogate pair
|
||||
// (>= 0xd800 and <= 0xdfff), refresh the line so
|
||||
// the character is displayed appropriately.
|
||||
@ -345,7 +345,7 @@ class Interface extends InterfaceConstructor {
|
||||
super(input, output, completer, terminal);
|
||||
}
|
||||
get columns() {
|
||||
if (this.output && this.output.columns) return this.output.columns;
|
||||
if (this.output?.columns) return this.output.columns;
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
|
@ -269,7 +269,7 @@ class SourceMap {
|
||||
ArrayPrototypePush(sources, url);
|
||||
this.#sources[url] = true;
|
||||
|
||||
if (map.sourcesContent && map.sourcesContent[i])
|
||||
if (map.sourcesContent?.[i])
|
||||
this.#sourceContentByURL[url] = map.sourcesContent[i];
|
||||
}
|
||||
|
||||
|
@ -26,10 +26,10 @@ function from(Readable, iterable, opts) {
|
||||
}
|
||||
|
||||
let isAsync;
|
||||
if (iterable && iterable[SymbolAsyncIterator]) {
|
||||
if (iterable?.[SymbolAsyncIterator]) {
|
||||
isAsync = true;
|
||||
iterator = iterable[SymbolAsyncIterator]();
|
||||
} else if (iterable && iterable[SymbolIterator]) {
|
||||
} else if (iterable?.[SymbolIterator]) {
|
||||
isAsync = false;
|
||||
iterator = iterable[SymbolIterator]();
|
||||
} else {
|
||||
|
@ -453,7 +453,7 @@ function pipe(src, dst, finish, finishOnlyHandleError, { end }) {
|
||||
if (
|
||||
err &&
|
||||
err.code === 'ERR_STREAM_PREMATURE_CLOSE' &&
|
||||
(rState && rState.ended && !rState.errored && !rState.errorEmitted)
|
||||
(rState?.ended && !rState.errored && !rState.errorEmitted)
|
||||
) {
|
||||
// Some readable streams will emit 'close' before 'end'. However, since
|
||||
// this is on the readable side 'end' should still be emitted if the
|
||||
|
@ -266,10 +266,10 @@ function ReadableState(options, stream, isDuplex) {
|
||||
|
||||
// Object stream flag. Used to make read(n) ignore n and to
|
||||
// make all the buffer merging and length checks go away.
|
||||
if (options && options.objectMode)
|
||||
if (options?.objectMode)
|
||||
this[kState] |= kObjectMode;
|
||||
|
||||
if (isDuplex && options && options.readableObjectMode)
|
||||
if (isDuplex && options?.readableObjectMode)
|
||||
this[kState] |= kObjectMode;
|
||||
|
||||
// The point at which it stops calling _read() to fill the buffer
|
||||
@ -305,7 +305,7 @@ function ReadableState(options, stream, isDuplex) {
|
||||
// type: null | Writable | Set<Writable>.
|
||||
this.awaitDrainWriters = null;
|
||||
|
||||
if (options && options.encoding) {
|
||||
if (options?.encoding) {
|
||||
this.decoder = new StringDecoder(options.encoding);
|
||||
this.encoding = options.encoding;
|
||||
}
|
||||
@ -791,7 +791,7 @@ function onEofChunk(stream, state) {
|
||||
const decoder = (state[kState] & kDecoder) !== 0 ? state[kDecoderValue] : null;
|
||||
if (decoder) {
|
||||
const chunk = decoder.end();
|
||||
if (chunk && chunk.length) {
|
||||
if (chunk?.length) {
|
||||
state.buffer.push(chunk);
|
||||
state.length += (state[kState] & kObjectMode) !== 0 ? 1 : chunk.length;
|
||||
}
|
||||
@ -1461,7 +1461,7 @@ ObjectDefineProperties(Readable.prototype, {
|
||||
__proto__: null,
|
||||
enumerable: false,
|
||||
get: function() {
|
||||
return this._readableState && this._readableState.buffer;
|
||||
return this._readableState?.buffer;
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -290,8 +290,7 @@ function willEmitClose(stream) {
|
||||
const state = wState || rState;
|
||||
|
||||
return (!state && isServerResponse(stream)) || !!(
|
||||
state &&
|
||||
state.autoDestroy &&
|
||||
state?.autoDestroy &&
|
||||
state.emitClose &&
|
||||
state.closed === false
|
||||
);
|
||||
|
@ -306,10 +306,10 @@ function WritableState(options, stream, isDuplex) {
|
||||
// instead of a V8 slot per field.
|
||||
this[kState] = kSync | kConstructed | kEmitClose | kAutoDestroy;
|
||||
|
||||
if (options && options.objectMode)
|
||||
if (options?.objectMode)
|
||||
this[kState] |= kObjectMode;
|
||||
|
||||
if (isDuplex && options && options.writableObjectMode)
|
||||
if (isDuplex && options?.writableObjectMode)
|
||||
this[kState] |= kObjectMode;
|
||||
|
||||
// The point at which write() starts returning false
|
||||
@ -1068,7 +1068,7 @@ ObjectDefineProperties(Writable.prototype, {
|
||||
__proto__: null,
|
||||
get() {
|
||||
const state = this._writableState;
|
||||
return state && state.highWaterMark;
|
||||
return state?.highWaterMark;
|
||||
},
|
||||
},
|
||||
|
||||
@ -1084,7 +1084,7 @@ ObjectDefineProperties(Writable.prototype, {
|
||||
__proto__: null,
|
||||
get() {
|
||||
const state = this._writableState;
|
||||
return state && state.length;
|
||||
return state?.length;
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -1284,7 +1284,7 @@ function improveStack(stack, constructor, name, tag) {
|
||||
if (constructor === null) {
|
||||
const start = RegExpPrototypeExec(/^([A-Z][a-z_ A-Z0-9[\]()-]+)(?::|\n {4}at)/, stack) ||
|
||||
RegExpPrototypeExec(/^([a-z_A-Z0-9-]*Error)$/, stack);
|
||||
fallback = (start && start[1]) || '';
|
||||
fallback = (start?.[1]) || '';
|
||||
len = fallback.length;
|
||||
fallback = fallback || 'Error';
|
||||
}
|
||||
|
@ -747,8 +747,7 @@ Socket.prototype.resetAndDestroy = function() {
|
||||
};
|
||||
|
||||
Socket.prototype.pause = function() {
|
||||
if (this[kBuffer] && !this.connecting && this._handle &&
|
||||
this._handle.reading) {
|
||||
if (this[kBuffer] && !this.connecting && this._handle?.reading) {
|
||||
this._handle.reading = false;
|
||||
if (!this.destroyed) {
|
||||
const err = this._handle.readStop();
|
||||
@ -1218,7 +1217,7 @@ Socket.prototype.connect = function(...args) {
|
||||
}
|
||||
|
||||
// If the parent is already connecting, do not attempt to connect again
|
||||
if (this._parent && this._parent.connecting) {
|
||||
if (this._parent?.connecting) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -2184,7 +2183,7 @@ ObjectDefineProperty(Server.prototype, 'listening', {
|
||||
});
|
||||
|
||||
Server.prototype.address = function() {
|
||||
if (this._handle && this._handle.getsockname) {
|
||||
if (this._handle?.getsockname) {
|
||||
const out = {};
|
||||
const err = this._handle.getsockname(out);
|
||||
if (err) {
|
||||
|
@ -163,7 +163,7 @@ Interface.prototype.question[promisify.custom] = function question(query, option
|
||||
options = kEmptyObject;
|
||||
}
|
||||
|
||||
if (options.signal && options.signal.aborted) {
|
||||
if (options.signal?.aborted) {
|
||||
return PromiseReject(
|
||||
new AbortError(undefined, { cause: options.signal.reason }));
|
||||
}
|
||||
|
@ -908,8 +908,8 @@ function REPLServer(prompt,
|
||||
StringPrototypeCharAt(trimmedCmd, 1) !== '.' &&
|
||||
NumberIsNaN(NumberParseFloat(trimmedCmd))) {
|
||||
const matches = RegExpPrototypeExec(/^\.([^\s]+)\s*(.*)$/, trimmedCmd);
|
||||
const keyword = matches && matches[1];
|
||||
const rest = matches && matches[2];
|
||||
const keyword = matches?.[1];
|
||||
const rest = matches?.[2];
|
||||
if (ReflectApply(_parseREPLKeyword, self, [keyword, rest]) === true) {
|
||||
return;
|
||||
}
|
||||
@ -1253,7 +1253,7 @@ function filteredOwnPropertyNames(obj) {
|
||||
let isObjectPrototype = false;
|
||||
if (ObjectGetPrototypeOf(obj) === null) {
|
||||
const ctorDescriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor');
|
||||
if (ctorDescriptor && ctorDescriptor.value) {
|
||||
if (ctorDescriptor?.value) {
|
||||
const ctorProto = ObjectGetPrototypeOf(ctorDescriptor.value);
|
||||
isObjectPrototype = ctorProto && ObjectGetPrototypeOf(ctorProto) === obj;
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ ObjectDefineProperty(setTimeout, customPromisify, {
|
||||
* @returns {void}
|
||||
*/
|
||||
function clearTimeout(timer) {
|
||||
if (timer && timer._onTimeout) {
|
||||
if (timer?._onTimeout) {
|
||||
timer._onTimeout = null;
|
||||
unenroll(timer);
|
||||
return;
|
||||
|
@ -256,7 +256,7 @@ function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) {
|
||||
this._defaultFlushFlag = flush;
|
||||
this._finishFlushFlag = finishFlush;
|
||||
this._defaultFullFlushFlag = fullFlush;
|
||||
this._info = opts && opts.info;
|
||||
this._info = opts?.info;
|
||||
this._maxOutputLength = maxOutputLength;
|
||||
}
|
||||
ObjectSetPrototypeOf(ZlibBase.prototype, Transform.prototype);
|
||||
|
@ -192,28 +192,28 @@ class ActivityCollector {
|
||||
_before(uid) {
|
||||
const h = this._getActivity(uid, 'before');
|
||||
this._stamp(h, 'before');
|
||||
this._maybeLog(uid, h && h.type, 'before');
|
||||
this._maybeLog(uid, h?.type, 'before');
|
||||
this.onbefore(uid);
|
||||
}
|
||||
|
||||
_after(uid) {
|
||||
const h = this._getActivity(uid, 'after');
|
||||
this._stamp(h, 'after');
|
||||
this._maybeLog(uid, h && h.type, 'after');
|
||||
this._maybeLog(uid, h?.type, 'after');
|
||||
this.onafter(uid);
|
||||
}
|
||||
|
||||
_destroy(uid) {
|
||||
const h = this._getActivity(uid, 'destroy');
|
||||
this._stamp(h, 'destroy');
|
||||
this._maybeLog(uid, h && h.type, 'destroy');
|
||||
this._maybeLog(uid, h?.type, 'destroy');
|
||||
this.ondestroy(uid);
|
||||
}
|
||||
|
||||
_promiseResolve(uid) {
|
||||
const h = this._getActivity(uid, 'promiseResolve');
|
||||
this._stamp(h, 'promiseResolve');
|
||||
this._maybeLog(uid, h && h.type, 'promiseResolve');
|
||||
this._maybeLog(uid, h?.type, 'promiseResolve');
|
||||
this.onpromiseResolve(uid);
|
||||
}
|
||||
|
||||
|
@ -196,11 +196,11 @@ function writeDNSPacket(parsed) {
|
||||
|
||||
buffers.push(new Uint16Array([
|
||||
parsed.id,
|
||||
parsed.flags === undefined ? kStandardResponseFlags : parsed.flags,
|
||||
parsed.questions && parsed.questions.length,
|
||||
parsed.answers && parsed.answers.length,
|
||||
parsed.authorityAnswers && parsed.authorityAnswers.length,
|
||||
parsed.additionalRecords && parsed.additionalRecords.length,
|
||||
parsed.flags ?? kStandardResponseFlags,
|
||||
parsed.questions?.length,
|
||||
parsed.answers?.length,
|
||||
parsed.authorityAnswers?.length,
|
||||
parsed.additionalRecords?.length,
|
||||
]));
|
||||
|
||||
for (const q of parsed.questions) {
|
||||
|
@ -50,8 +50,7 @@ if (cluster.isWorker) {
|
||||
checks.setupEvent = true;
|
||||
|
||||
settings = cluster.settings;
|
||||
if (settings &&
|
||||
settings.args && settings.args[0] === 'custom argument' &&
|
||||
if (settings?.args && settings.args[0] === 'custom argument' &&
|
||||
settings.silent === true &&
|
||||
settings.exec === process.argv[1]) {
|
||||
checks.settingsObject = true;
|
||||
|
@ -124,7 +124,7 @@ function checkResults(expected_results, results) {
|
||||
const expected = expected_results[k];
|
||||
|
||||
assert.strictEqual(
|
||||
actual, expected && expected.length ? expected[0] : expected,
|
||||
actual, expected?.length ? expected[0] : expected,
|
||||
`${expected[1] || ''} [expected: ${expected[0]} / actual: ${actual}]`);
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ function checkResults(expected_results, results) {
|
||||
const expected = expected_results[k];
|
||||
|
||||
assert.strictEqual(
|
||||
actual, expected && expected.length ? expected[0] : expected,
|
||||
actual, expected?.length ? expected[0] : expected,
|
||||
`${expected[1] || ''} [expected: ${expected[0]} / actual: ${actual}]`);
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ function createTestCmdLine(options) {
|
||||
|
||||
testCmd += `"${process.argv[0]}"`;
|
||||
|
||||
if (options && options.withAbortOnUncaughtException) {
|
||||
if (options?.withAbortOnUncaughtException) {
|
||||
testCmd += ' --abort-on-uncaught-exception';
|
||||
}
|
||||
|
||||
|
34
test/parallel/test-eslint-prefer-optional-chaining.js
Normal file
34
test/parallel/test-eslint-prefer-optional-chaining.js
Normal file
@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if ((!common.hasCrypto) || (!common.hasIntl)) {
|
||||
common.skip('ESLint tests require crypto and Intl');
|
||||
}
|
||||
|
||||
common.skipIfEslintMissing();
|
||||
|
||||
const RuleTester = require('../../tools/eslint/node_modules/eslint').RuleTester;
|
||||
const rule = require('../../tools/eslint-rules/prefer-optional-chaining');
|
||||
|
||||
new RuleTester().run('prefer-optional-chaining', rule, {
|
||||
valid: [
|
||||
{
|
||||
code: 'hello?.world',
|
||||
options: []
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: 'hello && hello.world',
|
||||
options: [],
|
||||
errors: [{ message: 'Prefer optional chaining.' }],
|
||||
output: 'hello?.world'
|
||||
},
|
||||
{
|
||||
code: 'hello && hello.world && hello.world.foobar',
|
||||
options: [],
|
||||
errors: [{ message: 'Prefer optional chaining.' }],
|
||||
output: 'hello?.world?.foobar'
|
||||
},
|
||||
]
|
||||
});
|
@ -15,7 +15,7 @@ function testFirstInPath(moduleName, isLocalModule) {
|
||||
assertFunction(paths[0], '.');
|
||||
|
||||
paths = _module._resolveLookupPaths(moduleName, null);
|
||||
assertFunction(paths && paths[0], '.');
|
||||
assertFunction(paths?.[0], '.');
|
||||
}
|
||||
|
||||
testFirstInPath('./lodash', true);
|
||||
|
@ -44,7 +44,7 @@ class FakeInput extends EventEmitter {
|
||||
function isWarned(emitter) {
|
||||
for (const name in emitter) {
|
||||
const listeners = emitter[name];
|
||||
if (listeners && listeners.warned) return true;
|
||||
if (listeners?.warned) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class FakeInput extends EventEmitter {
|
||||
function isWarned(emitter) {
|
||||
for (const name in emitter) {
|
||||
const listeners = emitter[name];
|
||||
if (listeners && listeners.warned) return true;
|
||||
if (listeners?.warned) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -45,11 +45,11 @@ r.on('readable', function() {
|
||||
do {
|
||||
console.error(` > read(${READSIZE})`);
|
||||
ret = r.read(READSIZE);
|
||||
console.error(` < ${ret && ret.length} (${rs.length} remain)`);
|
||||
console.error(` < ${ret?.length} (${rs.length} remain)`);
|
||||
} while (ret && ret.length === READSIZE);
|
||||
|
||||
console.error('<< after read()',
|
||||
ret && ret.length,
|
||||
ret?.length,
|
||||
rs.needReadable,
|
||||
rs.length);
|
||||
});
|
||||
|
@ -331,8 +331,8 @@ const tests = [
|
||||
|
||||
tests.forEach(function(test, i) {
|
||||
const err = tls.checkServerIdentity(test.host, test.cert);
|
||||
assert.strictEqual(err && err.reason,
|
||||
assert.strictEqual(err?.reason,
|
||||
test.error,
|
||||
`Test# ${i} failed: ${util.inspect(test)} \n` +
|
||||
`${test.error} != ${(err && err.reason)}`);
|
||||
`${test.error} != ${(err?.reason)}`);
|
||||
});
|
||||
|
@ -38,7 +38,7 @@ async function test() {
|
||||
const events = [];
|
||||
let tracingComplete = false;
|
||||
session.on('NodeTracing.dataCollected', (n) => {
|
||||
assert.ok(n && n.params && n.params.value);
|
||||
assert.ok(n?.params?.value);
|
||||
events.push(...n.params.value); // append the events.
|
||||
});
|
||||
session.on('NodeTracing.tracingComplete', () => tracingComplete = true);
|
||||
|
@ -15,7 +15,7 @@ export function replaceLinks({ filename, linksMapper }) {
|
||||
}
|
||||
});
|
||||
visit(tree, 'definition', (node) => {
|
||||
const htmlUrl = fileHtmlUrls && fileHtmlUrls[node.identifier];
|
||||
const htmlUrl = fileHtmlUrls?.[node.identifier];
|
||||
|
||||
if (htmlUrl && typeof htmlUrl === 'string') {
|
||||
node.url = htmlUrl;
|
||||
|
@ -93,8 +93,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
function checkLiteral(node) {
|
||||
const isTemplate = (node.type === 'TemplateLiteral' && node.quasis &&
|
||||
node.quasis.length);
|
||||
const isTemplate = (node.type === 'TemplateLiteral' && node.quasis?.length);
|
||||
if (inRegExp &&
|
||||
(isTemplate || (typeof node.value === 'string' && node.value.length))) {
|
||||
let p = node.parent;
|
||||
@ -108,7 +107,7 @@ module.exports = {
|
||||
const quasis = node.quasis;
|
||||
for (let i = 0; i < quasis.length; ++i) {
|
||||
const el = quasis[i];
|
||||
if (el.type === 'TemplateElement' && el.value && el.value.cooked)
|
||||
if (el.type === 'TemplateElement' && el.value?.cooked)
|
||||
regexpBuffer.push([el, el.value.cooked]);
|
||||
}
|
||||
} else {
|
||||
|
93
tools/eslint-rules/prefer-optional-chaining.js
Normal file
93
tools/eslint-rules/prefer-optional-chaining.js
Normal file
@ -0,0 +1,93 @@
|
||||
'use strict';
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Prefer optional chaining',
|
||||
category: 'suggestion',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [],
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.getSourceCode();
|
||||
|
||||
// Helper function: Checks if two nodes have identical tokens
|
||||
function equalTokens(left, right) {
|
||||
const leftTokens = sourceCode.getTokens(left);
|
||||
const rightTokens = sourceCode.getTokens(right);
|
||||
return (
|
||||
leftTokens.length === rightTokens.length &&
|
||||
leftTokens.every((tokenL, i) => tokenL.type === rightTokens[i].type && tokenL.value === rightTokens[i].value)
|
||||
);
|
||||
}
|
||||
|
||||
// Check if a sequence of two nodes forms a valid member expression chain
|
||||
function isValidMemberExpressionPair(left, right) {
|
||||
return (
|
||||
right.type === 'MemberExpression' &&
|
||||
equalTokens(left, right.object)
|
||||
);
|
||||
}
|
||||
|
||||
// Generate the optional chaining expression
|
||||
function generateOptionalChaining(ops, first, last) {
|
||||
return ops.slice(first, last + 1).reduce((chain, node, i) => {
|
||||
const property = node.computed ?
|
||||
`[${sourceCode.getText(node.property)}]` :
|
||||
sourceCode.getText(node.property);
|
||||
return i === 0 ? sourceCode.getText(node) : `${chain}?.${property}`;
|
||||
}, '');
|
||||
}
|
||||
|
||||
return {
|
||||
'LogicalExpression[operator=&&]:exit'(node) {
|
||||
// Early return if part of a larger `&&` chain
|
||||
if (node.parent.type === 'LogicalExpression' && node.parent.operator === '&&') {
|
||||
return;
|
||||
}
|
||||
|
||||
const ops = [];
|
||||
let current = node;
|
||||
|
||||
// Collect `&&` expressions into the ops array
|
||||
while (current.type === 'LogicalExpression' && current.operator === '&&') {
|
||||
ops.unshift(current.right); // Add right operand
|
||||
current = current.left;
|
||||
}
|
||||
ops.unshift(current); // Add the leftmost operand
|
||||
|
||||
// Find the first valid member expression sequence
|
||||
let first = 0;
|
||||
while (first < ops.length - 1 && !isValidMemberExpressionPair(ops[first], ops[first + 1])) {
|
||||
first++;
|
||||
}
|
||||
|
||||
// No valid sequence found
|
||||
if (first === ops.length - 1) return;
|
||||
|
||||
context.report({
|
||||
node,
|
||||
message: 'Prefer optional chaining.',
|
||||
fix(fixer) {
|
||||
// Find the last valid member expression sequence
|
||||
let last = first;
|
||||
while (last < ops.length - 1 && isValidMemberExpressionPair(ops[last], ops[last + 1])) {
|
||||
last++;
|
||||
}
|
||||
|
||||
return fixer.replaceTextRange(
|
||||
[ops[first].range[0], ops[last].range[1]],
|
||||
generateOptionalChaining(ops, first, last),
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
@ -100,9 +100,5 @@ module.exports.inSkipBlock = function(node) {
|
||||
};
|
||||
|
||||
function hasSkip(expression) {
|
||||
return expression &&
|
||||
expression.callee &&
|
||||
(expression.callee.name === 'skip' ||
|
||||
expression.callee.property &&
|
||||
expression.callee.property.name === 'skip');
|
||||
return expression?.callee?.name === 'skip' || expression?.callee?.property?.name === 'skip';
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user