update
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @fileoverview Optimized version of the `text-table` npm module to improve performance by replacing inefficient regex-based
|
||||
* whitespace trimming with a modern built-in method.
|
||||
*
|
||||
* This modification addresses a performance issue reported in https://github.com/eslint/eslint/issues/18709
|
||||
*
|
||||
* The `text-table` module is published under the MIT License. For the original source, refer to:
|
||||
* https://www.npmjs.com/package/text-table.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* This software is released under the MIT license:
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = function (rows_, opts) {
|
||||
const hsep = " ";
|
||||
const align = opts.align;
|
||||
const stringLength = opts.stringLength;
|
||||
|
||||
const sizes = rows_.reduce((acc, row) => {
|
||||
row.forEach((c, ix) => {
|
||||
const n = stringLength(c);
|
||||
|
||||
if (!acc[ix] || n > acc[ix]) {
|
||||
acc[ix] = n;
|
||||
}
|
||||
});
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return rows_
|
||||
.map(row =>
|
||||
row
|
||||
.map((c, ix) => {
|
||||
const n = sizes[ix] - stringLength(c) || 0;
|
||||
const s = Array(Math.max(n + 1, 1)).join(" ");
|
||||
|
||||
if (align[ix] === "r") {
|
||||
return s + c;
|
||||
}
|
||||
|
||||
return c + s;
|
||||
})
|
||||
.join(hsep)
|
||||
.trimEnd(),
|
||||
)
|
||||
.join("\n");
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"names":["_toPrimitive","require","toPropertyKey","arg","key","toPrimitive","String"],"sources":["../../src/helpers/toPropertyKey.ts"],"sourcesContent":["/* @minVersion 7.1.5 */\n\n// https://tc39.es/ecma262/#sec-topropertykey\n\nimport toPrimitive from \"./toPrimitive.ts\";\n\nexport default function toPropertyKey(arg: unknown) {\n var key = toPrimitive(arg, \"string\");\n return typeof key === \"symbol\" ? key : String(key);\n}\n"],"mappings":";;;;;;AAIA,IAAAA,YAAA,GAAAC,OAAA;AAEe,SAASC,aAAaA,CAACC,GAAY,EAAE;EAClD,IAAIC,GAAG,GAAG,IAAAC,oBAAW,EAACF,GAAG,EAAE,QAAQ,CAAC;EACpC,OAAO,OAAOC,GAAG,KAAK,QAAQ,GAAGA,GAAG,GAAGE,MAAM,CAACF,GAAG,CAAC;AACpD","ignoreList":[]}
|
||||
@@ -0,0 +1,8 @@
|
||||
export { decodeOriginalScopes, encodeOriginalScopes, decodeGeneratedRanges, encodeGeneratedRanges, } from './scopes';
|
||||
export type { OriginalScope, GeneratedRange, CallSite, BindingExpressionRange } from './scopes';
|
||||
export declare type SourceMapSegment = [number] | [number, number, number, number] | [number, number, number, number, number];
|
||||
export declare type SourceMapLine = SourceMapSegment[];
|
||||
export declare type SourceMapMappings = SourceMapLine[];
|
||||
export declare function decode(mappings: string): SourceMapMappings;
|
||||
export declare function encode(decoded: SourceMapMappings): string;
|
||||
export declare function encode(decoded: Readonly<SourceMapMappings>): string;
|
||||
@@ -0,0 +1 @@
|
||||
function r(e){var o,t,f="";if("string"==typeof e||"number"==typeof e)f+=e;else if("object"==typeof e)if(Array.isArray(e)){var n=e.length;for(o=0;o<n;o++)e[o]&&(t=r(e[o]))&&(f&&(f+=" "),f+=t)}else for(t in e)e[t]&&(f&&(f+=" "),f+=t);return f}function e(){for(var e,o,t=0,f="",n=arguments.length;t<n;t++)(e=arguments[t])&&(o=r(e))&&(f&&(f+=" "),f+=o);return f}module.exports=e,module.exports.clsx=e;
|
||||
@@ -0,0 +1,330 @@
|
||||
/**
|
||||
* @license React
|
||||
* scheduler.native.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
function push(heap, node) {
|
||||
var index = heap.length;
|
||||
heap.push(node);
|
||||
a: for (; 0 < index; ) {
|
||||
var parentIndex = (index - 1) >>> 1,
|
||||
parent = heap[parentIndex];
|
||||
if (0 < compare(parent, node))
|
||||
(heap[parentIndex] = node), (heap[index] = parent), (index = parentIndex);
|
||||
else break a;
|
||||
}
|
||||
}
|
||||
function peek(heap) {
|
||||
return 0 === heap.length ? null : heap[0];
|
||||
}
|
||||
function pop(heap) {
|
||||
if (0 === heap.length) return null;
|
||||
var first = heap[0],
|
||||
last = heap.pop();
|
||||
if (last !== first) {
|
||||
heap[0] = last;
|
||||
a: for (
|
||||
var index = 0, length = heap.length, halfLength = length >>> 1;
|
||||
index < halfLength;
|
||||
|
||||
) {
|
||||
var leftIndex = 2 * (index + 1) - 1,
|
||||
left = heap[leftIndex],
|
||||
rightIndex = leftIndex + 1,
|
||||
right = heap[rightIndex];
|
||||
if (0 > compare(left, last))
|
||||
rightIndex < length && 0 > compare(right, left)
|
||||
? ((heap[index] = right),
|
||||
(heap[rightIndex] = last),
|
||||
(index = rightIndex))
|
||||
: ((heap[index] = left),
|
||||
(heap[leftIndex] = last),
|
||||
(index = leftIndex));
|
||||
else if (rightIndex < length && 0 > compare(right, last))
|
||||
(heap[index] = right), (heap[rightIndex] = last), (index = rightIndex);
|
||||
else break a;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
}
|
||||
function compare(a, b) {
|
||||
var diff = a.sortIndex - b.sortIndex;
|
||||
return 0 !== diff ? diff : a.id - b.id;
|
||||
}
|
||||
var getCurrentTime;
|
||||
if ("object" === typeof performance && "function" === typeof performance.now) {
|
||||
var localPerformance = performance;
|
||||
getCurrentTime = function () {
|
||||
return localPerformance.now();
|
||||
};
|
||||
} else {
|
||||
var localDate = Date,
|
||||
initialTime = localDate.now();
|
||||
getCurrentTime = function () {
|
||||
return localDate.now() - initialTime;
|
||||
};
|
||||
}
|
||||
var taskQueue = [],
|
||||
timerQueue = [],
|
||||
taskIdCounter = 1,
|
||||
currentTask = null,
|
||||
currentPriorityLevel = 3,
|
||||
isPerformingWork = !1,
|
||||
isHostCallbackScheduled = !1,
|
||||
isHostTimeoutScheduled = !1,
|
||||
needsPaint = !1,
|
||||
localSetTimeout = "function" === typeof setTimeout ? setTimeout : null,
|
||||
localClearTimeout = "function" === typeof clearTimeout ? clearTimeout : null,
|
||||
localSetImmediate = "undefined" !== typeof setImmediate ? setImmediate : null;
|
||||
function advanceTimers(currentTime) {
|
||||
for (var timer = peek(timerQueue); null !== timer; ) {
|
||||
if (null === timer.callback) pop(timerQueue);
|
||||
else if (timer.startTime <= currentTime)
|
||||
pop(timerQueue),
|
||||
(timer.sortIndex = timer.expirationTime),
|
||||
push(taskQueue, timer);
|
||||
else break;
|
||||
timer = peek(timerQueue);
|
||||
}
|
||||
}
|
||||
function handleTimeout(currentTime) {
|
||||
isHostTimeoutScheduled = !1;
|
||||
advanceTimers(currentTime);
|
||||
if (!isHostCallbackScheduled)
|
||||
if (null !== peek(taskQueue))
|
||||
(isHostCallbackScheduled = !0),
|
||||
isMessageLoopRunning ||
|
||||
((isMessageLoopRunning = !0), schedulePerformWorkUntilDeadline());
|
||||
else {
|
||||
var firstTimer = peek(timerQueue);
|
||||
null !== firstTimer &&
|
||||
requestHostTimeout(handleTimeout, firstTimer.startTime - currentTime);
|
||||
}
|
||||
}
|
||||
function unstable_scheduleCallback$1(priorityLevel, callback, options) {
|
||||
var currentTime = getCurrentTime();
|
||||
"object" === typeof options && null !== options
|
||||
? ((options = options.delay),
|
||||
(options =
|
||||
"number" === typeof options && 0 < options
|
||||
? currentTime + options
|
||||
: currentTime))
|
||||
: (options = currentTime);
|
||||
switch (priorityLevel) {
|
||||
case 1:
|
||||
var timeout = -1;
|
||||
break;
|
||||
case 2:
|
||||
timeout = 250;
|
||||
break;
|
||||
case 5:
|
||||
timeout = 1073741823;
|
||||
break;
|
||||
case 4:
|
||||
timeout = 1e4;
|
||||
break;
|
||||
default:
|
||||
timeout = 5e3;
|
||||
}
|
||||
timeout = options + timeout;
|
||||
priorityLevel = {
|
||||
id: taskIdCounter++,
|
||||
callback: callback,
|
||||
priorityLevel: priorityLevel,
|
||||
startTime: options,
|
||||
expirationTime: timeout,
|
||||
sortIndex: -1
|
||||
};
|
||||
options > currentTime
|
||||
? ((priorityLevel.sortIndex = options),
|
||||
push(timerQueue, priorityLevel),
|
||||
null === peek(taskQueue) &&
|
||||
priorityLevel === peek(timerQueue) &&
|
||||
(isHostTimeoutScheduled
|
||||
? (localClearTimeout(taskTimeoutID), (taskTimeoutID = -1))
|
||||
: (isHostTimeoutScheduled = !0),
|
||||
requestHostTimeout(handleTimeout, options - currentTime)))
|
||||
: ((priorityLevel.sortIndex = timeout),
|
||||
push(taskQueue, priorityLevel),
|
||||
isHostCallbackScheduled ||
|
||||
isPerformingWork ||
|
||||
((isHostCallbackScheduled = !0),
|
||||
isMessageLoopRunning ||
|
||||
((isMessageLoopRunning = !0), schedulePerformWorkUntilDeadline())));
|
||||
return priorityLevel;
|
||||
}
|
||||
function unstable_cancelCallback$1(task) {
|
||||
task.callback = null;
|
||||
}
|
||||
function unstable_getCurrentPriorityLevel$1() {
|
||||
return currentPriorityLevel;
|
||||
}
|
||||
var isMessageLoopRunning = !1,
|
||||
taskTimeoutID = -1,
|
||||
startTime = -1;
|
||||
function shouldYieldToHost() {
|
||||
return needsPaint ? !0 : 5 > getCurrentTime() - startTime ? !1 : !0;
|
||||
}
|
||||
function requestPaint() {
|
||||
needsPaint = !0;
|
||||
}
|
||||
function performWorkUntilDeadline() {
|
||||
needsPaint = !1;
|
||||
if (isMessageLoopRunning) {
|
||||
var currentTime = getCurrentTime();
|
||||
startTime = currentTime;
|
||||
var hasMoreWork = !0;
|
||||
try {
|
||||
a: {
|
||||
isHostCallbackScheduled = !1;
|
||||
isHostTimeoutScheduled &&
|
||||
((isHostTimeoutScheduled = !1),
|
||||
localClearTimeout(taskTimeoutID),
|
||||
(taskTimeoutID = -1));
|
||||
isPerformingWork = !0;
|
||||
var previousPriorityLevel = currentPriorityLevel;
|
||||
try {
|
||||
b: {
|
||||
advanceTimers(currentTime);
|
||||
for (
|
||||
currentTask = peek(taskQueue);
|
||||
null !== currentTask &&
|
||||
!(
|
||||
currentTask.expirationTime > currentTime && shouldYieldToHost()
|
||||
);
|
||||
|
||||
) {
|
||||
var callback = currentTask.callback;
|
||||
if ("function" === typeof callback) {
|
||||
currentTask.callback = null;
|
||||
currentPriorityLevel = currentTask.priorityLevel;
|
||||
var continuationCallback = callback(
|
||||
currentTask.expirationTime <= currentTime
|
||||
);
|
||||
currentTime = getCurrentTime();
|
||||
if ("function" === typeof continuationCallback) {
|
||||
currentTask.callback = continuationCallback;
|
||||
advanceTimers(currentTime);
|
||||
hasMoreWork = !0;
|
||||
break b;
|
||||
}
|
||||
currentTask === peek(taskQueue) && pop(taskQueue);
|
||||
advanceTimers(currentTime);
|
||||
} else pop(taskQueue);
|
||||
currentTask = peek(taskQueue);
|
||||
}
|
||||
if (null !== currentTask) hasMoreWork = !0;
|
||||
else {
|
||||
var firstTimer = peek(timerQueue);
|
||||
null !== firstTimer &&
|
||||
requestHostTimeout(
|
||||
handleTimeout,
|
||||
firstTimer.startTime - currentTime
|
||||
);
|
||||
hasMoreWork = !1;
|
||||
}
|
||||
}
|
||||
break a;
|
||||
} finally {
|
||||
(currentTask = null),
|
||||
(currentPriorityLevel = previousPriorityLevel),
|
||||
(isPerformingWork = !1);
|
||||
}
|
||||
hasMoreWork = void 0;
|
||||
}
|
||||
} finally {
|
||||
hasMoreWork
|
||||
? schedulePerformWorkUntilDeadline()
|
||||
: (isMessageLoopRunning = !1);
|
||||
}
|
||||
}
|
||||
}
|
||||
var schedulePerformWorkUntilDeadline;
|
||||
if ("function" === typeof localSetImmediate)
|
||||
schedulePerformWorkUntilDeadline = function () {
|
||||
localSetImmediate(performWorkUntilDeadline);
|
||||
};
|
||||
else if ("undefined" !== typeof MessageChannel) {
|
||||
var channel = new MessageChannel(),
|
||||
port = channel.port2;
|
||||
channel.port1.onmessage = performWorkUntilDeadline;
|
||||
schedulePerformWorkUntilDeadline = function () {
|
||||
port.postMessage(null);
|
||||
};
|
||||
} else
|
||||
schedulePerformWorkUntilDeadline = function () {
|
||||
localSetTimeout(performWorkUntilDeadline, 0);
|
||||
};
|
||||
function requestHostTimeout(callback, ms) {
|
||||
taskTimeoutID = localSetTimeout(function () {
|
||||
callback(getCurrentTime());
|
||||
}, ms);
|
||||
}
|
||||
var unstable_UserBlockingPriority =
|
||||
"undefined" !== typeof nativeRuntimeScheduler
|
||||
? nativeRuntimeScheduler.unstable_UserBlockingPriority
|
||||
: 2,
|
||||
unstable_NormalPriority =
|
||||
"undefined" !== typeof nativeRuntimeScheduler
|
||||
? nativeRuntimeScheduler.unstable_NormalPriority
|
||||
: 3,
|
||||
unstable_LowPriority =
|
||||
"undefined" !== typeof nativeRuntimeScheduler
|
||||
? nativeRuntimeScheduler.unstable_LowPriority
|
||||
: 4,
|
||||
unstable_ImmediatePriority =
|
||||
"undefined" !== typeof nativeRuntimeScheduler
|
||||
? nativeRuntimeScheduler.unstable_ImmediatePriority
|
||||
: 1,
|
||||
unstable_scheduleCallback =
|
||||
"undefined" !== typeof nativeRuntimeScheduler
|
||||
? nativeRuntimeScheduler.unstable_scheduleCallback
|
||||
: unstable_scheduleCallback$1,
|
||||
unstable_cancelCallback =
|
||||
"undefined" !== typeof nativeRuntimeScheduler
|
||||
? nativeRuntimeScheduler.unstable_cancelCallback
|
||||
: unstable_cancelCallback$1,
|
||||
unstable_getCurrentPriorityLevel =
|
||||
"undefined" !== typeof nativeRuntimeScheduler
|
||||
? nativeRuntimeScheduler.unstable_getCurrentPriorityLevel
|
||||
: unstable_getCurrentPriorityLevel$1,
|
||||
unstable_shouldYield =
|
||||
"undefined" !== typeof nativeRuntimeScheduler
|
||||
? nativeRuntimeScheduler.unstable_shouldYield
|
||||
: shouldYieldToHost,
|
||||
unstable_requestPaint =
|
||||
"undefined" !== typeof nativeRuntimeScheduler
|
||||
? nativeRuntimeScheduler.unstable_requestPaint
|
||||
: requestPaint,
|
||||
unstable_now =
|
||||
"undefined" !== typeof nativeRuntimeScheduler
|
||||
? nativeRuntimeScheduler.unstable_now
|
||||
: getCurrentTime;
|
||||
function throwNotImplemented() {
|
||||
throw Error("Not implemented.");
|
||||
}
|
||||
exports.unstable_IdlePriority =
|
||||
"undefined" !== typeof nativeRuntimeScheduler
|
||||
? nativeRuntimeScheduler.unstable_IdlePriority
|
||||
: 5;
|
||||
exports.unstable_ImmediatePriority = unstable_ImmediatePriority;
|
||||
exports.unstable_LowPriority = unstable_LowPriority;
|
||||
exports.unstable_NormalPriority = unstable_NormalPriority;
|
||||
exports.unstable_Profiling = null;
|
||||
exports.unstable_UserBlockingPriority = unstable_UserBlockingPriority;
|
||||
exports.unstable_cancelCallback = unstable_cancelCallback;
|
||||
exports.unstable_forceFrameRate = throwNotImplemented;
|
||||
exports.unstable_getCurrentPriorityLevel = unstable_getCurrentPriorityLevel;
|
||||
exports.unstable_next = throwNotImplemented;
|
||||
exports.unstable_now = unstable_now;
|
||||
exports.unstable_requestPaint = unstable_requestPaint;
|
||||
exports.unstable_runWithPriority = throwNotImplemented;
|
||||
exports.unstable_scheduleCallback = unstable_scheduleCallback;
|
||||
exports.unstable_shouldYield = unstable_shouldYield;
|
||||
exports.unstable_wrapCallback = throwNotImplemented;
|
||||
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* @fileoverview Rule to warn when a function expression does not have a name.
|
||||
* @author Kyle T. Nunery
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
/**
|
||||
* Checks whether or not a given variable is a function name.
|
||||
* @param {eslint-scope.Variable} variable A variable to check.
|
||||
* @returns {boolean} `true` if the variable is a function name.
|
||||
*/
|
||||
function isFunctionName(variable) {
|
||||
return variable && variable.defs[0].type === "FunctionName";
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
defaultOptions: ["always", {}],
|
||||
|
||||
docs: {
|
||||
description: "Require or disallow named `function` expressions",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/func-names",
|
||||
},
|
||||
|
||||
schema: {
|
||||
definitions: {
|
||||
value: {
|
||||
enum: ["always", "as-needed", "never"],
|
||||
},
|
||||
},
|
||||
items: [
|
||||
{
|
||||
$ref: "#/definitions/value",
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
generators: {
|
||||
$ref: "#/definitions/value",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
messages: {
|
||||
unnamed: "Unexpected unnamed {{name}}.",
|
||||
named: "Unexpected named {{name}}.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Returns the config option for the given node.
|
||||
* @param {ASTNode} node A node to get the config for.
|
||||
* @returns {string} The config option.
|
||||
*/
|
||||
function getConfigForNode(node) {
|
||||
if (node.generator && context.options[1].generators) {
|
||||
return context.options[1].generators;
|
||||
}
|
||||
|
||||
return context.options[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the current FunctionExpression node is a get, set, or
|
||||
* shorthand method in an object literal or a class.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} True if the node is a get, set, or shorthand method.
|
||||
*/
|
||||
function isObjectOrClassMethod(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
parent.type === "MethodDefinition" ||
|
||||
(parent.type === "Property" &&
|
||||
(parent.method ||
|
||||
parent.kind === "get" ||
|
||||
parent.kind === "set"))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the current FunctionExpression node has a name that would be
|
||||
* inferred from context in a conforming ES6 environment.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} True if the node would have a name assigned automatically.
|
||||
*/
|
||||
function hasInferredName(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
isObjectOrClassMethod(node) ||
|
||||
(parent.type === "VariableDeclarator" &&
|
||||
parent.id.type === "Identifier" &&
|
||||
parent.init === node) ||
|
||||
(parent.type === "Property" && parent.value === node) ||
|
||||
(parent.type === "PropertyDefinition" &&
|
||||
parent.value === node) ||
|
||||
(parent.type === "AssignmentExpression" &&
|
||||
parent.left.type === "Identifier" &&
|
||||
parent.right === node) ||
|
||||
(parent.type === "AssignmentPattern" &&
|
||||
parent.left.type === "Identifier" &&
|
||||
parent.right === node)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that an unnamed function should be named
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportUnexpectedUnnamedFunction(node) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unnamed",
|
||||
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
||||
data: { name: astUtils.getFunctionNameWithKind(node) },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that a named function should be unnamed
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportUnexpectedNamedFunction(node) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "named",
|
||||
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
||||
data: { name: astUtils.getFunctionNameWithKind(node) },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener for function nodes.
|
||||
* @param {ASTNode} node function node
|
||||
* @returns {void}
|
||||
*/
|
||||
function handleFunction(node) {
|
||||
// Skip recursive functions.
|
||||
const nameVar = sourceCode.getDeclaredVariables(node)[0];
|
||||
|
||||
if (isFunctionName(nameVar) && nameVar.references.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasName = Boolean(node.id && node.id.name);
|
||||
const config = getConfigForNode(node);
|
||||
|
||||
if (config === "never") {
|
||||
if (hasName && node.type !== "FunctionDeclaration") {
|
||||
reportUnexpectedNamedFunction(node);
|
||||
}
|
||||
} else if (config === "as-needed") {
|
||||
if (!hasName && !hasInferredName(node)) {
|
||||
reportUnexpectedUnnamedFunction(node);
|
||||
}
|
||||
} else {
|
||||
if (!hasName && !isObjectOrClassMethod(node)) {
|
||||
reportUnexpectedUnnamedFunction(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"FunctionExpression:exit": handleFunction,
|
||||
"ExportDefaultDeclaration > FunctionDeclaration": handleFunction,
|
||||
};
|
||||
},
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
export declare function SafeFragment(props: any): import("react/jsx-runtime").JSX.Element;
|
||||
@@ -0,0 +1,401 @@
|
||||
/**
|
||||
* @fileoverview Disallow redundant return statements
|
||||
* @author Teddy Katz
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils"),
|
||||
FixTracker = require("./utils/fix-tracker");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Removes the given element from the array.
|
||||
* @param {Array} array The source array to remove.
|
||||
* @param {any} element The target item to remove.
|
||||
* @returns {void}
|
||||
*/
|
||||
function remove(array, element) {
|
||||
const index = array.indexOf(element);
|
||||
|
||||
if (index !== -1) {
|
||||
array.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether it can remove the given return statement or not.
|
||||
* @param {ASTNode} node The return statement node to check.
|
||||
* @returns {boolean} `true` if the node is removable.
|
||||
*/
|
||||
function isRemovable(node) {
|
||||
return astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given return statement is in a `finally` block or not.
|
||||
* @param {ASTNode} node The return statement node to check.
|
||||
* @returns {boolean} `true` if the node is in a `finally` block.
|
||||
*/
|
||||
function isInFinally(node) {
|
||||
for (
|
||||
let currentNode = node;
|
||||
currentNode && currentNode.parent && !astUtils.isFunction(currentNode);
|
||||
currentNode = currentNode.parent
|
||||
) {
|
||||
if (
|
||||
currentNode.parent.type === "TryStatement" &&
|
||||
currentNode.parent.finalizer === currentNode
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks all segments in a set and returns true if any are reachable.
|
||||
* @param {Set<CodePathSegment>} segments The segments to check.
|
||||
* @returns {boolean} True if any segment is reachable; false otherwise.
|
||||
*/
|
||||
function isAnySegmentReachable(segments) {
|
||||
for (const segment of segments) {
|
||||
if (segment.reachable) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Disallow redundant return statements",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/no-useless-return",
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
unnecessaryReturn: "Unnecessary return statement.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const segmentInfoMap = new WeakMap();
|
||||
const sourceCode = context.sourceCode;
|
||||
let scopeInfo = null;
|
||||
|
||||
/**
|
||||
* Checks whether the given segment is terminated by a return statement or not.
|
||||
* @param {CodePathSegment} segment The segment to check.
|
||||
* @returns {boolean} `true` if the segment is terminated by a return statement, or if it's still a part of unreachable.
|
||||
*/
|
||||
function isReturned(segment) {
|
||||
const info = segmentInfoMap.get(segment);
|
||||
|
||||
return !info || info.returned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects useless return statements from the given previous segments.
|
||||
*
|
||||
* A previous segment may be an unreachable segment.
|
||||
* In that case, the information object of the unreachable segment is not
|
||||
* initialized because `onCodePathSegmentStart` event is not notified for
|
||||
* unreachable segments.
|
||||
* This goes to the previous segments of the unreachable segment recursively
|
||||
* if the unreachable segment was generated by a return statement. Otherwise,
|
||||
* this ignores the unreachable segment.
|
||||
*
|
||||
* This behavior would simulate code paths for the case that the return
|
||||
* statement does not exist.
|
||||
* @param {ASTNode[]} uselessReturns The collected return statements.
|
||||
* @param {CodePathSegment[]} prevSegments The previous segments to traverse.
|
||||
* @param {WeakSet<CodePathSegment>} [providedTraversedSegments] A set of segments that have already been traversed in this call
|
||||
* @returns {ASTNode[]} `uselessReturns`.
|
||||
*/
|
||||
function getUselessReturns(
|
||||
uselessReturns,
|
||||
prevSegments,
|
||||
providedTraversedSegments,
|
||||
) {
|
||||
const traversedSegments =
|
||||
providedTraversedSegments || new WeakSet();
|
||||
|
||||
for (const segment of prevSegments) {
|
||||
if (!segment.reachable) {
|
||||
if (!traversedSegments.has(segment)) {
|
||||
traversedSegments.add(segment);
|
||||
getUselessReturns(
|
||||
uselessReturns,
|
||||
segment.allPrevSegments.filter(isReturned),
|
||||
traversedSegments,
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (segmentInfoMap.has(segment)) {
|
||||
uselessReturns.push(
|
||||
...segmentInfoMap.get(segment).uselessReturns,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return uselessReturns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the return statements on the given segment from the useless return
|
||||
* statement list.
|
||||
*
|
||||
* This segment may be an unreachable segment.
|
||||
* In that case, the information object of the unreachable segment is not
|
||||
* initialized because `onCodePathSegmentStart` event is not notified for
|
||||
* unreachable segments.
|
||||
* This goes to the previous segments of the unreachable segment recursively
|
||||
* if the unreachable segment was generated by a return statement. Otherwise,
|
||||
* this ignores the unreachable segment.
|
||||
*
|
||||
* This behavior would simulate code paths for the case that the return
|
||||
* statement does not exist.
|
||||
* @param {CodePathSegment} segment The segment to get return statements.
|
||||
* @param {Set<CodePathSegment>} usedUnreachableSegments A set of segments that have already been traversed in this call.
|
||||
* @returns {void}
|
||||
*/
|
||||
function markReturnStatementsOnSegmentAsUsed(
|
||||
segment,
|
||||
usedUnreachableSegments,
|
||||
) {
|
||||
if (!segment.reachable) {
|
||||
usedUnreachableSegments.add(segment);
|
||||
segment.allPrevSegments
|
||||
.filter(isReturned)
|
||||
.filter(
|
||||
prevSegment =>
|
||||
!usedUnreachableSegments.has(prevSegment),
|
||||
)
|
||||
.forEach(prevSegment =>
|
||||
markReturnStatementsOnSegmentAsUsed(
|
||||
prevSegment,
|
||||
usedUnreachableSegments,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const info = segmentInfoMap.get(segment);
|
||||
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
|
||||
info.uselessReturns = info.uselessReturns.filter(node => {
|
||||
if (
|
||||
scopeInfo.traversedTryBlockStatements &&
|
||||
scopeInfo.traversedTryBlockStatements.length > 0
|
||||
) {
|
||||
const returnInitialRange = node.range[0];
|
||||
const returnFinalRange = node.range[1];
|
||||
|
||||
const areBlocksInRange =
|
||||
scopeInfo.traversedTryBlockStatements.some(
|
||||
tryBlockStatement => {
|
||||
const blockInitialRange =
|
||||
tryBlockStatement.range[0];
|
||||
const blockFinalRange =
|
||||
tryBlockStatement.range[1];
|
||||
|
||||
return (
|
||||
returnInitialRange >= blockInitialRange &&
|
||||
returnFinalRange <= blockFinalRange
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (areBlocksInRange) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
remove(scopeInfo.uselessReturns, node);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the return statements on the current segments from the useless
|
||||
* return statement list.
|
||||
*
|
||||
* This function will be called at every statement except FunctionDeclaration,
|
||||
* BlockStatement, and BreakStatement.
|
||||
*
|
||||
* - FunctionDeclarations are always executed whether it's returned or not.
|
||||
* - BlockStatements do nothing.
|
||||
* - BreakStatements go the next merely.
|
||||
* @returns {void}
|
||||
*/
|
||||
function markReturnStatementsOnCurrentSegmentsAsUsed() {
|
||||
scopeInfo.currentSegments.forEach(segment =>
|
||||
markReturnStatementsOnSegmentAsUsed(segment, new Set()),
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Public
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
// Makes and pushes a new scope information.
|
||||
onCodePathStart(codePath) {
|
||||
scopeInfo = {
|
||||
upper: scopeInfo,
|
||||
uselessReturns: [],
|
||||
traversedTryBlockStatements: [],
|
||||
codePath,
|
||||
currentSegments: new Set(),
|
||||
};
|
||||
},
|
||||
|
||||
// Reports useless return statements if exist.
|
||||
onCodePathEnd() {
|
||||
for (const node of scopeInfo.uselessReturns) {
|
||||
context.report({
|
||||
node,
|
||||
loc: node.loc,
|
||||
messageId: "unnecessaryReturn",
|
||||
fix(fixer) {
|
||||
if (
|
||||
isRemovable(node) &&
|
||||
!sourceCode.getCommentsInside(node).length
|
||||
) {
|
||||
/*
|
||||
* Extend the replacement range to include the
|
||||
* entire function to avoid conflicting with
|
||||
* no-else-return.
|
||||
* https://github.com/eslint/eslint/issues/8026
|
||||
*/
|
||||
return new FixTracker(fixer, sourceCode)
|
||||
.retainEnclosingFunction(node)
|
||||
.remove(node);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
scopeInfo = scopeInfo.upper;
|
||||
},
|
||||
|
||||
/*
|
||||
* Initializes segments.
|
||||
* NOTE: This event is notified for only reachable segments.
|
||||
*/
|
||||
onCodePathSegmentStart(segment) {
|
||||
scopeInfo.currentSegments.add(segment);
|
||||
|
||||
const info = {
|
||||
uselessReturns: getUselessReturns(
|
||||
[],
|
||||
segment.allPrevSegments,
|
||||
),
|
||||
returned: false,
|
||||
};
|
||||
|
||||
// Stores the info.
|
||||
segmentInfoMap.set(segment, info);
|
||||
},
|
||||
|
||||
onUnreachableCodePathSegmentStart(segment) {
|
||||
scopeInfo.currentSegments.add(segment);
|
||||
},
|
||||
|
||||
onUnreachableCodePathSegmentEnd(segment) {
|
||||
scopeInfo.currentSegments.delete(segment);
|
||||
},
|
||||
|
||||
onCodePathSegmentEnd(segment) {
|
||||
scopeInfo.currentSegments.delete(segment);
|
||||
},
|
||||
|
||||
// Adds ReturnStatement node to check whether it's useless or not.
|
||||
ReturnStatement(node) {
|
||||
if (node.argument) {
|
||||
markReturnStatementsOnCurrentSegmentsAsUsed();
|
||||
}
|
||||
if (
|
||||
node.argument ||
|
||||
astUtils.isInLoop(node) ||
|
||||
isInFinally(node) ||
|
||||
// Ignore `return` statements in unreachable places (https://github.com/eslint/eslint/issues/11647).
|
||||
!isAnySegmentReachable(scopeInfo.currentSegments)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const segment of scopeInfo.currentSegments) {
|
||||
const info = segmentInfoMap.get(segment);
|
||||
|
||||
if (info) {
|
||||
info.uselessReturns.push(node);
|
||||
info.returned = true;
|
||||
}
|
||||
}
|
||||
scopeInfo.uselessReturns.push(node);
|
||||
},
|
||||
|
||||
"TryStatement > BlockStatement.block:exit"(node) {
|
||||
scopeInfo.traversedTryBlockStatements.push(node);
|
||||
},
|
||||
|
||||
"TryStatement:exit"() {
|
||||
scopeInfo.traversedTryBlockStatements.pop();
|
||||
},
|
||||
|
||||
/*
|
||||
* Registers for all statement nodes except FunctionDeclaration, BlockStatement, BreakStatement.
|
||||
* Removes return statements of the current segments from the useless return statement list.
|
||||
*/
|
||||
ClassDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
ContinueStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
DebuggerStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
DoWhileStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
EmptyStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
ExpressionStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
ForInStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
ForOfStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
ForStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
IfStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
ImportDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
LabeledStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
SwitchStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
ThrowStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
TryStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
VariableDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
WhileStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
WithStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
ExportNamedDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
ExportDefaultDeclaration:
|
||||
markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
ExportAllDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
|
||||
};
|
||||
},
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
module.exports={A:{A:{"2":"mC","8":"K D E F A B"},B:{"1":"0 9 Q H R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I","8":"C L M G N O P"},C:{"1":"0 1 2 3 4 5 6 7 8 9 J PB K D E F A B C L M G N O P QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB uB vB MC wB NC xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R OC S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I PC EC QC RC oC pC","8":"nC LC qC rC"},D:{"1":"0 1 2 3 4 5 6 7 8 9 PB K D E F A B C L M G N O P QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB uB vB MC wB NC xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z AB BB CB DB EB FB GB HB IB JB KB LB MB NB OB I PC EC QC RC","4":"J"},E:{"1":"K D E F A B C L M G uC vC wC TC FC GC xC yC zC UC VC HC 0C IC WC XC YC ZC aC 1C JC bC cC dC eC fC 2C KC gC hC iC jC 3C","8":"sC SC","132":"J PB tC"},F:{"1":"0 1 2 3 4 5 6 7 8 F B C G N O P QB RB SB TB UB VB WB XB YB ZB aB bB cB dB eB fB gB hB iB jB kB lB mB nB oB pB qB rB sB tB uB vB wB xB yB zB 0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AC BC CC DC Q H R OC S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z 4C 5C 6C 7C FC kC 8C GC"},G:{"1":"E BD CD DD ED FD GD HD ID JD KD LD MD ND OD PD QD RD SD UC VC HC TD IC WC XC YC ZC aC UD JC bC cC dC eC fC VD KC gC hC iC jC","132":"SC 9C lC AD"},H:{"2":"WD"},I:{"1":"LC J I aD lC bD cD","2":"XD YD ZD"},J:{"1":"D A"},K:{"1":"A B C H FC kC GC"},L:{"1":"I"},M:{"1":"EC"},N:{"8":"A B"},O:{"1":"HC"},P:{"1":"1 2 3 4 5 6 7 8 J dD eD fD gD hD TC iD jD kD lD mD IC JC KC nD"},Q:{"1":"oD"},R:{"1":"pD"},S:{"1":"qD rD"}},B:2,C:"SVG SMIL animation",D:true};
|
||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict"
|
||||
|
||||
require("../dist/bin.js")
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"names":["_utils","require","PLACEHOLDERS","exports","PLACEHOLDERS_ALIAS","Declaration","Pattern","type","alias","ALIAS_KEYS","length","PLACEHOLDERS_FLIPPED_ALIAS","Object","keys","forEach","hasOwnProperty","call","push"],"sources":["../../src/definitions/placeholders.ts"],"sourcesContent":["import { ALIAS_KEYS } from \"./utils.ts\";\n\nexport const PLACEHOLDERS = [\n \"Identifier\",\n \"StringLiteral\",\n \"Expression\",\n \"Statement\",\n \"Declaration\",\n \"BlockStatement\",\n \"ClassBody\",\n \"Pattern\",\n] as const;\n\nexport const PLACEHOLDERS_ALIAS: Record<string, string[]> = {\n Declaration: [\"Statement\"],\n Pattern: [\"PatternLike\", \"LVal\"],\n};\n\nfor (const type of PLACEHOLDERS) {\n const alias = ALIAS_KEYS[type];\n if (alias?.length) PLACEHOLDERS_ALIAS[type] = alias;\n}\n\nexport const PLACEHOLDERS_FLIPPED_ALIAS: Record<string, string[]> = {};\n\nObject.keys(PLACEHOLDERS_ALIAS).forEach(type => {\n PLACEHOLDERS_ALIAS[type].forEach(alias => {\n if (!Object.hasOwn(PLACEHOLDERS_FLIPPED_ALIAS, alias)) {\n PLACEHOLDERS_FLIPPED_ALIAS[alias] = [];\n }\n PLACEHOLDERS_FLIPPED_ALIAS[alias].push(type);\n });\n});\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAEO,MAAMC,YAAY,GAAAC,OAAA,CAAAD,YAAA,GAAG,CAC1B,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,SAAS,CACD;AAEH,MAAME,kBAA4C,GAAAD,OAAA,CAAAC,kBAAA,GAAG;EAC1DC,WAAW,EAAE,CAAC,WAAW,CAAC;EAC1BC,OAAO,EAAE,CAAC,aAAa,EAAE,MAAM;AACjC,CAAC;AAED,KAAK,MAAMC,IAAI,IAAIL,YAAY,EAAE;EAC/B,MAAMM,KAAK,GAAGC,iBAAU,CAACF,IAAI,CAAC;EAC9B,IAAIC,KAAK,YAALA,KAAK,CAAEE,MAAM,EAAEN,kBAAkB,CAACG,IAAI,CAAC,GAAGC,KAAK;AACrD;AAEO,MAAMG,0BAAoD,GAAAR,OAAA,CAAAQ,0BAAA,GAAG,CAAC,CAAC;AAEtEC,MAAM,CAACC,IAAI,CAACT,kBAAkB,CAAC,CAACU,OAAO,CAACP,IAAI,IAAI;EAC9CH,kBAAkB,CAACG,IAAI,CAAC,CAACO,OAAO,CAACN,KAAK,IAAI;IACxC,IAAI,CAACO,cAAA,CAAAC,IAAA,CAAcL,0BAA0B,EAAEH,KAAK,CAAC,EAAE;MACrDG,0BAA0B,CAACH,KAAK,CAAC,GAAG,EAAE;IACxC;IACAG,0BAA0B,CAACH,KAAK,CAAC,CAACS,IAAI,CAACV,IAAI,CAAC;EAC9C,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
|
||||
@@ -0,0 +1,72 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = _wrapRegExp;
|
||||
var _setPrototypeOf = require("./setPrototypeOf.js");
|
||||
var _inherits = require("./inherits.js");
|
||||
function _wrapRegExp() {
|
||||
exports.default = _wrapRegExp = function (re, groups) {
|
||||
return new BabelRegExp(re, undefined, groups);
|
||||
};
|
||||
var _super = RegExp.prototype;
|
||||
var _groups = new WeakMap();
|
||||
function BabelRegExp(re, flags, groups) {
|
||||
var _this = new RegExp(re, flags);
|
||||
_groups.set(_this, groups || _groups.get(re));
|
||||
return (0, _setPrototypeOf.default)(_this, BabelRegExp.prototype);
|
||||
}
|
||||
(0, _inherits.default)(BabelRegExp, RegExp);
|
||||
BabelRegExp.prototype.exec = function (str) {
|
||||
var result = _super.exec.call(this, str);
|
||||
if (result) {
|
||||
result.groups = buildGroups(result, this);
|
||||
var indices = result.indices;
|
||||
if (indices) indices.groups = buildGroups(indices, this);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
BabelRegExp.prototype[Symbol.replace] = function (str, substitution) {
|
||||
if (typeof substitution === "string") {
|
||||
var groups = _groups.get(this);
|
||||
return _super[Symbol.replace].call(this, str, substitution.replace(/\$<([^>]+)(>|$)/g, function (match, name, end) {
|
||||
if (end === "") {
|
||||
return match;
|
||||
} else {
|
||||
var group = groups[name];
|
||||
return Array.isArray(group) ? "$" + group.join("$") : typeof group === "number" ? "$" + group : "";
|
||||
}
|
||||
}));
|
||||
} else if (typeof substitution === "function") {
|
||||
var _this = this;
|
||||
return _super[Symbol.replace].call(this, str, function () {
|
||||
var args = arguments;
|
||||
if (typeof args[args.length - 1] !== "object") {
|
||||
args = [].slice.call(args);
|
||||
args.push(buildGroups(args, _this));
|
||||
}
|
||||
return substitution.apply(this, args);
|
||||
});
|
||||
} else {
|
||||
return _super[Symbol.replace].call(this, str, substitution);
|
||||
}
|
||||
};
|
||||
function buildGroups(result, re) {
|
||||
var g = _groups.get(re);
|
||||
return Object.keys(g).reduce(function (groups, name) {
|
||||
var i = g[name];
|
||||
if (typeof i === "number") groups[name] = result[i];else {
|
||||
var k = 0;
|
||||
while (result[i[k]] === undefined && k + 1 < i.length) {
|
||||
k++;
|
||||
}
|
||||
groups[name] = result[i[k]];
|
||||
}
|
||||
return groups;
|
||||
}, Object.create(null));
|
||||
}
|
||||
return _wrapRegExp.apply(this, arguments);
|
||||
}
|
||||
|
||||
//# sourceMappingURL=wrapRegExp.js.map
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { StringReader, StringWriter } from './strings';
|
||||
export declare const comma: number;
|
||||
export declare const semicolon: number;
|
||||
export declare function decodeInteger(reader: StringReader, relative: number): number;
|
||||
export declare function encodeInteger(builder: StringWriter, num: number, relative: number): number;
|
||||
export declare function hasMoreVlq(reader: StringReader, max: number): boolean;
|
||||
@@ -0,0 +1,664 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Tobias Koppers @sokra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const nextTick = require("process").nextTick;
|
||||
|
||||
/** @typedef {import("./Resolver").FileSystem} FileSystem */
|
||||
/** @typedef {import("./Resolver").PathLike} PathLike */
|
||||
/** @typedef {import("./Resolver").PathOrFileDescriptor} PathOrFileDescriptor */
|
||||
/** @typedef {import("./Resolver").SyncFileSystem} SyncFileSystem */
|
||||
/** @typedef {FileSystem & SyncFileSystem} BaseFileSystem */
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {import("./Resolver").FileSystemCallback<T>} FileSystemCallback<T>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} path path
|
||||
* @returns {string} dirname
|
||||
*/
|
||||
const dirname = path => {
|
||||
let idx = path.length - 1;
|
||||
while (idx >= 0) {
|
||||
const c = path.charCodeAt(idx);
|
||||
// slash or backslash
|
||||
if (c === 47 || c === 92) break;
|
||||
idx--;
|
||||
}
|
||||
if (idx < 0) return "";
|
||||
return path.slice(0, idx);
|
||||
};
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {FileSystemCallback<T>[]} callbacks callbacks
|
||||
* @param {Error | null} err error
|
||||
* @param {T} result result
|
||||
*/
|
||||
const runCallbacks = (callbacks, err, result) => {
|
||||
if (callbacks.length === 1) {
|
||||
callbacks[0](err, result);
|
||||
callbacks.length = 0;
|
||||
return;
|
||||
}
|
||||
let error;
|
||||
for (const callback of callbacks) {
|
||||
try {
|
||||
callback(err, result);
|
||||
} catch (e) {
|
||||
if (!error) error = e;
|
||||
}
|
||||
}
|
||||
callbacks.length = 0;
|
||||
if (error) throw error;
|
||||
};
|
||||
|
||||
class OperationMergerBackend {
|
||||
/**
|
||||
* @param {Function | undefined} provider async method in filesystem
|
||||
* @param {Function | undefined} syncProvider sync method in filesystem
|
||||
* @param {BaseFileSystem} providerContext call context for the provider methods
|
||||
*/
|
||||
constructor(provider, syncProvider, providerContext) {
|
||||
this._provider = provider;
|
||||
this._syncProvider = syncProvider;
|
||||
this._providerContext = providerContext;
|
||||
this._activeAsyncOperations = new Map();
|
||||
|
||||
this.provide = this._provider
|
||||
? /**
|
||||
* @param {PathLike | PathOrFileDescriptor} path path
|
||||
* @param {object | FileSystemCallback<any> | undefined} options options
|
||||
* @param {FileSystemCallback<any>=} callback callback
|
||||
* @returns {any} result
|
||||
*/
|
||||
(path, options, callback) => {
|
||||
if (typeof options === "function") {
|
||||
callback = /** @type {FileSystemCallback<any>} */ (options);
|
||||
options = undefined;
|
||||
}
|
||||
if (
|
||||
typeof path !== "string" &&
|
||||
!Buffer.isBuffer(path) &&
|
||||
!(path instanceof URL) &&
|
||||
typeof path !== "number"
|
||||
) {
|
||||
/** @type {Function} */
|
||||
(callback)(
|
||||
new TypeError("path must be a string, Buffer, URL or number")
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (options) {
|
||||
return /** @type {Function} */ (this._provider).call(
|
||||
this._providerContext,
|
||||
path,
|
||||
options,
|
||||
callback
|
||||
);
|
||||
}
|
||||
let callbacks = this._activeAsyncOperations.get(path);
|
||||
if (callbacks) {
|
||||
callbacks.push(callback);
|
||||
return;
|
||||
}
|
||||
this._activeAsyncOperations.set(path, (callbacks = [callback]));
|
||||
/** @type {Function} */
|
||||
(provider)(
|
||||
path,
|
||||
/**
|
||||
* @param {Error} err error
|
||||
* @param {any} result result
|
||||
*/
|
||||
(err, result) => {
|
||||
this._activeAsyncOperations.delete(path);
|
||||
runCallbacks(callbacks, err, result);
|
||||
}
|
||||
);
|
||||
}
|
||||
: null;
|
||||
this.provideSync = this._syncProvider
|
||||
? /**
|
||||
* @param {PathLike | PathOrFileDescriptor} path path
|
||||
* @param {object=} options options
|
||||
* @returns {any} result
|
||||
*/
|
||||
(path, options) => {
|
||||
return /** @type {Function} */ (this._syncProvider).call(
|
||||
this._providerContext,
|
||||
path,
|
||||
options
|
||||
);
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
purge() {}
|
||||
purgeParent() {}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
IDLE:
|
||||
insert data: goto SYNC
|
||||
|
||||
SYNC:
|
||||
before provide: run ticks
|
||||
event loop tick: goto ASYNC_ACTIVE
|
||||
|
||||
ASYNC:
|
||||
timeout: run tick, goto ASYNC_PASSIVE
|
||||
|
||||
ASYNC_PASSIVE:
|
||||
before provide: run ticks
|
||||
|
||||
IDLE --[insert data]--> SYNC --[event loop tick]--> ASYNC_ACTIVE --[interval tick]-> ASYNC_PASSIVE
|
||||
^ |
|
||||
+---------[insert data]-------+
|
||||
*/
|
||||
|
||||
const STORAGE_MODE_IDLE = 0;
|
||||
const STORAGE_MODE_SYNC = 1;
|
||||
const STORAGE_MODE_ASYNC = 2;
|
||||
|
||||
/**
|
||||
* @callback Provide
|
||||
* @param {PathLike | PathOrFileDescriptor} path path
|
||||
* @param {any} options options
|
||||
* @param {FileSystemCallback<any>} callback callback
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
class CacheBackend {
|
||||
/**
|
||||
* @param {number} duration max cache duration of items
|
||||
* @param {function | undefined} provider async method
|
||||
* @param {function | undefined} syncProvider sync method
|
||||
* @param {BaseFileSystem} providerContext call context for the provider methods
|
||||
*/
|
||||
constructor(duration, provider, syncProvider, providerContext) {
|
||||
this._duration = duration;
|
||||
this._provider = provider;
|
||||
this._syncProvider = syncProvider;
|
||||
this._providerContext = providerContext;
|
||||
/** @type {Map<string, FileSystemCallback<any>[]>} */
|
||||
this._activeAsyncOperations = new Map();
|
||||
/** @type {Map<string, { err: Error | null, result?: any, level: Set<string> }>} */
|
||||
this._data = new Map();
|
||||
/** @type {Set<string>[]} */
|
||||
this._levels = [];
|
||||
for (let i = 0; i < 10; i++) this._levels.push(new Set());
|
||||
for (let i = 5000; i < duration; i += 500) this._levels.push(new Set());
|
||||
this._currentLevel = 0;
|
||||
this._tickInterval = Math.floor(duration / this._levels.length);
|
||||
/** @type {STORAGE_MODE_IDLE | STORAGE_MODE_SYNC | STORAGE_MODE_ASYNC} */
|
||||
this._mode = STORAGE_MODE_IDLE;
|
||||
|
||||
/** @type {NodeJS.Timeout | undefined} */
|
||||
this._timeout = undefined;
|
||||
/** @type {number | undefined} */
|
||||
this._nextDecay = undefined;
|
||||
|
||||
// @ts-ignore
|
||||
this.provide = provider ? this.provide.bind(this) : null;
|
||||
// @ts-ignore
|
||||
this.provideSync = syncProvider ? this.provideSync.bind(this) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PathLike | PathOrFileDescriptor} path path
|
||||
* @param {any} options options
|
||||
* @param {FileSystemCallback<any>} callback callback
|
||||
* @returns {void}
|
||||
*/
|
||||
provide(path, options, callback) {
|
||||
if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
}
|
||||
if (
|
||||
typeof path !== "string" &&
|
||||
!Buffer.isBuffer(path) &&
|
||||
!(path instanceof URL) &&
|
||||
typeof path !== "number"
|
||||
) {
|
||||
callback(new TypeError("path must be a string, Buffer, URL or number"));
|
||||
return;
|
||||
}
|
||||
const strPath = typeof path !== "string" ? path.toString() : path;
|
||||
if (options) {
|
||||
return /** @type {Function} */ (this._provider).call(
|
||||
this._providerContext,
|
||||
path,
|
||||
options,
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
// When in sync mode we can move to async mode
|
||||
if (this._mode === STORAGE_MODE_SYNC) {
|
||||
this._enterAsyncMode();
|
||||
}
|
||||
|
||||
// Check in cache
|
||||
let cacheEntry = this._data.get(strPath);
|
||||
if (cacheEntry !== undefined) {
|
||||
if (cacheEntry.err) return nextTick(callback, cacheEntry.err);
|
||||
return nextTick(callback, null, cacheEntry.result);
|
||||
}
|
||||
|
||||
// Check if there is already the same operation running
|
||||
let callbacks = this._activeAsyncOperations.get(strPath);
|
||||
if (callbacks !== undefined) {
|
||||
callbacks.push(callback);
|
||||
return;
|
||||
}
|
||||
this._activeAsyncOperations.set(strPath, (callbacks = [callback]));
|
||||
|
||||
// Run the operation
|
||||
/** @type {Function} */
|
||||
(this._provider).call(
|
||||
this._providerContext,
|
||||
path,
|
||||
/**
|
||||
* @param {Error | null} err error
|
||||
* @param {any} [result] result
|
||||
*/
|
||||
(err, result) => {
|
||||
this._activeAsyncOperations.delete(strPath);
|
||||
this._storeResult(strPath, err, result);
|
||||
|
||||
// Enter async mode if not yet done
|
||||
this._enterAsyncMode();
|
||||
|
||||
runCallbacks(
|
||||
/** @type {FileSystemCallback<any>[]} */ (callbacks),
|
||||
err,
|
||||
result
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PathLike | PathOrFileDescriptor} path path
|
||||
* @param {any} options options
|
||||
* @returns {any} result
|
||||
*/
|
||||
provideSync(path, options) {
|
||||
if (
|
||||
typeof path !== "string" &&
|
||||
!Buffer.isBuffer(path) &&
|
||||
!(path instanceof URL) &&
|
||||
typeof path !== "number"
|
||||
) {
|
||||
throw new TypeError("path must be a string");
|
||||
}
|
||||
const strPath = typeof path !== "string" ? path.toString() : path;
|
||||
if (options) {
|
||||
return /** @type {Function} */ (this._syncProvider).call(
|
||||
this._providerContext,
|
||||
path,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
// In sync mode we may have to decay some cache items
|
||||
if (this._mode === STORAGE_MODE_SYNC) {
|
||||
this._runDecays();
|
||||
}
|
||||
|
||||
// Check in cache
|
||||
let cacheEntry = this._data.get(strPath);
|
||||
if (cacheEntry !== undefined) {
|
||||
if (cacheEntry.err) throw cacheEntry.err;
|
||||
return cacheEntry.result;
|
||||
}
|
||||
|
||||
// Get all active async operations
|
||||
// This sync operation will also complete them
|
||||
const callbacks = this._activeAsyncOperations.get(strPath);
|
||||
this._activeAsyncOperations.delete(strPath);
|
||||
|
||||
// Run the operation
|
||||
// When in idle mode, we will enter sync mode
|
||||
let result;
|
||||
try {
|
||||
result = /** @type {Function} */ (this._syncProvider).call(
|
||||
this._providerContext,
|
||||
path
|
||||
);
|
||||
} catch (err) {
|
||||
this._storeResult(strPath, /** @type {Error} */ (err), undefined);
|
||||
this._enterSyncModeWhenIdle();
|
||||
if (callbacks) {
|
||||
runCallbacks(callbacks, /** @type {Error} */ (err), undefined);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
this._storeResult(strPath, null, result);
|
||||
this._enterSyncModeWhenIdle();
|
||||
if (callbacks) {
|
||||
runCallbacks(callbacks, null, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | Buffer | URL | number | (string | URL | Buffer | number)[] | Set<string | URL | Buffer | number>} [what] what to purge
|
||||
*/
|
||||
purge(what) {
|
||||
if (!what) {
|
||||
if (this._mode !== STORAGE_MODE_IDLE) {
|
||||
this._data.clear();
|
||||
for (const level of this._levels) {
|
||||
level.clear();
|
||||
}
|
||||
this._enterIdleMode();
|
||||
}
|
||||
} else if (
|
||||
typeof what === "string" ||
|
||||
Buffer.isBuffer(what) ||
|
||||
what instanceof URL ||
|
||||
typeof what === "number"
|
||||
) {
|
||||
const strWhat = typeof what !== "string" ? what.toString() : what;
|
||||
for (let [key, data] of this._data) {
|
||||
if (key.startsWith(strWhat)) {
|
||||
this._data.delete(key);
|
||||
data.level.delete(key);
|
||||
}
|
||||
}
|
||||
if (this._data.size === 0) {
|
||||
this._enterIdleMode();
|
||||
}
|
||||
} else {
|
||||
for (let [key, data] of this._data) {
|
||||
for (const item of what) {
|
||||
const strItem = typeof item !== "string" ? item.toString() : item;
|
||||
if (key.startsWith(strItem)) {
|
||||
this._data.delete(key);
|
||||
data.level.delete(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this._data.size === 0) {
|
||||
this._enterIdleMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | Buffer | URL | number | (string | URL | Buffer | number)[] | Set<string | URL | Buffer | number>} [what] what to purge
|
||||
*/
|
||||
purgeParent(what) {
|
||||
if (!what) {
|
||||
this.purge();
|
||||
} else if (
|
||||
typeof what === "string" ||
|
||||
Buffer.isBuffer(what) ||
|
||||
what instanceof URL ||
|
||||
typeof what === "number"
|
||||
) {
|
||||
const strWhat = typeof what !== "string" ? what.toString() : what;
|
||||
this.purge(dirname(strWhat));
|
||||
} else {
|
||||
const set = new Set();
|
||||
for (const item of what) {
|
||||
const strItem = typeof item !== "string" ? item.toString() : item;
|
||||
set.add(dirname(strItem));
|
||||
}
|
||||
this.purge(set);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path path
|
||||
* @param {Error | null} err error
|
||||
* @param {any} result result
|
||||
*/
|
||||
_storeResult(path, err, result) {
|
||||
if (this._data.has(path)) return;
|
||||
const level = this._levels[this._currentLevel];
|
||||
this._data.set(path, { err, result, level });
|
||||
level.add(path);
|
||||
}
|
||||
|
||||
_decayLevel() {
|
||||
const nextLevel = (this._currentLevel + 1) % this._levels.length;
|
||||
const decay = this._levels[nextLevel];
|
||||
this._currentLevel = nextLevel;
|
||||
for (let item of decay) {
|
||||
this._data.delete(item);
|
||||
}
|
||||
decay.clear();
|
||||
if (this._data.size === 0) {
|
||||
this._enterIdleMode();
|
||||
} else {
|
||||
/** @type {number} */
|
||||
(this._nextDecay) += this._tickInterval;
|
||||
}
|
||||
}
|
||||
|
||||
_runDecays() {
|
||||
while (
|
||||
/** @type {number} */ (this._nextDecay) <= Date.now() &&
|
||||
this._mode !== STORAGE_MODE_IDLE
|
||||
) {
|
||||
this._decayLevel();
|
||||
}
|
||||
}
|
||||
|
||||
_enterAsyncMode() {
|
||||
let timeout = 0;
|
||||
switch (this._mode) {
|
||||
case STORAGE_MODE_ASYNC:
|
||||
return;
|
||||
case STORAGE_MODE_IDLE:
|
||||
this._nextDecay = Date.now() + this._tickInterval;
|
||||
timeout = this._tickInterval;
|
||||
break;
|
||||
case STORAGE_MODE_SYNC:
|
||||
this._runDecays();
|
||||
// _runDecays may change the mode
|
||||
if (
|
||||
/** @type {STORAGE_MODE_IDLE | STORAGE_MODE_SYNC | STORAGE_MODE_ASYNC}*/
|
||||
(this._mode) === STORAGE_MODE_IDLE
|
||||
)
|
||||
return;
|
||||
timeout = Math.max(
|
||||
0,
|
||||
/** @type {number} */ (this._nextDecay) - Date.now()
|
||||
);
|
||||
break;
|
||||
}
|
||||
this._mode = STORAGE_MODE_ASYNC;
|
||||
const ref = setTimeout(() => {
|
||||
this._mode = STORAGE_MODE_SYNC;
|
||||
this._runDecays();
|
||||
}, timeout);
|
||||
if (ref.unref) ref.unref();
|
||||
this._timeout = ref;
|
||||
}
|
||||
|
||||
_enterSyncModeWhenIdle() {
|
||||
if (this._mode === STORAGE_MODE_IDLE) {
|
||||
this._mode = STORAGE_MODE_SYNC;
|
||||
this._nextDecay = Date.now() + this._tickInterval;
|
||||
}
|
||||
}
|
||||
|
||||
_enterIdleMode() {
|
||||
this._mode = STORAGE_MODE_IDLE;
|
||||
this._nextDecay = undefined;
|
||||
if (this._timeout) clearTimeout(this._timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {function} Provider
|
||||
* @template {function} AsyncProvider
|
||||
* @template FileSystem
|
||||
* @param {number} duration duration in ms files are cached
|
||||
* @param {Provider | undefined} provider provider
|
||||
* @param {AsyncProvider | undefined} syncProvider sync provider
|
||||
* @param {BaseFileSystem} providerContext provider context
|
||||
* @returns {OperationMergerBackend | CacheBackend} backend
|
||||
*/
|
||||
const createBackend = (duration, provider, syncProvider, providerContext) => {
|
||||
if (duration > 0) {
|
||||
return new CacheBackend(duration, provider, syncProvider, providerContext);
|
||||
}
|
||||
return new OperationMergerBackend(provider, syncProvider, providerContext);
|
||||
};
|
||||
|
||||
module.exports = class CachedInputFileSystem {
|
||||
/**
|
||||
* @param {BaseFileSystem} fileSystem file system
|
||||
* @param {number} duration duration in ms files are cached
|
||||
*/
|
||||
constructor(fileSystem, duration) {
|
||||
this.fileSystem = fileSystem;
|
||||
|
||||
this._lstatBackend = createBackend(
|
||||
duration,
|
||||
this.fileSystem.lstat,
|
||||
this.fileSystem.lstatSync,
|
||||
this.fileSystem
|
||||
);
|
||||
const lstat = this._lstatBackend.provide;
|
||||
this.lstat = /** @type {FileSystem["lstat"]} */ (lstat);
|
||||
const lstatSync = this._lstatBackend.provideSync;
|
||||
this.lstatSync = /** @type {SyncFileSystem["lstatSync"]} */ (lstatSync);
|
||||
|
||||
this._statBackend = createBackend(
|
||||
duration,
|
||||
this.fileSystem.stat,
|
||||
this.fileSystem.statSync,
|
||||
this.fileSystem
|
||||
);
|
||||
const stat = this._statBackend.provide;
|
||||
this.stat = /** @type {FileSystem["stat"]} */ (stat);
|
||||
const statSync = this._statBackend.provideSync;
|
||||
this.statSync = /** @type {SyncFileSystem["statSync"]} */ (statSync);
|
||||
|
||||
this._readdirBackend = createBackend(
|
||||
duration,
|
||||
this.fileSystem.readdir,
|
||||
this.fileSystem.readdirSync,
|
||||
this.fileSystem
|
||||
);
|
||||
const readdir = this._readdirBackend.provide;
|
||||
this.readdir = /** @type {FileSystem["readdir"]} */ (readdir);
|
||||
const readdirSync = this._readdirBackend.provideSync;
|
||||
this.readdirSync = /** @type {SyncFileSystem["readdirSync"]} */ (
|
||||
readdirSync
|
||||
);
|
||||
|
||||
this._readFileBackend = createBackend(
|
||||
duration,
|
||||
this.fileSystem.readFile,
|
||||
this.fileSystem.readFileSync,
|
||||
this.fileSystem
|
||||
);
|
||||
const readFile = this._readFileBackend.provide;
|
||||
this.readFile = /** @type {FileSystem["readFile"]} */ (readFile);
|
||||
const readFileSync = this._readFileBackend.provideSync;
|
||||
this.readFileSync = /** @type {SyncFileSystem["readFileSync"]} */ (
|
||||
readFileSync
|
||||
);
|
||||
|
||||
this._readJsonBackend = createBackend(
|
||||
duration,
|
||||
// prettier-ignore
|
||||
this.fileSystem.readJson ||
|
||||
(this.readFile &&
|
||||
(
|
||||
/**
|
||||
* @param {string} path path
|
||||
* @param {FileSystemCallback<any>} callback
|
||||
*/
|
||||
(path, callback) => {
|
||||
this.readFile(path, (err, buffer) => {
|
||||
if (err) return callback(err);
|
||||
if (!buffer || buffer.length === 0)
|
||||
return callback(new Error("No file content"));
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(buffer.toString("utf-8"));
|
||||
} catch (e) {
|
||||
return callback(/** @type {Error} */ (e));
|
||||
}
|
||||
callback(null, data);
|
||||
});
|
||||
})
|
||||
),
|
||||
// prettier-ignore
|
||||
this.fileSystem.readJsonSync ||
|
||||
(this.readFileSync &&
|
||||
(
|
||||
/**
|
||||
* @param {string} path path
|
||||
* @returns {any} result
|
||||
*/
|
||||
(path) => {
|
||||
const buffer = this.readFileSync(path);
|
||||
const data = JSON.parse(buffer.toString("utf-8"));
|
||||
return data;
|
||||
}
|
||||
)),
|
||||
this.fileSystem
|
||||
);
|
||||
const readJson = this._readJsonBackend.provide;
|
||||
this.readJson = /** @type {FileSystem["readJson"]} */ (readJson);
|
||||
const readJsonSync = this._readJsonBackend.provideSync;
|
||||
this.readJsonSync = /** @type {SyncFileSystem["readJsonSync"]} */ (
|
||||
readJsonSync
|
||||
);
|
||||
|
||||
this._readlinkBackend = createBackend(
|
||||
duration,
|
||||
this.fileSystem.readlink,
|
||||
this.fileSystem.readlinkSync,
|
||||
this.fileSystem
|
||||
);
|
||||
const readlink = this._readlinkBackend.provide;
|
||||
this.readlink = /** @type {FileSystem["readlink"]} */ (readlink);
|
||||
const readlinkSync = this._readlinkBackend.provideSync;
|
||||
this.readlinkSync = /** @type {SyncFileSystem["readlinkSync"]} */ (
|
||||
readlinkSync
|
||||
);
|
||||
|
||||
this._realpathBackend = createBackend(
|
||||
duration,
|
||||
this.fileSystem.realpath,
|
||||
this.fileSystem.realpathSync,
|
||||
this.fileSystem
|
||||
);
|
||||
const realpath = this._realpathBackend.provide;
|
||||
this.realpath = /** @type {FileSystem["realpath"]} */ (realpath);
|
||||
const realpathSync = this._realpathBackend.provideSync;
|
||||
this.realpathSync = /** @type {SyncFileSystem["realpathSync"]} */ (
|
||||
realpathSync
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | Buffer | URL | number | (string | URL | Buffer | number)[] | Set<string | URL | Buffer | number>} [what] what to purge
|
||||
*/
|
||||
purge(what) {
|
||||
this._statBackend.purge(what);
|
||||
this._lstatBackend.purge(what);
|
||||
this._readdirBackend.purgeParent(what);
|
||||
this._readFileBackend.purge(what);
|
||||
this._readlinkBackend.purge(what);
|
||||
this._readJsonBackend.purge(what);
|
||||
this._realpathBackend.purge(what);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag assignment in a conditional statement's test expression
|
||||
* @author Stephen Murray <spmurrayzzz>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const TEST_CONDITION_PARENT_TYPES = new Set([
|
||||
"IfStatement",
|
||||
"WhileStatement",
|
||||
"DoWhileStatement",
|
||||
"ForStatement",
|
||||
"ConditionalExpression",
|
||||
]);
|
||||
|
||||
const NODE_DESCRIPTIONS = {
|
||||
DoWhileStatement: "a 'do...while' statement",
|
||||
ForStatement: "a 'for' statement",
|
||||
IfStatement: "an 'if' statement",
|
||||
WhileStatement: "a 'while' statement",
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "problem",
|
||||
|
||||
defaultOptions: ["except-parens"],
|
||||
|
||||
docs: {
|
||||
description:
|
||||
"Disallow assignment operators in conditional expressions",
|
||||
recommended: true,
|
||||
url: "https://eslint.org/docs/latest/rules/no-cond-assign",
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["except-parens", "always"],
|
||||
},
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpected: "Unexpected assignment within {{type}}.",
|
||||
|
||||
// must match JSHint's error message
|
||||
missing:
|
||||
"Expected a conditional expression and instead saw an assignment.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const [prohibitAssign] = context.options;
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Check whether an AST node is the test expression for a conditional statement.
|
||||
* @param {!Object} node The node to test.
|
||||
* @returns {boolean} `true` if the node is the text expression for a conditional statement; otherwise, `false`.
|
||||
*/
|
||||
function isConditionalTestExpression(node) {
|
||||
return (
|
||||
node.parent &&
|
||||
TEST_CONDITION_PARENT_TYPES.has(node.parent.type) &&
|
||||
node === node.parent.test
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an AST node, perform a bottom-up search for the first ancestor that represents a conditional statement.
|
||||
* @param {!Object} node The node to use at the start of the search.
|
||||
* @returns {?Object} The closest ancestor node that represents a conditional statement.
|
||||
*/
|
||||
function findConditionalAncestor(node) {
|
||||
let currentAncestor = node;
|
||||
|
||||
do {
|
||||
if (isConditionalTestExpression(currentAncestor)) {
|
||||
return currentAncestor.parent;
|
||||
}
|
||||
} while (
|
||||
(currentAncestor = currentAncestor.parent) &&
|
||||
!astUtils.isFunction(currentAncestor)
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the code represented by an AST node is enclosed in two sets of parentheses.
|
||||
* @param {!Object} node The node to test.
|
||||
* @returns {boolean} `true` if the code is enclosed in two sets of parentheses; otherwise, `false`.
|
||||
*/
|
||||
function isParenthesisedTwice(node) {
|
||||
const previousToken = sourceCode.getTokenBefore(node, 1),
|
||||
nextToken = sourceCode.getTokenAfter(node, 1);
|
||||
|
||||
return (
|
||||
astUtils.isParenthesised(sourceCode, node) &&
|
||||
previousToken &&
|
||||
astUtils.isOpeningParenToken(previousToken) &&
|
||||
previousToken.range[1] <= node.range[0] &&
|
||||
astUtils.isClosingParenToken(nextToken) &&
|
||||
nextToken.range[0] >= node.range[1]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a conditional statement's test expression for top-level assignments that are not enclosed in parentheses.
|
||||
* @param {!Object} node The node for the conditional statement.
|
||||
* @returns {void}
|
||||
*/
|
||||
function testForAssign(node) {
|
||||
if (
|
||||
node.test &&
|
||||
node.test.type === "AssignmentExpression" &&
|
||||
(node.type === "ForStatement"
|
||||
? !astUtils.isParenthesised(sourceCode, node.test)
|
||||
: !isParenthesisedTwice(node.test))
|
||||
) {
|
||||
context.report({
|
||||
node: node.test,
|
||||
messageId: "missing",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether an assignment expression is descended from a conditional statement's test expression.
|
||||
* @param {!Object} node The node for the assignment expression.
|
||||
* @returns {void}
|
||||
*/
|
||||
function testForConditionalAncestor(node) {
|
||||
const ancestor = findConditionalAncestor(node);
|
||||
|
||||
if (ancestor) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpected",
|
||||
data: {
|
||||
type: NODE_DESCRIPTIONS[ancestor.type] || ancestor.type,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (prohibitAssign === "always") {
|
||||
return {
|
||||
AssignmentExpression: testForConditionalAncestor,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
DoWhileStatement: testForAssign,
|
||||
ForStatement: testForAssign,
|
||||
IfStatement: testForAssign,
|
||||
WhileStatement: testForAssign,
|
||||
ConditionalExpression: testForAssign,
|
||||
};
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user