WIP: bootstrap and partial real Solana watcher implementation
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag when IIFE is not wrapped in parens
|
||||
* @author Ilya Volodin
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
const eslintUtils = require("@eslint-community/eslint-utils");
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Helpers
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Check if the given node is callee of a `NewExpression` node
|
||||
* @param {ASTNode} node node to check
|
||||
* @returns {boolean} True if the node is callee of a `NewExpression` node
|
||||
* @private
|
||||
*/
|
||||
function isCalleeOfNewExpression(node) {
|
||||
const maybeCallee =
|
||||
node.parent.type === "ChainExpression" ? node.parent : node;
|
||||
|
||||
return (
|
||||
maybeCallee.parent.type === "NewExpression" &&
|
||||
maybeCallee.parent.callee === maybeCallee
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: {
|
||||
message: "Formatting rules are being moved out of ESLint core.",
|
||||
url: "https://eslint.org/blog/2023/10/deprecating-formatting-rules/",
|
||||
deprecatedSince: "8.53.0",
|
||||
availableUntil: "11.0.0",
|
||||
replacedBy: [
|
||||
{
|
||||
message:
|
||||
"ESLint Stylistic now maintains deprecated stylistic core rules.",
|
||||
url: "https://eslint.style/guide/migration",
|
||||
plugin: {
|
||||
name: "@stylistic/eslint-plugin",
|
||||
url: "https://eslint.style",
|
||||
},
|
||||
rule: {
|
||||
name: "wrap-iife",
|
||||
url: "https://eslint.style/rules/wrap-iife",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description:
|
||||
"Require parentheses around immediate `function` invocations",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/wrap-iife",
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["outside", "inside", "any"],
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
functionPrototypeMethods: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
|
||||
fixable: "code",
|
||||
messages: {
|
||||
wrapInvocation:
|
||||
"Wrap an immediate function invocation in parentheses.",
|
||||
wrapExpression: "Wrap only the function expression in parens.",
|
||||
moveInvocation:
|
||||
"Move the invocation into the parens that contain the function.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const style = context.options[0] || "outside";
|
||||
const includeFunctionPrototypeMethods =
|
||||
context.options[1] && context.options[1].functionPrototypeMethods;
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Check if the node is wrapped in any (). All parens count: grouping parens and parens for constructs such as if()
|
||||
* @param {ASTNode} node node to evaluate
|
||||
* @returns {boolean} True if it is wrapped in any parens
|
||||
* @private
|
||||
*/
|
||||
function isWrappedInAnyParens(node) {
|
||||
return astUtils.isParenthesised(sourceCode, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the node is wrapped in grouping (). Parens for constructs such as if() don't count
|
||||
* @param {ASTNode} node node to evaluate
|
||||
* @returns {boolean} True if it is wrapped in grouping parens
|
||||
* @private
|
||||
*/
|
||||
function isWrappedInGroupingParens(node) {
|
||||
return eslintUtils.isParenthesized(1, node, sourceCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the function node from an IIFE
|
||||
* @param {ASTNode} node node to evaluate
|
||||
* @returns {ASTNode} node that is the function expression of the given IIFE, or null if none exist
|
||||
*/
|
||||
function getFunctionNodeFromIIFE(node) {
|
||||
const callee = astUtils.skipChainExpression(node.callee);
|
||||
|
||||
if (callee.type === "FunctionExpression") {
|
||||
return callee;
|
||||
}
|
||||
|
||||
if (
|
||||
includeFunctionPrototypeMethods &&
|
||||
callee.type === "MemberExpression" &&
|
||||
callee.object.type === "FunctionExpression" &&
|
||||
(astUtils.getStaticPropertyName(callee) === "call" ||
|
||||
astUtils.getStaticPropertyName(callee) === "apply")
|
||||
) {
|
||||
return callee.object;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
const innerNode = getFunctionNodeFromIIFE(node);
|
||||
|
||||
if (!innerNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isCallExpressionWrapped = isWrappedInAnyParens(node),
|
||||
isFunctionExpressionWrapped =
|
||||
isWrappedInAnyParens(innerNode);
|
||||
|
||||
if (!isCallExpressionWrapped && !isFunctionExpressionWrapped) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "wrapInvocation",
|
||||
fix(fixer) {
|
||||
const nodeToSurround =
|
||||
style === "inside" ? innerNode : node;
|
||||
|
||||
return fixer.replaceText(
|
||||
nodeToSurround,
|
||||
`(${sourceCode.getText(nodeToSurround)})`,
|
||||
);
|
||||
},
|
||||
});
|
||||
} else if (style === "inside" && !isFunctionExpressionWrapped) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "wrapExpression",
|
||||
fix(fixer) {
|
||||
// The outer call expression will always be wrapped at this point.
|
||||
|
||||
if (
|
||||
isWrappedInGroupingParens(node) &&
|
||||
!isCalleeOfNewExpression(node)
|
||||
) {
|
||||
/*
|
||||
* Parenthesize the function expression and remove unnecessary grouping parens around the call expression.
|
||||
* Replace the range between the end of the function expression and the end of the call expression.
|
||||
* for example, in `(function(foo) {}(bar))`, the range `(bar))` should get replaced with `)(bar)`.
|
||||
*/
|
||||
|
||||
const parenAfter =
|
||||
sourceCode.getTokenAfter(node);
|
||||
|
||||
return fixer.replaceTextRange(
|
||||
[innerNode.range[1], parenAfter.range[1]],
|
||||
`)${sourceCode.getText().slice(innerNode.range[1], parenAfter.range[0])}`,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call expression is wrapped in mandatory parens such as if(), or in necessary grouping parens.
|
||||
* These parens cannot be removed, so just parenthesize the function expression.
|
||||
*/
|
||||
|
||||
return fixer.replaceText(
|
||||
innerNode,
|
||||
`(${sourceCode.getText(innerNode)})`,
|
||||
);
|
||||
},
|
||||
});
|
||||
} else if (style === "outside" && !isCallExpressionWrapped) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "moveInvocation",
|
||||
fix(fixer) {
|
||||
/*
|
||||
* The inner function expression will always be wrapped at this point.
|
||||
* It's only necessary to replace the range between the end of the function expression
|
||||
* and the call expression. For example, in `(function(foo) {})(bar)`, the range `)(bar)`
|
||||
* should get replaced with `(bar))`.
|
||||
*/
|
||||
const parenAfter =
|
||||
sourceCode.getTokenAfter(innerNode);
|
||||
|
||||
return fixer.replaceTextRange(
|
||||
[parenAfter.range[0], node.range[1]],
|
||||
`${sourceCode.getText().slice(parenAfter.range[1], node.range[1])})`,
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,464 @@
|
||||
(function (global, factory) {
|
||||
if (typeof exports === 'object' && typeof module !== 'undefined') {
|
||||
factory(module);
|
||||
module.exports = def(module);
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
define(['module'], function(mod) {
|
||||
factory.apply(this, arguments);
|
||||
mod.exports = def(mod);
|
||||
});
|
||||
} else {
|
||||
const mod = { exports: {} };
|
||||
factory(mod);
|
||||
global = typeof globalThis !== 'undefined' ? globalThis : global || self;
|
||||
global.sourcemapCodec = def(mod);
|
||||
}
|
||||
function def(m) { return 'default' in m.exports ? m.exports.default : m.exports; }
|
||||
})(this, (function (module) {
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/sourcemap-codec.ts
|
||||
var sourcemap_codec_exports = {};
|
||||
__export(sourcemap_codec_exports, {
|
||||
decode: () => decode,
|
||||
decodeGeneratedRanges: () => decodeGeneratedRanges,
|
||||
decodeOriginalScopes: () => decodeOriginalScopes,
|
||||
encode: () => encode,
|
||||
encodeGeneratedRanges: () => encodeGeneratedRanges,
|
||||
encodeOriginalScopes: () => encodeOriginalScopes
|
||||
});
|
||||
module.exports = __toCommonJS(sourcemap_codec_exports);
|
||||
|
||||
// src/vlq.ts
|
||||
var comma = ",".charCodeAt(0);
|
||||
var semicolon = ";".charCodeAt(0);
|
||||
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var intToChar = new Uint8Array(64);
|
||||
var charToInt = new Uint8Array(128);
|
||||
for (let i = 0; i < chars.length; i++) {
|
||||
const c = chars.charCodeAt(i);
|
||||
intToChar[i] = c;
|
||||
charToInt[c] = i;
|
||||
}
|
||||
function decodeInteger(reader, relative) {
|
||||
let value = 0;
|
||||
let shift = 0;
|
||||
let integer = 0;
|
||||
do {
|
||||
const c = reader.next();
|
||||
integer = charToInt[c];
|
||||
value |= (integer & 31) << shift;
|
||||
shift += 5;
|
||||
} while (integer & 32);
|
||||
const shouldNegate = value & 1;
|
||||
value >>>= 1;
|
||||
if (shouldNegate) {
|
||||
value = -2147483648 | -value;
|
||||
}
|
||||
return relative + value;
|
||||
}
|
||||
function encodeInteger(builder, num, relative) {
|
||||
let delta = num - relative;
|
||||
delta = delta < 0 ? -delta << 1 | 1 : delta << 1;
|
||||
do {
|
||||
let clamped = delta & 31;
|
||||
delta >>>= 5;
|
||||
if (delta > 0) clamped |= 32;
|
||||
builder.write(intToChar[clamped]);
|
||||
} while (delta > 0);
|
||||
return num;
|
||||
}
|
||||
function hasMoreVlq(reader, max) {
|
||||
if (reader.pos >= max) return false;
|
||||
return reader.peek() !== comma;
|
||||
}
|
||||
|
||||
// src/strings.ts
|
||||
var bufLength = 1024 * 16;
|
||||
var td = typeof TextDecoder !== "undefined" ? /* @__PURE__ */ new TextDecoder() : typeof Buffer !== "undefined" ? {
|
||||
decode(buf) {
|
||||
const out = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength);
|
||||
return out.toString();
|
||||
}
|
||||
} : {
|
||||
decode(buf) {
|
||||
let out = "";
|
||||
for (let i = 0; i < buf.length; i++) {
|
||||
out += String.fromCharCode(buf[i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
var StringWriter = class {
|
||||
constructor() {
|
||||
this.pos = 0;
|
||||
this.out = "";
|
||||
this.buffer = new Uint8Array(bufLength);
|
||||
}
|
||||
write(v) {
|
||||
const { buffer } = this;
|
||||
buffer[this.pos++] = v;
|
||||
if (this.pos === bufLength) {
|
||||
this.out += td.decode(buffer);
|
||||
this.pos = 0;
|
||||
}
|
||||
}
|
||||
flush() {
|
||||
const { buffer, out, pos } = this;
|
||||
return pos > 0 ? out + td.decode(buffer.subarray(0, pos)) : out;
|
||||
}
|
||||
};
|
||||
var StringReader = class {
|
||||
constructor(buffer) {
|
||||
this.pos = 0;
|
||||
this.buffer = buffer;
|
||||
}
|
||||
next() {
|
||||
return this.buffer.charCodeAt(this.pos++);
|
||||
}
|
||||
peek() {
|
||||
return this.buffer.charCodeAt(this.pos);
|
||||
}
|
||||
indexOf(char) {
|
||||
const { buffer, pos } = this;
|
||||
const idx = buffer.indexOf(char, pos);
|
||||
return idx === -1 ? buffer.length : idx;
|
||||
}
|
||||
};
|
||||
|
||||
// src/scopes.ts
|
||||
var EMPTY = [];
|
||||
function decodeOriginalScopes(input) {
|
||||
const { length } = input;
|
||||
const reader = new StringReader(input);
|
||||
const scopes = [];
|
||||
const stack = [];
|
||||
let line = 0;
|
||||
for (; reader.pos < length; reader.pos++) {
|
||||
line = decodeInteger(reader, line);
|
||||
const column = decodeInteger(reader, 0);
|
||||
if (!hasMoreVlq(reader, length)) {
|
||||
const last = stack.pop();
|
||||
last[2] = line;
|
||||
last[3] = column;
|
||||
continue;
|
||||
}
|
||||
const kind = decodeInteger(reader, 0);
|
||||
const fields = decodeInteger(reader, 0);
|
||||
const hasName = fields & 1;
|
||||
const scope = hasName ? [line, column, 0, 0, kind, decodeInteger(reader, 0)] : [line, column, 0, 0, kind];
|
||||
let vars = EMPTY;
|
||||
if (hasMoreVlq(reader, length)) {
|
||||
vars = [];
|
||||
do {
|
||||
const varsIndex = decodeInteger(reader, 0);
|
||||
vars.push(varsIndex);
|
||||
} while (hasMoreVlq(reader, length));
|
||||
}
|
||||
scope.vars = vars;
|
||||
scopes.push(scope);
|
||||
stack.push(scope);
|
||||
}
|
||||
return scopes;
|
||||
}
|
||||
function encodeOriginalScopes(scopes) {
|
||||
const writer = new StringWriter();
|
||||
for (let i = 0; i < scopes.length; ) {
|
||||
i = _encodeOriginalScopes(scopes, i, writer, [0]);
|
||||
}
|
||||
return writer.flush();
|
||||
}
|
||||
function _encodeOriginalScopes(scopes, index, writer, state) {
|
||||
const scope = scopes[index];
|
||||
const { 0: startLine, 1: startColumn, 2: endLine, 3: endColumn, 4: kind, vars } = scope;
|
||||
if (index > 0) writer.write(comma);
|
||||
state[0] = encodeInteger(writer, startLine, state[0]);
|
||||
encodeInteger(writer, startColumn, 0);
|
||||
encodeInteger(writer, kind, 0);
|
||||
const fields = scope.length === 6 ? 1 : 0;
|
||||
encodeInteger(writer, fields, 0);
|
||||
if (scope.length === 6) encodeInteger(writer, scope[5], 0);
|
||||
for (const v of vars) {
|
||||
encodeInteger(writer, v, 0);
|
||||
}
|
||||
for (index++; index < scopes.length; ) {
|
||||
const next = scopes[index];
|
||||
const { 0: l, 1: c } = next;
|
||||
if (l > endLine || l === endLine && c >= endColumn) {
|
||||
break;
|
||||
}
|
||||
index = _encodeOriginalScopes(scopes, index, writer, state);
|
||||
}
|
||||
writer.write(comma);
|
||||
state[0] = encodeInteger(writer, endLine, state[0]);
|
||||
encodeInteger(writer, endColumn, 0);
|
||||
return index;
|
||||
}
|
||||
function decodeGeneratedRanges(input) {
|
||||
const { length } = input;
|
||||
const reader = new StringReader(input);
|
||||
const ranges = [];
|
||||
const stack = [];
|
||||
let genLine = 0;
|
||||
let definitionSourcesIndex = 0;
|
||||
let definitionScopeIndex = 0;
|
||||
let callsiteSourcesIndex = 0;
|
||||
let callsiteLine = 0;
|
||||
let callsiteColumn = 0;
|
||||
let bindingLine = 0;
|
||||
let bindingColumn = 0;
|
||||
do {
|
||||
const semi = reader.indexOf(";");
|
||||
let genColumn = 0;
|
||||
for (; reader.pos < semi; reader.pos++) {
|
||||
genColumn = decodeInteger(reader, genColumn);
|
||||
if (!hasMoreVlq(reader, semi)) {
|
||||
const last = stack.pop();
|
||||
last[2] = genLine;
|
||||
last[3] = genColumn;
|
||||
continue;
|
||||
}
|
||||
const fields = decodeInteger(reader, 0);
|
||||
const hasDefinition = fields & 1;
|
||||
const hasCallsite = fields & 2;
|
||||
const hasScope = fields & 4;
|
||||
let callsite = null;
|
||||
let bindings = EMPTY;
|
||||
let range;
|
||||
if (hasDefinition) {
|
||||
const defSourcesIndex = decodeInteger(reader, definitionSourcesIndex);
|
||||
definitionScopeIndex = decodeInteger(
|
||||
reader,
|
||||
definitionSourcesIndex === defSourcesIndex ? definitionScopeIndex : 0
|
||||
);
|
||||
definitionSourcesIndex = defSourcesIndex;
|
||||
range = [genLine, genColumn, 0, 0, defSourcesIndex, definitionScopeIndex];
|
||||
} else {
|
||||
range = [genLine, genColumn, 0, 0];
|
||||
}
|
||||
range.isScope = !!hasScope;
|
||||
if (hasCallsite) {
|
||||
const prevCsi = callsiteSourcesIndex;
|
||||
const prevLine = callsiteLine;
|
||||
callsiteSourcesIndex = decodeInteger(reader, callsiteSourcesIndex);
|
||||
const sameSource = prevCsi === callsiteSourcesIndex;
|
||||
callsiteLine = decodeInteger(reader, sameSource ? callsiteLine : 0);
|
||||
callsiteColumn = decodeInteger(
|
||||
reader,
|
||||
sameSource && prevLine === callsiteLine ? callsiteColumn : 0
|
||||
);
|
||||
callsite = [callsiteSourcesIndex, callsiteLine, callsiteColumn];
|
||||
}
|
||||
range.callsite = callsite;
|
||||
if (hasMoreVlq(reader, semi)) {
|
||||
bindings = [];
|
||||
do {
|
||||
bindingLine = genLine;
|
||||
bindingColumn = genColumn;
|
||||
const expressionsCount = decodeInteger(reader, 0);
|
||||
let expressionRanges;
|
||||
if (expressionsCount < -1) {
|
||||
expressionRanges = [[decodeInteger(reader, 0)]];
|
||||
for (let i = -1; i > expressionsCount; i--) {
|
||||
const prevBl = bindingLine;
|
||||
bindingLine = decodeInteger(reader, bindingLine);
|
||||
bindingColumn = decodeInteger(reader, bindingLine === prevBl ? bindingColumn : 0);
|
||||
const expression = decodeInteger(reader, 0);
|
||||
expressionRanges.push([expression, bindingLine, bindingColumn]);
|
||||
}
|
||||
} else {
|
||||
expressionRanges = [[expressionsCount]];
|
||||
}
|
||||
bindings.push(expressionRanges);
|
||||
} while (hasMoreVlq(reader, semi));
|
||||
}
|
||||
range.bindings = bindings;
|
||||
ranges.push(range);
|
||||
stack.push(range);
|
||||
}
|
||||
genLine++;
|
||||
reader.pos = semi + 1;
|
||||
} while (reader.pos < length);
|
||||
return ranges;
|
||||
}
|
||||
function encodeGeneratedRanges(ranges) {
|
||||
if (ranges.length === 0) return "";
|
||||
const writer = new StringWriter();
|
||||
for (let i = 0; i < ranges.length; ) {
|
||||
i = _encodeGeneratedRanges(ranges, i, writer, [0, 0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
return writer.flush();
|
||||
}
|
||||
function _encodeGeneratedRanges(ranges, index, writer, state) {
|
||||
const range = ranges[index];
|
||||
const {
|
||||
0: startLine,
|
||||
1: startColumn,
|
||||
2: endLine,
|
||||
3: endColumn,
|
||||
isScope,
|
||||
callsite,
|
||||
bindings
|
||||
} = range;
|
||||
if (state[0] < startLine) {
|
||||
catchupLine(writer, state[0], startLine);
|
||||
state[0] = startLine;
|
||||
state[1] = 0;
|
||||
} else if (index > 0) {
|
||||
writer.write(comma);
|
||||
}
|
||||
state[1] = encodeInteger(writer, range[1], state[1]);
|
||||
const fields = (range.length === 6 ? 1 : 0) | (callsite ? 2 : 0) | (isScope ? 4 : 0);
|
||||
encodeInteger(writer, fields, 0);
|
||||
if (range.length === 6) {
|
||||
const { 4: sourcesIndex, 5: scopesIndex } = range;
|
||||
if (sourcesIndex !== state[2]) {
|
||||
state[3] = 0;
|
||||
}
|
||||
state[2] = encodeInteger(writer, sourcesIndex, state[2]);
|
||||
state[3] = encodeInteger(writer, scopesIndex, state[3]);
|
||||
}
|
||||
if (callsite) {
|
||||
const { 0: sourcesIndex, 1: callLine, 2: callColumn } = range.callsite;
|
||||
if (sourcesIndex !== state[4]) {
|
||||
state[5] = 0;
|
||||
state[6] = 0;
|
||||
} else if (callLine !== state[5]) {
|
||||
state[6] = 0;
|
||||
}
|
||||
state[4] = encodeInteger(writer, sourcesIndex, state[4]);
|
||||
state[5] = encodeInteger(writer, callLine, state[5]);
|
||||
state[6] = encodeInteger(writer, callColumn, state[6]);
|
||||
}
|
||||
if (bindings) {
|
||||
for (const binding of bindings) {
|
||||
if (binding.length > 1) encodeInteger(writer, -binding.length, 0);
|
||||
const expression = binding[0][0];
|
||||
encodeInteger(writer, expression, 0);
|
||||
let bindingStartLine = startLine;
|
||||
let bindingStartColumn = startColumn;
|
||||
for (let i = 1; i < binding.length; i++) {
|
||||
const expRange = binding[i];
|
||||
bindingStartLine = encodeInteger(writer, expRange[1], bindingStartLine);
|
||||
bindingStartColumn = encodeInteger(writer, expRange[2], bindingStartColumn);
|
||||
encodeInteger(writer, expRange[0], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (index++; index < ranges.length; ) {
|
||||
const next = ranges[index];
|
||||
const { 0: l, 1: c } = next;
|
||||
if (l > endLine || l === endLine && c >= endColumn) {
|
||||
break;
|
||||
}
|
||||
index = _encodeGeneratedRanges(ranges, index, writer, state);
|
||||
}
|
||||
if (state[0] < endLine) {
|
||||
catchupLine(writer, state[0], endLine);
|
||||
state[0] = endLine;
|
||||
state[1] = 0;
|
||||
} else {
|
||||
writer.write(comma);
|
||||
}
|
||||
state[1] = encodeInteger(writer, endColumn, state[1]);
|
||||
return index;
|
||||
}
|
||||
function catchupLine(writer, lastLine, line) {
|
||||
do {
|
||||
writer.write(semicolon);
|
||||
} while (++lastLine < line);
|
||||
}
|
||||
|
||||
// src/sourcemap-codec.ts
|
||||
function decode(mappings) {
|
||||
const { length } = mappings;
|
||||
const reader = new StringReader(mappings);
|
||||
const decoded = [];
|
||||
let genColumn = 0;
|
||||
let sourcesIndex = 0;
|
||||
let sourceLine = 0;
|
||||
let sourceColumn = 0;
|
||||
let namesIndex = 0;
|
||||
do {
|
||||
const semi = reader.indexOf(";");
|
||||
const line = [];
|
||||
let sorted = true;
|
||||
let lastCol = 0;
|
||||
genColumn = 0;
|
||||
while (reader.pos < semi) {
|
||||
let seg;
|
||||
genColumn = decodeInteger(reader, genColumn);
|
||||
if (genColumn < lastCol) sorted = false;
|
||||
lastCol = genColumn;
|
||||
if (hasMoreVlq(reader, semi)) {
|
||||
sourcesIndex = decodeInteger(reader, sourcesIndex);
|
||||
sourceLine = decodeInteger(reader, sourceLine);
|
||||
sourceColumn = decodeInteger(reader, sourceColumn);
|
||||
if (hasMoreVlq(reader, semi)) {
|
||||
namesIndex = decodeInteger(reader, namesIndex);
|
||||
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex];
|
||||
} else {
|
||||
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn];
|
||||
}
|
||||
} else {
|
||||
seg = [genColumn];
|
||||
}
|
||||
line.push(seg);
|
||||
reader.pos++;
|
||||
}
|
||||
if (!sorted) sort(line);
|
||||
decoded.push(line);
|
||||
reader.pos = semi + 1;
|
||||
} while (reader.pos <= length);
|
||||
return decoded;
|
||||
}
|
||||
function sort(line) {
|
||||
line.sort(sortComparator);
|
||||
}
|
||||
function sortComparator(a, b) {
|
||||
return a[0] - b[0];
|
||||
}
|
||||
function encode(decoded) {
|
||||
const writer = new StringWriter();
|
||||
let sourcesIndex = 0;
|
||||
let sourceLine = 0;
|
||||
let sourceColumn = 0;
|
||||
let namesIndex = 0;
|
||||
for (let i = 0; i < decoded.length; i++) {
|
||||
const line = decoded[i];
|
||||
if (i > 0) writer.write(semicolon);
|
||||
if (line.length === 0) continue;
|
||||
let genColumn = 0;
|
||||
for (let j = 0; j < line.length; j++) {
|
||||
const segment = line[j];
|
||||
if (j > 0) writer.write(comma);
|
||||
genColumn = encodeInteger(writer, segment[0], genColumn);
|
||||
if (segment.length === 1) continue;
|
||||
sourcesIndex = encodeInteger(writer, segment[1], sourcesIndex);
|
||||
sourceLine = encodeInteger(writer, segment[2], sourceLine);
|
||||
sourceColumn = encodeInteger(writer, segment[3], sourceColumn);
|
||||
if (segment.length === 4) continue;
|
||||
namesIndex = encodeInteger(writer, segment[4], namesIndex);
|
||||
}
|
||||
}
|
||||
return writer.flush();
|
||||
}
|
||||
}));
|
||||
//# sourceMappingURL=sourcemap-codec.umd.js.map
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { TSESLint } from '@typescript-eslint/utils';
|
||||
type AllowedSingleElementEquality = 'always' | 'never';
|
||||
export type Options = [
|
||||
{
|
||||
allowSingleElementEquality?: AllowedSingleElementEquality;
|
||||
}
|
||||
];
|
||||
export type MessageIds = 'preferEndsWith' | 'preferStartsWith';
|
||||
declare const _default: TSESLint.RuleModule<MessageIds, Options, import("../../rules").ESLintPluginDocs, TSESLint.RuleListener> & {
|
||||
name: string;
|
||||
};
|
||||
export default _default;
|
||||
@@ -0,0 +1,11 @@
|
||||
const pino = require("../../../../");
|
||||
|
||||
module.exports = function() {
|
||||
const logger = pino(
|
||||
pino.transport({
|
||||
target: 'pino/file'
|
||||
})
|
||||
)
|
||||
|
||||
logger.info('done!')
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
var stringify = require('../');
|
||||
|
||||
var obj = { d: 6, c: 5, b: [{z:3,y:2,x:1},9], a: 10 };
|
||||
var s = stringify(obj, function (a, b) {
|
||||
return a.value < b.value ? 1 : -1;
|
||||
});
|
||||
console.log(s);
|
||||
@@ -0,0 +1,19 @@
|
||||
name: Lock Threads
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 1 * *'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: lock
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lock-threads:
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
uses: fastify/workflows/.github/workflows/lock-threads.yml@2073dc8e1f9e172bf42daa3843c9dbd31af1e8cb # v6.0.0
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { TSESTree } from '@typescript-eslint/types';
|
||||
import type { Referencer } from './Referencer';
|
||||
import { Visitor } from './Visitor';
|
||||
export type ExportNode = TSESTree.ExportAllDeclaration | TSESTree.ExportDefaultDeclaration | TSESTree.ExportNamedDeclaration;
|
||||
export declare class ExportVisitor extends Visitor {
|
||||
#private;
|
||||
constructor(node: ExportNode, referencer: Referencer);
|
||||
static visit(referencer: Referencer, node: ExportNode): void;
|
||||
protected ExportDefaultDeclaration(node: TSESTree.ExportDefaultDeclaration): void;
|
||||
protected ExportNamedDeclaration(node: TSESTree.ExportNamedDeclaration): void;
|
||||
protected ExportSpecifier(node: TSESTree.ExportSpecifier): void;
|
||||
protected Identifier(node: TSESTree.Identifier): void;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag references to the undefined variable.
|
||||
* @author Michael Ficarra
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @typedef {import("eslint-scope").Scope} Scope */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Disallow the use of `undefined` as an identifier",
|
||||
recommended: false,
|
||||
frozen: true,
|
||||
url: "https://eslint.org/docs/latest/rules/no-undefined",
|
||||
},
|
||||
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
unexpectedUndefined: "Unexpected use of undefined.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Report an invalid "undefined" identifier node.
|
||||
* @param {ASTNode} node The node to report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function report(node) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpectedUndefined",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given scope for references to `undefined` and reports
|
||||
* all references found.
|
||||
* @param {Scope} scope The scope to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkScope(scope) {
|
||||
const undefinedVar = scope.set.get("undefined");
|
||||
|
||||
if (!undefinedVar) {
|
||||
return;
|
||||
}
|
||||
|
||||
const references = undefinedVar.references;
|
||||
|
||||
const defs = undefinedVar.defs;
|
||||
|
||||
// Report non-initializing references (those are covered in defs below)
|
||||
references
|
||||
.filter(ref => !ref.init)
|
||||
.forEach(ref => report(ref.identifier));
|
||||
|
||||
defs.forEach(def => report(def.name));
|
||||
}
|
||||
|
||||
return {
|
||||
"Program:exit"(node) {
|
||||
const globalScope = sourceCode.getScope(node);
|
||||
|
||||
const stack = [globalScope];
|
||||
|
||||
while (stack.length) {
|
||||
const scope = stack.pop();
|
||||
|
||||
stack.push(...scope.childScopes);
|
||||
checkScope(scope);
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,841 @@
|
||||
// parse a single path portion
|
||||
var _a;
|
||||
import { parseClass } from './brace-expressions.js';
|
||||
import { unescape } from './unescape.js';
|
||||
const types = new Set(['!', '?', '+', '*', '@']);
|
||||
const isExtglobType = (c) => types.has(c);
|
||||
const isExtglobAST = (c) => isExtglobType(c.type);
|
||||
// Map of which extglob types can adopt the children of a nested extglob
|
||||
//
|
||||
// anything but ! can adopt a matching type:
|
||||
// +(a|+(b|c)|d) => +(a|b|c|d)
|
||||
// *(a|*(b|c)|d) => *(a|b|c|d)
|
||||
// @(a|@(b|c)|d) => @(a|b|c|d)
|
||||
// ?(a|?(b|c)|d) => ?(a|b|c|d)
|
||||
//
|
||||
// * can adopt anything, because 0 or repetition is allowed
|
||||
// *(a|?(b|c)|d) => *(a|b|c|d)
|
||||
// *(a|+(b|c)|d) => *(a|b|c|d)
|
||||
// *(a|@(b|c)|d) => *(a|b|c|d)
|
||||
//
|
||||
// + can adopt @, because 1 or repetition is allowed
|
||||
// +(a|@(b|c)|d) => +(a|b|c|d)
|
||||
//
|
||||
// + and @ CANNOT adopt *, because 0 would be allowed
|
||||
// +(a|*(b|c)|d) => would match "", on *(b|c)
|
||||
// @(a|*(b|c)|d) => would match "", on *(b|c)
|
||||
//
|
||||
// + and @ CANNOT adopt ?, because 0 would be allowed
|
||||
// +(a|?(b|c)|d) => would match "", on ?(b|c)
|
||||
// @(a|?(b|c)|d) => would match "", on ?(b|c)
|
||||
//
|
||||
// ? can adopt @, because 0 or 1 is allowed
|
||||
// ?(a|@(b|c)|d) => ?(a|b|c|d)
|
||||
//
|
||||
// ? and @ CANNOT adopt * or +, because >1 would be allowed
|
||||
// ?(a|*(b|c)|d) => would match bbb on *(b|c)
|
||||
// @(a|*(b|c)|d) => would match bbb on *(b|c)
|
||||
// ?(a|+(b|c)|d) => would match bbb on +(b|c)
|
||||
// @(a|+(b|c)|d) => would match bbb on +(b|c)
|
||||
//
|
||||
// ! CANNOT adopt ! (nothing else can either)
|
||||
// !(a|!(b|c)|d) => !(a|b|c|d) would fail to match on b (not not b|c)
|
||||
//
|
||||
// ! can adopt @
|
||||
// !(a|@(b|c)|d) => !(a|b|c|d)
|
||||
//
|
||||
// ! CANNOT adopt *
|
||||
// !(a|*(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
|
||||
//
|
||||
// ! CANNOT adopt +
|
||||
// !(a|+(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
|
||||
//
|
||||
// ! CANNOT adopt ?
|
||||
// x!(a|?(b|c)|d) => x!(a|b|c|d) would fail to match "x"
|
||||
const adoptionMap = new Map([
|
||||
['!', ['@']],
|
||||
['?', ['?', '@']],
|
||||
['@', ['@']],
|
||||
['*', ['*', '+', '?', '@']],
|
||||
['+', ['+', '@']],
|
||||
]);
|
||||
// nested extglobs that can be adopted in, but with the addition of
|
||||
// a blank '' element.
|
||||
const adoptionWithSpaceMap = new Map([
|
||||
['!', ['?']],
|
||||
['@', ['?']],
|
||||
['+', ['?', '*']],
|
||||
]);
|
||||
// union of the previous two maps
|
||||
const adoptionAnyMap = new Map([
|
||||
['!', ['?', '@']],
|
||||
['?', ['?', '@']],
|
||||
['@', ['?', '@']],
|
||||
['*', ['*', '+', '?', '@']],
|
||||
['+', ['+', '@', '?', '*']],
|
||||
]);
|
||||
// Extglobs that can take over their parent if they are the only child
|
||||
// the key is parent, value maps child to resulting extglob parent type
|
||||
// '@' is omitted because it's a special case. An `@` extglob with a single
|
||||
// member can always be usurped by that subpattern.
|
||||
const usurpMap = new Map([
|
||||
['!', new Map([['!', '@']])],
|
||||
[
|
||||
'?',
|
||||
new Map([
|
||||
['*', '*'],
|
||||
['+', '*'],
|
||||
]),
|
||||
],
|
||||
[
|
||||
'@',
|
||||
new Map([
|
||||
['!', '!'],
|
||||
['?', '?'],
|
||||
['@', '@'],
|
||||
['*', '*'],
|
||||
['+', '+'],
|
||||
]),
|
||||
],
|
||||
[
|
||||
'+',
|
||||
new Map([
|
||||
['?', '*'],
|
||||
['*', '*'],
|
||||
]),
|
||||
],
|
||||
]);
|
||||
// Patterns that get prepended to bind to the start of either the
|
||||
// entire string, or just a single path portion, to prevent dots
|
||||
// and/or traversal patterns, when needed.
|
||||
// Exts don't need the ^ or / bit, because the root binds that already.
|
||||
const startNoTraversal = '(?!(?:^|/)\\.\\.?(?:$|/))';
|
||||
const startNoDot = '(?!\\.)';
|
||||
// characters that indicate a start of pattern needs the "no dots" bit,
|
||||
// because a dot *might* be matched. ( is not in the list, because in
|
||||
// the case of a child extglob, it will handle the prevention itself.
|
||||
const addPatternStart = new Set(['[', '.']);
|
||||
// cases where traversal is A-OK, no dot prevention needed
|
||||
const justDots = new Set(['..', '.']);
|
||||
const reSpecials = new Set('().*{}+?[]^$\\!');
|
||||
const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
||||
// any single thing other than /
|
||||
const qmark = '[^/]';
|
||||
// * => any number of characters
|
||||
const star = qmark + '*?';
|
||||
// use + when we need to ensure that *something* matches, because the * is
|
||||
// the only thing in the path portion.
|
||||
const starNoEmpty = qmark + '+?';
|
||||
// remove the \ chars that we added if we end up doing a nonmagic compare
|
||||
// const deslash = (s: string) => s.replace(/\\(.)/g, '$1')
|
||||
let ID = 0;
|
||||
export class AST {
|
||||
type;
|
||||
#root;
|
||||
#hasMagic;
|
||||
#uflag = false;
|
||||
#parts = [];
|
||||
#parent;
|
||||
#parentIndex;
|
||||
#negs;
|
||||
#filledNegs = false;
|
||||
#options;
|
||||
#toString;
|
||||
// set to true if it's an extglob with no children
|
||||
// (which really means one child of '')
|
||||
#emptyExt = false;
|
||||
id = ++ID;
|
||||
get depth() {
|
||||
return (this.#parent?.depth ?? -1) + 1;
|
||||
}
|
||||
[Symbol.for('nodejs.util.inspect.custom')]() {
|
||||
return {
|
||||
'@@type': 'AST',
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
root: this.#root.id,
|
||||
parent: this.#parent?.id,
|
||||
depth: this.depth,
|
||||
partsLength: this.#parts.length,
|
||||
parts: this.#parts,
|
||||
};
|
||||
}
|
||||
constructor(type, parent, options = {}) {
|
||||
this.type = type;
|
||||
// extglobs are inherently magical
|
||||
if (type)
|
||||
this.#hasMagic = true;
|
||||
this.#parent = parent;
|
||||
this.#root = this.#parent ? this.#parent.#root : this;
|
||||
this.#options = this.#root === this ? options : this.#root.#options;
|
||||
this.#negs = this.#root === this ? [] : this.#root.#negs;
|
||||
if (type === '!' && !this.#root.#filledNegs)
|
||||
this.#negs.push(this);
|
||||
this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0;
|
||||
}
|
||||
get hasMagic() {
|
||||
/* c8 ignore start */
|
||||
if (this.#hasMagic !== undefined)
|
||||
return this.#hasMagic;
|
||||
/* c8 ignore stop */
|
||||
for (const p of this.#parts) {
|
||||
if (typeof p === 'string')
|
||||
continue;
|
||||
if (p.type || p.hasMagic)
|
||||
return (this.#hasMagic = true);
|
||||
}
|
||||
// note: will be undefined until we generate the regexp src and find out
|
||||
return this.#hasMagic;
|
||||
}
|
||||
// reconstructs the pattern
|
||||
toString() {
|
||||
return (this.#toString !== undefined ? this.#toString
|
||||
: !this.type ?
|
||||
(this.#toString = this.#parts.map(p => String(p)).join(''))
|
||||
: (this.#toString =
|
||||
this.type +
|
||||
'(' +
|
||||
this.#parts.map(p => String(p)).join('|') +
|
||||
')'));
|
||||
}
|
||||
#fillNegs() {
|
||||
/* c8 ignore start */
|
||||
if (this !== this.#root)
|
||||
throw new Error('should only call on root');
|
||||
if (this.#filledNegs)
|
||||
return this;
|
||||
/* c8 ignore stop */
|
||||
// call toString() once to fill this out
|
||||
this.toString();
|
||||
this.#filledNegs = true;
|
||||
let n;
|
||||
while ((n = this.#negs.pop())) {
|
||||
if (n.type !== '!')
|
||||
continue;
|
||||
// walk up the tree, appending everthing that comes AFTER parentIndex
|
||||
let p = n;
|
||||
let pp = p.#parent;
|
||||
while (pp) {
|
||||
for (let i = p.#parentIndex + 1; !pp.type && i < pp.#parts.length; i++) {
|
||||
for (const part of n.#parts) {
|
||||
/* c8 ignore start */
|
||||
if (typeof part === 'string') {
|
||||
throw new Error('string part in extglob AST??');
|
||||
}
|
||||
/* c8 ignore stop */
|
||||
part.copyIn(pp.#parts[i]);
|
||||
}
|
||||
}
|
||||
p = pp;
|
||||
pp = p.#parent;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
push(...parts) {
|
||||
for (const p of parts) {
|
||||
if (p === '')
|
||||
continue;
|
||||
/* c8 ignore start */
|
||||
if (typeof p !== 'string' &&
|
||||
!(p instanceof _a && p.#parent === this)) {
|
||||
throw new Error('invalid part: ' + p);
|
||||
}
|
||||
/* c8 ignore stop */
|
||||
this.#parts.push(p);
|
||||
}
|
||||
}
|
||||
toJSON() {
|
||||
const ret = this.type === null ?
|
||||
this.#parts
|
||||
.slice()
|
||||
.map(p => (typeof p === 'string' ? p : p.toJSON()))
|
||||
: [this.type, ...this.#parts.map(p => p.toJSON())];
|
||||
if (this.isStart() && !this.type)
|
||||
ret.unshift([]);
|
||||
if (this.isEnd() &&
|
||||
(this === this.#root ||
|
||||
(this.#root.#filledNegs && this.#parent?.type === '!'))) {
|
||||
ret.push({});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
isStart() {
|
||||
if (this.#root === this)
|
||||
return true;
|
||||
// if (this.type) return !!this.#parent?.isStart()
|
||||
if (!this.#parent?.isStart())
|
||||
return false;
|
||||
if (this.#parentIndex === 0)
|
||||
return true;
|
||||
// if everything AHEAD of this is a negation, then it's still the "start"
|
||||
const p = this.#parent;
|
||||
for (let i = 0; i < this.#parentIndex; i++) {
|
||||
const pp = p.#parts[i];
|
||||
if (!(pp instanceof _a && pp.type === '!')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
isEnd() {
|
||||
if (this.#root === this)
|
||||
return true;
|
||||
if (this.#parent?.type === '!')
|
||||
return true;
|
||||
if (!this.#parent?.isEnd())
|
||||
return false;
|
||||
if (!this.type)
|
||||
return this.#parent?.isEnd();
|
||||
// if not root, it'll always have a parent
|
||||
/* c8 ignore start */
|
||||
const pl = this.#parent ? this.#parent.#parts.length : 0;
|
||||
/* c8 ignore stop */
|
||||
return this.#parentIndex === pl - 1;
|
||||
}
|
||||
copyIn(part) {
|
||||
if (typeof part === 'string')
|
||||
this.push(part);
|
||||
else
|
||||
this.push(part.clone(this));
|
||||
}
|
||||
clone(parent) {
|
||||
const c = new _a(this.type, parent);
|
||||
for (const p of this.#parts) {
|
||||
c.copyIn(p);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
static #parseAST(str, ast, pos, opt, extDepth) {
|
||||
const maxDepth = opt.maxExtglobRecursion ?? 2;
|
||||
let escaping = false;
|
||||
let inBrace = false;
|
||||
let braceStart = -1;
|
||||
let braceNeg = false;
|
||||
if (ast.type === null) {
|
||||
// outside of a extglob, append until we find a start
|
||||
let i = pos;
|
||||
let acc = '';
|
||||
while (i < str.length) {
|
||||
const c = str.charAt(i++);
|
||||
// still accumulate escapes at this point, but we do ignore
|
||||
// starts that are escaped
|
||||
if (escaping || c === '\\') {
|
||||
escaping = !escaping;
|
||||
acc += c;
|
||||
continue;
|
||||
}
|
||||
if (inBrace) {
|
||||
if (i === braceStart + 1) {
|
||||
if (c === '^' || c === '!') {
|
||||
braceNeg = true;
|
||||
}
|
||||
}
|
||||
else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
|
||||
inBrace = false;
|
||||
}
|
||||
acc += c;
|
||||
continue;
|
||||
}
|
||||
else if (c === '[') {
|
||||
inBrace = true;
|
||||
braceStart = i;
|
||||
braceNeg = false;
|
||||
acc += c;
|
||||
continue;
|
||||
}
|
||||
// we don't have to check for adoption here, because that's
|
||||
// done at the other recursion point.
|
||||
const doRecurse = !opt.noext &&
|
||||
isExtglobType(c) &&
|
||||
str.charAt(i) === '(' &&
|
||||
extDepth <= maxDepth;
|
||||
if (doRecurse) {
|
||||
ast.push(acc);
|
||||
acc = '';
|
||||
const ext = new _a(c, ast);
|
||||
i = _a.#parseAST(str, ext, i, opt, extDepth + 1);
|
||||
ast.push(ext);
|
||||
continue;
|
||||
}
|
||||
acc += c;
|
||||
}
|
||||
ast.push(acc);
|
||||
return i;
|
||||
}
|
||||
// some kind of extglob, pos is at the (
|
||||
// find the next | or )
|
||||
let i = pos + 1;
|
||||
let part = new _a(null, ast);
|
||||
const parts = [];
|
||||
let acc = '';
|
||||
while (i < str.length) {
|
||||
const c = str.charAt(i++);
|
||||
// still accumulate escapes at this point, but we do ignore
|
||||
// starts that are escaped
|
||||
if (escaping || c === '\\') {
|
||||
escaping = !escaping;
|
||||
acc += c;
|
||||
continue;
|
||||
}
|
||||
if (inBrace) {
|
||||
if (i === braceStart + 1) {
|
||||
if (c === '^' || c === '!') {
|
||||
braceNeg = true;
|
||||
}
|
||||
}
|
||||
else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
|
||||
inBrace = false;
|
||||
}
|
||||
acc += c;
|
||||
continue;
|
||||
}
|
||||
else if (c === '[') {
|
||||
inBrace = true;
|
||||
braceStart = i;
|
||||
braceNeg = false;
|
||||
acc += c;
|
||||
continue;
|
||||
}
|
||||
const doRecurse = !opt.noext &&
|
||||
isExtglobType(c) &&
|
||||
str.charAt(i) === '(' &&
|
||||
/* c8 ignore start - the maxDepth is sufficient here */
|
||||
(extDepth <= maxDepth || (ast && ast.#canAdoptType(c)));
|
||||
/* c8 ignore stop */
|
||||
if (doRecurse) {
|
||||
const depthAdd = ast && ast.#canAdoptType(c) ? 0 : 1;
|
||||
part.push(acc);
|
||||
acc = '';
|
||||
const ext = new _a(c, part);
|
||||
part.push(ext);
|
||||
i = _a.#parseAST(str, ext, i, opt, extDepth + depthAdd);
|
||||
continue;
|
||||
}
|
||||
if (c === '|') {
|
||||
part.push(acc);
|
||||
acc = '';
|
||||
parts.push(part);
|
||||
part = new _a(null, ast);
|
||||
continue;
|
||||
}
|
||||
if (c === ')') {
|
||||
if (acc === '' && ast.#parts.length === 0) {
|
||||
ast.#emptyExt = true;
|
||||
}
|
||||
part.push(acc);
|
||||
acc = '';
|
||||
ast.push(...parts, part);
|
||||
return i;
|
||||
}
|
||||
acc += c;
|
||||
}
|
||||
// unfinished extglob
|
||||
// if we got here, it was a malformed extglob! not an extglob, but
|
||||
// maybe something else in there.
|
||||
ast.type = null;
|
||||
ast.#hasMagic = undefined;
|
||||
ast.#parts = [str.substring(pos - 1)];
|
||||
return i;
|
||||
}
|
||||
#canAdoptWithSpace(child) {
|
||||
return this.#canAdopt(child, adoptionWithSpaceMap);
|
||||
}
|
||||
#canAdopt(child, map = adoptionMap) {
|
||||
if (!child ||
|
||||
typeof child !== 'object' ||
|
||||
child.type !== null ||
|
||||
child.#parts.length !== 1 ||
|
||||
this.type === null) {
|
||||
return false;
|
||||
}
|
||||
const gc = child.#parts[0];
|
||||
if (!gc || typeof gc !== 'object' || gc.type === null) {
|
||||
return false;
|
||||
}
|
||||
return this.#canAdoptType(gc.type, map);
|
||||
}
|
||||
#canAdoptType(c, map = adoptionAnyMap) {
|
||||
return !!map.get(this.type)?.includes(c);
|
||||
}
|
||||
#adoptWithSpace(child, index) {
|
||||
const gc = child.#parts[0];
|
||||
const blank = new _a(null, gc, this.options);
|
||||
blank.#parts.push('');
|
||||
gc.push(blank);
|
||||
this.#adopt(child, index);
|
||||
}
|
||||
#adopt(child, index) {
|
||||
const gc = child.#parts[0];
|
||||
this.#parts.splice(index, 1, ...gc.#parts);
|
||||
for (const p of gc.#parts) {
|
||||
if (typeof p === 'object')
|
||||
p.#parent = this;
|
||||
}
|
||||
this.#toString = undefined;
|
||||
}
|
||||
#canUsurpType(c) {
|
||||
const m = usurpMap.get(this.type);
|
||||
return !!m?.has(c);
|
||||
}
|
||||
#canUsurp(child) {
|
||||
if (!child ||
|
||||
typeof child !== 'object' ||
|
||||
child.type !== null ||
|
||||
child.#parts.length !== 1 ||
|
||||
this.type === null ||
|
||||
this.#parts.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
const gc = child.#parts[0];
|
||||
if (!gc || typeof gc !== 'object' || gc.type === null) {
|
||||
return false;
|
||||
}
|
||||
return this.#canUsurpType(gc.type);
|
||||
}
|
||||
#usurp(child) {
|
||||
const m = usurpMap.get(this.type);
|
||||
const gc = child.#parts[0];
|
||||
const nt = m?.get(gc.type);
|
||||
/* c8 ignore start - impossible */
|
||||
if (!nt)
|
||||
return false;
|
||||
/* c8 ignore stop */
|
||||
this.#parts = gc.#parts;
|
||||
for (const p of this.#parts) {
|
||||
if (typeof p === 'object') {
|
||||
p.#parent = this;
|
||||
}
|
||||
}
|
||||
this.type = nt;
|
||||
this.#toString = undefined;
|
||||
this.#emptyExt = false;
|
||||
}
|
||||
static fromGlob(pattern, options = {}) {
|
||||
const ast = new _a(null, undefined, options);
|
||||
_a.#parseAST(pattern, ast, 0, options, 0);
|
||||
return ast;
|
||||
}
|
||||
// returns the regular expression if there's magic, or the unescaped
|
||||
// string if not.
|
||||
toMMPattern() {
|
||||
// should only be called on root
|
||||
/* c8 ignore start */
|
||||
if (this !== this.#root)
|
||||
return this.#root.toMMPattern();
|
||||
/* c8 ignore stop */
|
||||
const glob = this.toString();
|
||||
const [re, body, hasMagic, uflag] = this.toRegExpSource();
|
||||
// if we're in nocase mode, and not nocaseMagicOnly, then we do
|
||||
// still need a regular expression if we have to case-insensitively
|
||||
// match capital/lowercase characters.
|
||||
const anyMagic = hasMagic ||
|
||||
this.#hasMagic ||
|
||||
(this.#options.nocase &&
|
||||
!this.#options.nocaseMagicOnly &&
|
||||
glob.toUpperCase() !== glob.toLowerCase());
|
||||
if (!anyMagic) {
|
||||
return body;
|
||||
}
|
||||
const flags = (this.#options.nocase ? 'i' : '') + (uflag ? 'u' : '');
|
||||
return Object.assign(new RegExp(`^${re}$`, flags), {
|
||||
_src: re,
|
||||
_glob: glob,
|
||||
});
|
||||
}
|
||||
get options() {
|
||||
return this.#options;
|
||||
}
|
||||
// returns the string match, the regexp source, whether there's magic
|
||||
// in the regexp (so a regular expression is required) and whether or
|
||||
// not the uflag is needed for the regular expression (for posix classes)
|
||||
// TODO: instead of injecting the start/end at this point, just return
|
||||
// the BODY of the regexp, along with the start/end portions suitable
|
||||
// for binding the start/end in either a joined full-path makeRe context
|
||||
// (where we bind to (^|/), or a standalone matchPart context (where
|
||||
// we bind to ^, and not /). Otherwise slashes get duped!
|
||||
//
|
||||
// In part-matching mode, the start is:
|
||||
// - if not isStart: nothing
|
||||
// - if traversal possible, but not allowed: ^(?!\.\.?$)
|
||||
// - if dots allowed or not possible: ^
|
||||
// - if dots possible and not allowed: ^(?!\.)
|
||||
// end is:
|
||||
// - if not isEnd(): nothing
|
||||
// - else: $
|
||||
//
|
||||
// In full-path matching mode, we put the slash at the START of the
|
||||
// pattern, so start is:
|
||||
// - if first pattern: same as part-matching mode
|
||||
// - if not isStart(): nothing
|
||||
// - if traversal possible, but not allowed: /(?!\.\.?(?:$|/))
|
||||
// - if dots allowed or not possible: /
|
||||
// - if dots possible and not allowed: /(?!\.)
|
||||
// end is:
|
||||
// - if last pattern, same as part-matching mode
|
||||
// - else nothing
|
||||
//
|
||||
// Always put the (?:$|/) on negated tails, though, because that has to be
|
||||
// there to bind the end of the negated pattern portion, and it's easier to
|
||||
// just stick it in now rather than try to inject it later in the middle of
|
||||
// the pattern.
|
||||
//
|
||||
// We can just always return the same end, and leave it up to the caller
|
||||
// to know whether it's going to be used joined or in parts.
|
||||
// And, if the start is adjusted slightly, can do the same there:
|
||||
// - if not isStart: nothing
|
||||
// - if traversal possible, but not allowed: (?:/|^)(?!\.\.?$)
|
||||
// - if dots allowed or not possible: (?:/|^)
|
||||
// - if dots possible and not allowed: (?:/|^)(?!\.)
|
||||
//
|
||||
// But it's better to have a simpler binding without a conditional, for
|
||||
// performance, so probably better to return both start options.
|
||||
//
|
||||
// Then the caller just ignores the end if it's not the first pattern,
|
||||
// and the start always gets applied.
|
||||
//
|
||||
// But that's always going to be $ if it's the ending pattern, or nothing,
|
||||
// so the caller can just attach $ at the end of the pattern when building.
|
||||
//
|
||||
// So the todo is:
|
||||
// - better detect what kind of start is needed
|
||||
// - return both flavors of starting pattern
|
||||
// - attach $ at the end of the pattern when creating the actual RegExp
|
||||
//
|
||||
// Ah, but wait, no, that all only applies to the root when the first pattern
|
||||
// is not an extglob. If the first pattern IS an extglob, then we need all
|
||||
// that dot prevention biz to live in the extglob portions, because eg
|
||||
// +(*|.x*) can match .xy but not .yx.
|
||||
//
|
||||
// So, return the two flavors if it's #root and the first child is not an
|
||||
// AST, otherwise leave it to the child AST to handle it, and there,
|
||||
// use the (?:^|/) style of start binding.
|
||||
//
|
||||
// Even simplified further:
|
||||
// - Since the start for a join is eg /(?!\.) and the start for a part
|
||||
// is ^(?!\.), we can just prepend (?!\.) to the pattern (either root
|
||||
// or start or whatever) and prepend ^ or / at the Regexp construction.
|
||||
toRegExpSource(allowDot) {
|
||||
const dot = allowDot ?? !!this.#options.dot;
|
||||
if (this.#root === this) {
|
||||
this.#flatten();
|
||||
this.#fillNegs();
|
||||
}
|
||||
if (!isExtglobAST(this)) {
|
||||
const noEmpty = this.isStart() &&
|
||||
this.isEnd() &&
|
||||
!this.#parts.some(s => typeof s !== 'string');
|
||||
const src = this.#parts
|
||||
.map(p => {
|
||||
const [re, _, hasMagic, uflag] = typeof p === 'string' ?
|
||||
_a.#parseGlob(p, this.#hasMagic, noEmpty)
|
||||
: p.toRegExpSource(allowDot);
|
||||
this.#hasMagic = this.#hasMagic || hasMagic;
|
||||
this.#uflag = this.#uflag || uflag;
|
||||
return re;
|
||||
})
|
||||
.join('');
|
||||
let start = '';
|
||||
if (this.isStart()) {
|
||||
if (typeof this.#parts[0] === 'string') {
|
||||
// this is the string that will match the start of the pattern,
|
||||
// so we need to protect against dots and such.
|
||||
// '.' and '..' cannot match unless the pattern is that exactly,
|
||||
// even if it starts with . or dot:true is set.
|
||||
const dotTravAllowed = this.#parts.length === 1 && justDots.has(this.#parts[0]);
|
||||
if (!dotTravAllowed) {
|
||||
const aps = addPatternStart;
|
||||
// check if we have a possibility of matching . or ..,
|
||||
// and prevent that.
|
||||
const needNoTrav =
|
||||
// dots are allowed, and the pattern starts with [ or .
|
||||
(dot && aps.has(src.charAt(0))) ||
|
||||
// the pattern starts with \., and then [ or .
|
||||
(src.startsWith('\\.') && aps.has(src.charAt(2))) ||
|
||||
// the pattern starts with \.\., and then [ or .
|
||||
(src.startsWith('\\.\\.') && aps.has(src.charAt(4)));
|
||||
// no need to prevent dots if it can't match a dot, or if a
|
||||
// sub-pattern will be preventing it anyway.
|
||||
const needNoDot = !dot && !allowDot && aps.has(src.charAt(0));
|
||||
start =
|
||||
needNoTrav ? startNoTraversal
|
||||
: needNoDot ? startNoDot
|
||||
: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
// append the "end of path portion" pattern to negation tails
|
||||
let end = '';
|
||||
if (this.isEnd() &&
|
||||
this.#root.#filledNegs &&
|
||||
this.#parent?.type === '!') {
|
||||
end = '(?:$|\\/)';
|
||||
}
|
||||
const final = start + src + end;
|
||||
return [
|
||||
final,
|
||||
unescape(src),
|
||||
(this.#hasMagic = !!this.#hasMagic),
|
||||
this.#uflag,
|
||||
];
|
||||
}
|
||||
// We need to calculate the body *twice* if it's a repeat pattern
|
||||
// at the start, once in nodot mode, then again in dot mode, so a
|
||||
// pattern like *(?) can match 'x.y'
|
||||
const repeated = this.type === '*' || this.type === '+';
|
||||
// some kind of extglob
|
||||
const start = this.type === '!' ? '(?:(?!(?:' : '(?:';
|
||||
let body = this.#partsToRegExp(dot);
|
||||
if (this.isStart() && this.isEnd() && !body && this.type !== '!') {
|
||||
// invalid extglob, has to at least be *something* present, if it's
|
||||
// the entire path portion.
|
||||
const s = this.toString();
|
||||
const me = this;
|
||||
me.#parts = [s];
|
||||
me.type = null;
|
||||
me.#hasMagic = undefined;
|
||||
return [s, unescape(this.toString()), false, false];
|
||||
}
|
||||
let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot ?
|
||||
''
|
||||
: this.#partsToRegExp(true);
|
||||
if (bodyDotAllowed === body) {
|
||||
bodyDotAllowed = '';
|
||||
}
|
||||
if (bodyDotAllowed) {
|
||||
body = `(?:${body})(?:${bodyDotAllowed})*?`;
|
||||
}
|
||||
// an empty !() is exactly equivalent to a starNoEmpty
|
||||
let final = '';
|
||||
if (this.type === '!' && this.#emptyExt) {
|
||||
final = (this.isStart() && !dot ? startNoDot : '') + starNoEmpty;
|
||||
}
|
||||
else {
|
||||
const close = this.type === '!' ?
|
||||
// !() must match something,but !(x) can match ''
|
||||
'))' +
|
||||
(this.isStart() && !dot && !allowDot ? startNoDot : '') +
|
||||
star +
|
||||
')'
|
||||
: this.type === '@' ? ')'
|
||||
: this.type === '?' ? ')?'
|
||||
: this.type === '+' && bodyDotAllowed ? ')'
|
||||
: this.type === '*' && bodyDotAllowed ? `)?`
|
||||
: `)${this.type}`;
|
||||
final = start + body + close;
|
||||
}
|
||||
return [
|
||||
final,
|
||||
unescape(body),
|
||||
(this.#hasMagic = !!this.#hasMagic),
|
||||
this.#uflag,
|
||||
];
|
||||
}
|
||||
#flatten() {
|
||||
if (!isExtglobAST(this)) {
|
||||
for (const p of this.#parts) {
|
||||
if (typeof p === 'object') {
|
||||
p.#flatten();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// do up to 10 passes to flatten as much as possible
|
||||
let iterations = 0;
|
||||
let done = false;
|
||||
do {
|
||||
done = true;
|
||||
for (let i = 0; i < this.#parts.length; i++) {
|
||||
const c = this.#parts[i];
|
||||
if (typeof c === 'object') {
|
||||
c.#flatten();
|
||||
if (this.#canAdopt(c)) {
|
||||
done = false;
|
||||
this.#adopt(c, i);
|
||||
}
|
||||
else if (this.#canAdoptWithSpace(c)) {
|
||||
done = false;
|
||||
this.#adoptWithSpace(c, i);
|
||||
}
|
||||
else if (this.#canUsurp(c)) {
|
||||
done = false;
|
||||
this.#usurp(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!done && ++iterations < 10);
|
||||
}
|
||||
this.#toString = undefined;
|
||||
}
|
||||
#partsToRegExp(dot) {
|
||||
return this.#parts
|
||||
.map(p => {
|
||||
// extglob ASTs should only contain parent ASTs
|
||||
/* c8 ignore start */
|
||||
if (typeof p === 'string') {
|
||||
throw new Error('string type in extglob ast??');
|
||||
}
|
||||
/* c8 ignore stop */
|
||||
// can ignore hasMagic, because extglobs are already always magic
|
||||
const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot);
|
||||
this.#uflag = this.#uflag || uflag;
|
||||
return re;
|
||||
})
|
||||
.filter(p => !(this.isStart() && this.isEnd()) || !!p)
|
||||
.join('|');
|
||||
}
|
||||
static #parseGlob(glob, hasMagic, noEmpty = false) {
|
||||
let escaping = false;
|
||||
let re = '';
|
||||
let uflag = false;
|
||||
// multiple stars that aren't globstars coalesce into one *
|
||||
let inStar = false;
|
||||
for (let i = 0; i < glob.length; i++) {
|
||||
const c = glob.charAt(i);
|
||||
if (escaping) {
|
||||
escaping = false;
|
||||
re += (reSpecials.has(c) ? '\\' : '') + c;
|
||||
continue;
|
||||
}
|
||||
if (c === '*') {
|
||||
if (inStar)
|
||||
continue;
|
||||
inStar = true;
|
||||
re += noEmpty && /^[*]+$/.test(glob) ? starNoEmpty : star;
|
||||
hasMagic = true;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
inStar = false;
|
||||
}
|
||||
if (c === '\\') {
|
||||
if (i === glob.length - 1) {
|
||||
re += '\\\\';
|
||||
}
|
||||
else {
|
||||
escaping = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (c === '[') {
|
||||
const [src, needUflag, consumed, magic] = parseClass(glob, i);
|
||||
if (consumed) {
|
||||
re += src;
|
||||
uflag = uflag || needUflag;
|
||||
i += consumed - 1;
|
||||
hasMagic = hasMagic || magic;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (c === '?') {
|
||||
re += qmark;
|
||||
hasMagic = true;
|
||||
continue;
|
||||
}
|
||||
re += regExpEscape(c);
|
||||
}
|
||||
return [re, unescape(glob), !!hasMagic, uflag];
|
||||
}
|
||||
}
|
||||
_a = AST;
|
||||
//# sourceMappingURL=ast.js.map
|
||||
@@ -0,0 +1,73 @@
|
||||
'use strict';
|
||||
module.exports = function generate_anyOf(it, $keyword, $ruleType) {
|
||||
var out = ' ';
|
||||
var $lvl = it.level;
|
||||
var $dataLvl = it.dataLevel;
|
||||
var $schema = it.schema[$keyword];
|
||||
var $schemaPath = it.schemaPath + it.util.getProperty($keyword);
|
||||
var $errSchemaPath = it.errSchemaPath + '/' + $keyword;
|
||||
var $breakOnError = !it.opts.allErrors;
|
||||
var $data = 'data' + ($dataLvl || '');
|
||||
var $valid = 'valid' + $lvl;
|
||||
var $errs = 'errs__' + $lvl;
|
||||
var $it = it.util.copy(it);
|
||||
var $closingBraces = '';
|
||||
$it.level++;
|
||||
var $nextValid = 'valid' + $it.level;
|
||||
var $noEmptySchema = $schema.every(function($sch) {
|
||||
return (it.opts.strictKeywords ? (typeof $sch == 'object' && Object.keys($sch).length > 0) || $sch === false : it.util.schemaHasRules($sch, it.RULES.all));
|
||||
});
|
||||
if ($noEmptySchema) {
|
||||
var $currentBaseId = $it.baseId;
|
||||
out += ' var ' + ($errs) + ' = errors; var ' + ($valid) + ' = false; ';
|
||||
var $wasComposite = it.compositeRule;
|
||||
it.compositeRule = $it.compositeRule = true;
|
||||
var arr1 = $schema;
|
||||
if (arr1) {
|
||||
var $sch, $i = -1,
|
||||
l1 = arr1.length - 1;
|
||||
while ($i < l1) {
|
||||
$sch = arr1[$i += 1];
|
||||
$it.schema = $sch;
|
||||
$it.schemaPath = $schemaPath + '[' + $i + ']';
|
||||
$it.errSchemaPath = $errSchemaPath + '/' + $i;
|
||||
out += ' ' + (it.validate($it)) + ' ';
|
||||
$it.baseId = $currentBaseId;
|
||||
out += ' ' + ($valid) + ' = ' + ($valid) + ' || ' + ($nextValid) + '; if (!' + ($valid) + ') { ';
|
||||
$closingBraces += '}';
|
||||
}
|
||||
}
|
||||
it.compositeRule = $it.compositeRule = $wasComposite;
|
||||
out += ' ' + ($closingBraces) + ' if (!' + ($valid) + ') { var err = '; /* istanbul ignore else */
|
||||
if (it.createErrors !== false) {
|
||||
out += ' { keyword: \'' + ('anyOf') + '\' , dataPath: (dataPath || \'\') + ' + (it.errorPath) + ' , schemaPath: ' + (it.util.toQuotedString($errSchemaPath)) + ' , params: {} ';
|
||||
if (it.opts.messages !== false) {
|
||||
out += ' , message: \'should match some schema in anyOf\' ';
|
||||
}
|
||||
if (it.opts.verbose) {
|
||||
out += ' , schema: validate.schema' + ($schemaPath) + ' , parentSchema: validate.schema' + (it.schemaPath) + ' , data: ' + ($data) + ' ';
|
||||
}
|
||||
out += ' } ';
|
||||
} else {
|
||||
out += ' {} ';
|
||||
}
|
||||
out += '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
|
||||
if (!it.compositeRule && $breakOnError) {
|
||||
/* istanbul ignore if */
|
||||
if (it.async) {
|
||||
out += ' throw new ValidationError(vErrors); ';
|
||||
} else {
|
||||
out += ' validate.errors = vErrors; return false; ';
|
||||
}
|
||||
}
|
||||
out += ' } else { errors = ' + ($errs) + '; if (vErrors !== null) { if (' + ($errs) + ') vErrors.length = ' + ($errs) + '; else vErrors = null; } ';
|
||||
if (it.opts.allErrors) {
|
||||
out += ' } ';
|
||||
}
|
||||
} else {
|
||||
if ($breakOnError) {
|
||||
out += ' if (true) { ';
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import Dispatcher from './dispatcher'
|
||||
|
||||
export default RetryHandler
|
||||
|
||||
declare class RetryHandler implements Dispatcher.DispatchHandler {
|
||||
constructor (
|
||||
options: Dispatcher.DispatchOptions & {
|
||||
retryOptions?: RetryHandler.RetryOptions;
|
||||
},
|
||||
retryHandlers: RetryHandler.RetryHandlers
|
||||
)
|
||||
}
|
||||
|
||||
declare namespace RetryHandler {
|
||||
export type RetryState = { counter: number; }
|
||||
|
||||
export type RetryContext = {
|
||||
state: RetryState;
|
||||
opts: Dispatcher.DispatchOptions & {
|
||||
retryOptions?: RetryHandler.RetryOptions;
|
||||
};
|
||||
}
|
||||
|
||||
export type OnRetryCallback = (result?: Error | null) => void
|
||||
|
||||
export type RetryCallback = (
|
||||
err: Error,
|
||||
context: {
|
||||
state: RetryState;
|
||||
opts: Dispatcher.DispatchOptions & {
|
||||
retryOptions?: RetryHandler.RetryOptions;
|
||||
};
|
||||
},
|
||||
callback: OnRetryCallback
|
||||
) => void
|
||||
|
||||
export interface RetryOptions {
|
||||
/**
|
||||
* If true, the retry handler will throw an error if the request fails,
|
||||
* this will prevent the folling handlers from being called, and will destroy the socket.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof RetryOptions
|
||||
* @default true
|
||||
*/
|
||||
throwOnError?: boolean;
|
||||
/**
|
||||
* Callback to be invoked on every retry iteration.
|
||||
* It receives the error, current state of the retry object and the options object
|
||||
* passed when instantiating the retry handler.
|
||||
*
|
||||
* @type {RetryCallback}
|
||||
* @memberof RetryOptions
|
||||
*/
|
||||
retry?: RetryCallback;
|
||||
/**
|
||||
* Maximum number of retries to allow.
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof RetryOptions
|
||||
* @default 5
|
||||
*/
|
||||
maxRetries?: number;
|
||||
/**
|
||||
* Max number of milliseconds allow between retries
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof RetryOptions
|
||||
* @default 30000
|
||||
*/
|
||||
maxTimeout?: number;
|
||||
/**
|
||||
* Initial number of milliseconds to wait before retrying for the first time.
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof RetryOptions
|
||||
* @default 500
|
||||
*/
|
||||
minTimeout?: number;
|
||||
/**
|
||||
* Factior to multiply the timeout factor between retries.
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof RetryOptions
|
||||
* @default 2
|
||||
*/
|
||||
timeoutFactor?: number;
|
||||
/**
|
||||
* It enables to automatically infer timeout between retries based on the `Retry-After` header.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof RetryOptions
|
||||
* @default true
|
||||
*/
|
||||
retryAfter?: boolean;
|
||||
/**
|
||||
* HTTP methods to retry.
|
||||
*
|
||||
* @type {Dispatcher.HttpMethod[]}
|
||||
* @memberof RetryOptions
|
||||
* @default ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE'],
|
||||
*/
|
||||
methods?: Dispatcher.HttpMethod[];
|
||||
/**
|
||||
* Error codes to be retried. e.g. `ECONNRESET`, `ENOTFOUND`, `ETIMEDOUT`, `ECONNREFUSED`, etc.
|
||||
*
|
||||
* @type {string[]}
|
||||
* @default ['ECONNRESET','ECONNREFUSED','ENOTFOUND','ENETDOWN','ENETUNREACH','EHOSTDOWN','EHOSTUNREACH','EPIPE']
|
||||
*/
|
||||
errorCodes?: string[];
|
||||
/**
|
||||
* HTTP status codes to be retried.
|
||||
*
|
||||
* @type {number[]}
|
||||
* @memberof RetryOptions
|
||||
* @default [500, 502, 503, 504, 429],
|
||||
*/
|
||||
statusCodes?: number[];
|
||||
}
|
||||
|
||||
export interface RetryHandlers {
|
||||
dispatch: Dispatcher['dispatch'];
|
||||
handler: Dispatcher.DispatchHandler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
//compile doT templates to js functions
|
||||
'use strict';
|
||||
|
||||
var glob = require('glob')
|
||||
, fs = require('fs')
|
||||
, path = require('path')
|
||||
, doT = require('dot')
|
||||
, beautify = require('js-beautify').js_beautify;
|
||||
|
||||
var defsRootPath = process.argv[2] || path.join(__dirname, '../lib');
|
||||
|
||||
var defs = {};
|
||||
var defFiles = glob.sync('./dot/**/*.def', { cwd: defsRootPath });
|
||||
defFiles.forEach(function (f) {
|
||||
var name = path.basename(f, '.def');
|
||||
defs[name] = fs.readFileSync(path.join(defsRootPath, f));
|
||||
});
|
||||
|
||||
var filesRootPath = process.argv[3] || path.join(__dirname, '../lib');
|
||||
var files = glob.sync('./dot/**/*.jst', { cwd: filesRootPath });
|
||||
|
||||
var dotjsPath = path.join(filesRootPath, './dotjs');
|
||||
try { fs.mkdirSync(dotjsPath); } catch(e) {}
|
||||
|
||||
console.log('\n\nCompiling:');
|
||||
|
||||
var FUNCTION_NAME = /function\s+anonymous\s*\(it[^)]*\)\s*{/;
|
||||
var OUT_EMPTY_STRING = /out\s*\+=\s*'\s*';/g;
|
||||
var ISTANBUL = /'(istanbul[^']+)';/g;
|
||||
var ERROR_KEYWORD = /\$errorKeyword/g;
|
||||
var ERROR_KEYWORD_OR = /\$errorKeyword\s+\|\|/g;
|
||||
var VARS = [
|
||||
'$errs', '$valid', '$lvl', '$data', '$dataLvl',
|
||||
'$errorKeyword', '$closingBraces', '$schemaPath',
|
||||
'$validate'
|
||||
];
|
||||
|
||||
files.forEach(function (f) {
|
||||
var keyword = path.basename(f, '.jst');
|
||||
var targetPath = path.join(dotjsPath, keyword + '.js');
|
||||
var template = fs.readFileSync(path.join(filesRootPath, f));
|
||||
var code = doT.compile(template, defs);
|
||||
code = code.toString()
|
||||
.replace(OUT_EMPTY_STRING, '')
|
||||
.replace(FUNCTION_NAME, 'function generate_' + keyword + '(it, $keyword, $ruleType) {')
|
||||
.replace(ISTANBUL, '/* $1 */');
|
||||
removeAlwaysFalsyInOr();
|
||||
VARS.forEach(removeUnusedVar);
|
||||
code = "'use strict';\nmodule.exports = " + code;
|
||||
code = beautify(code, { indent_size: 2 }) + '\n';
|
||||
fs.writeFileSync(targetPath, code);
|
||||
console.log('compiled', keyword);
|
||||
|
||||
function removeUnusedVar(v) {
|
||||
v = v.replace(/\$/g, '\\$$');
|
||||
var regexp = new RegExp(v + '[^A-Za-z0-9_$]', 'g');
|
||||
var count = occurrences(regexp);
|
||||
if (count == 1) {
|
||||
regexp = new RegExp('var\\s+' + v + '\\s*=[^;]+;|var\\s+' + v + ';');
|
||||
code = code.replace(regexp, '');
|
||||
}
|
||||
}
|
||||
|
||||
function removeAlwaysFalsyInOr() {
|
||||
var countUsed = occurrences(ERROR_KEYWORD);
|
||||
var countOr = occurrences(ERROR_KEYWORD_OR);
|
||||
if (countUsed == countOr + 1) code = code.replace(ERROR_KEYWORD_OR, '');
|
||||
}
|
||||
|
||||
function occurrences(regexp) {
|
||||
return (code.match(regexp) || []).length;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,130 @@
|
||||
export var CharacterCodes;
|
||||
(function (CharacterCodes) {
|
||||
CharacterCodes[CharacterCodes["EOF"] = -1] = "EOF";
|
||||
CharacterCodes[CharacterCodes["nullCharacter"] = 0] = "nullCharacter";
|
||||
CharacterCodes[CharacterCodes["maxAsciiCharacter"] = 127] = "maxAsciiCharacter";
|
||||
CharacterCodes[CharacterCodes["lineFeed"] = 10] = "lineFeed";
|
||||
CharacterCodes[CharacterCodes["carriageReturn"] = 13] = "carriageReturn";
|
||||
CharacterCodes[CharacterCodes["lineSeparator"] = 8232] = "lineSeparator";
|
||||
CharacterCodes[CharacterCodes["paragraphSeparator"] = 8233] = "paragraphSeparator";
|
||||
CharacterCodes[CharacterCodes["nextLine"] = 133] = "nextLine";
|
||||
CharacterCodes[CharacterCodes["space"] = 32] = "space";
|
||||
CharacterCodes[CharacterCodes["nonBreakingSpace"] = 160] = "nonBreakingSpace";
|
||||
CharacterCodes[CharacterCodes["enQuad"] = 8192] = "enQuad";
|
||||
CharacterCodes[CharacterCodes["emQuad"] = 8193] = "emQuad";
|
||||
CharacterCodes[CharacterCodes["enSpace"] = 8194] = "enSpace";
|
||||
CharacterCodes[CharacterCodes["emSpace"] = 8195] = "emSpace";
|
||||
CharacterCodes[CharacterCodes["threePerEmSpace"] = 8196] = "threePerEmSpace";
|
||||
CharacterCodes[CharacterCodes["fourPerEmSpace"] = 8197] = "fourPerEmSpace";
|
||||
CharacterCodes[CharacterCodes["sixPerEmSpace"] = 8198] = "sixPerEmSpace";
|
||||
CharacterCodes[CharacterCodes["figureSpace"] = 8199] = "figureSpace";
|
||||
CharacterCodes[CharacterCodes["punctuationSpace"] = 8200] = "punctuationSpace";
|
||||
CharacterCodes[CharacterCodes["thinSpace"] = 8201] = "thinSpace";
|
||||
CharacterCodes[CharacterCodes["hairSpace"] = 8202] = "hairSpace";
|
||||
CharacterCodes[CharacterCodes["zeroWidthSpace"] = 8203] = "zeroWidthSpace";
|
||||
CharacterCodes[CharacterCodes["narrowNoBreakSpace"] = 8239] = "narrowNoBreakSpace";
|
||||
CharacterCodes[CharacterCodes["ideographicSpace"] = 12288] = "ideographicSpace";
|
||||
CharacterCodes[CharacterCodes["mathematicalSpace"] = 8287] = "mathematicalSpace";
|
||||
CharacterCodes[CharacterCodes["ogham"] = 5765] = "ogham";
|
||||
CharacterCodes[CharacterCodes["replacementCharacter"] = 65533] = "replacementCharacter";
|
||||
CharacterCodes[CharacterCodes["_"] = 95] = "_";
|
||||
CharacterCodes[CharacterCodes["$"] = 36] = "$";
|
||||
CharacterCodes[CharacterCodes["_0"] = 48] = "_0";
|
||||
CharacterCodes[CharacterCodes["_1"] = 49] = "_1";
|
||||
CharacterCodes[CharacterCodes["_2"] = 50] = "_2";
|
||||
CharacterCodes[CharacterCodes["_3"] = 51] = "_3";
|
||||
CharacterCodes[CharacterCodes["_4"] = 52] = "_4";
|
||||
CharacterCodes[CharacterCodes["_5"] = 53] = "_5";
|
||||
CharacterCodes[CharacterCodes["_6"] = 54] = "_6";
|
||||
CharacterCodes[CharacterCodes["_7"] = 55] = "_7";
|
||||
CharacterCodes[CharacterCodes["_8"] = 56] = "_8";
|
||||
CharacterCodes[CharacterCodes["_9"] = 57] = "_9";
|
||||
CharacterCodes[CharacterCodes["a"] = 97] = "a";
|
||||
CharacterCodes[CharacterCodes["b"] = 98] = "b";
|
||||
CharacterCodes[CharacterCodes["c"] = 99] = "c";
|
||||
CharacterCodes[CharacterCodes["d"] = 100] = "d";
|
||||
CharacterCodes[CharacterCodes["e"] = 101] = "e";
|
||||
CharacterCodes[CharacterCodes["f"] = 102] = "f";
|
||||
CharacterCodes[CharacterCodes["g"] = 103] = "g";
|
||||
CharacterCodes[CharacterCodes["h"] = 104] = "h";
|
||||
CharacterCodes[CharacterCodes["i"] = 105] = "i";
|
||||
CharacterCodes[CharacterCodes["j"] = 106] = "j";
|
||||
CharacterCodes[CharacterCodes["k"] = 107] = "k";
|
||||
CharacterCodes[CharacterCodes["l"] = 108] = "l";
|
||||
CharacterCodes[CharacterCodes["m"] = 109] = "m";
|
||||
CharacterCodes[CharacterCodes["n"] = 110] = "n";
|
||||
CharacterCodes[CharacterCodes["o"] = 111] = "o";
|
||||
CharacterCodes[CharacterCodes["p"] = 112] = "p";
|
||||
CharacterCodes[CharacterCodes["q"] = 113] = "q";
|
||||
CharacterCodes[CharacterCodes["r"] = 114] = "r";
|
||||
CharacterCodes[CharacterCodes["s"] = 115] = "s";
|
||||
CharacterCodes[CharacterCodes["t"] = 116] = "t";
|
||||
CharacterCodes[CharacterCodes["u"] = 117] = "u";
|
||||
CharacterCodes[CharacterCodes["v"] = 118] = "v";
|
||||
CharacterCodes[CharacterCodes["w"] = 119] = "w";
|
||||
CharacterCodes[CharacterCodes["x"] = 120] = "x";
|
||||
CharacterCodes[CharacterCodes["y"] = 121] = "y";
|
||||
CharacterCodes[CharacterCodes["z"] = 122] = "z";
|
||||
CharacterCodes[CharacterCodes["A"] = 65] = "A";
|
||||
CharacterCodes[CharacterCodes["B"] = 66] = "B";
|
||||
CharacterCodes[CharacterCodes["C"] = 67] = "C";
|
||||
CharacterCodes[CharacterCodes["D"] = 68] = "D";
|
||||
CharacterCodes[CharacterCodes["E"] = 69] = "E";
|
||||
CharacterCodes[CharacterCodes["F"] = 70] = "F";
|
||||
CharacterCodes[CharacterCodes["G"] = 71] = "G";
|
||||
CharacterCodes[CharacterCodes["H"] = 72] = "H";
|
||||
CharacterCodes[CharacterCodes["I"] = 73] = "I";
|
||||
CharacterCodes[CharacterCodes["J"] = 74] = "J";
|
||||
CharacterCodes[CharacterCodes["K"] = 75] = "K";
|
||||
CharacterCodes[CharacterCodes["L"] = 76] = "L";
|
||||
CharacterCodes[CharacterCodes["M"] = 77] = "M";
|
||||
CharacterCodes[CharacterCodes["N"] = 78] = "N";
|
||||
CharacterCodes[CharacterCodes["O"] = 79] = "O";
|
||||
CharacterCodes[CharacterCodes["P"] = 80] = "P";
|
||||
CharacterCodes[CharacterCodes["Q"] = 81] = "Q";
|
||||
CharacterCodes[CharacterCodes["R"] = 82] = "R";
|
||||
CharacterCodes[CharacterCodes["S"] = 83] = "S";
|
||||
CharacterCodes[CharacterCodes["T"] = 84] = "T";
|
||||
CharacterCodes[CharacterCodes["U"] = 85] = "U";
|
||||
CharacterCodes[CharacterCodes["V"] = 86] = "V";
|
||||
CharacterCodes[CharacterCodes["W"] = 87] = "W";
|
||||
CharacterCodes[CharacterCodes["X"] = 88] = "X";
|
||||
CharacterCodes[CharacterCodes["Y"] = 89] = "Y";
|
||||
CharacterCodes[CharacterCodes["Z"] = 90] = "Z";
|
||||
CharacterCodes[CharacterCodes["ampersand"] = 38] = "ampersand";
|
||||
CharacterCodes[CharacterCodes["asterisk"] = 42] = "asterisk";
|
||||
CharacterCodes[CharacterCodes["at"] = 64] = "at";
|
||||
CharacterCodes[CharacterCodes["backslash"] = 92] = "backslash";
|
||||
CharacterCodes[CharacterCodes["backtick"] = 96] = "backtick";
|
||||
CharacterCodes[CharacterCodes["bar"] = 124] = "bar";
|
||||
CharacterCodes[CharacterCodes["caret"] = 94] = "caret";
|
||||
CharacterCodes[CharacterCodes["closeBrace"] = 125] = "closeBrace";
|
||||
CharacterCodes[CharacterCodes["closeBracket"] = 93] = "closeBracket";
|
||||
CharacterCodes[CharacterCodes["closeParen"] = 41] = "closeParen";
|
||||
CharacterCodes[CharacterCodes["colon"] = 58] = "colon";
|
||||
CharacterCodes[CharacterCodes["comma"] = 44] = "comma";
|
||||
CharacterCodes[CharacterCodes["dot"] = 46] = "dot";
|
||||
CharacterCodes[CharacterCodes["doubleQuote"] = 34] = "doubleQuote";
|
||||
CharacterCodes[CharacterCodes["equals"] = 61] = "equals";
|
||||
CharacterCodes[CharacterCodes["exclamation"] = 33] = "exclamation";
|
||||
CharacterCodes[CharacterCodes["greaterThan"] = 62] = "greaterThan";
|
||||
CharacterCodes[CharacterCodes["hash"] = 35] = "hash";
|
||||
CharacterCodes[CharacterCodes["lessThan"] = 60] = "lessThan";
|
||||
CharacterCodes[CharacterCodes["minus"] = 45] = "minus";
|
||||
CharacterCodes[CharacterCodes["openBrace"] = 123] = "openBrace";
|
||||
CharacterCodes[CharacterCodes["openBracket"] = 91] = "openBracket";
|
||||
CharacterCodes[CharacterCodes["openParen"] = 40] = "openParen";
|
||||
CharacterCodes[CharacterCodes["percent"] = 37] = "percent";
|
||||
CharacterCodes[CharacterCodes["plus"] = 43] = "plus";
|
||||
CharacterCodes[CharacterCodes["question"] = 63] = "question";
|
||||
CharacterCodes[CharacterCodes["semicolon"] = 59] = "semicolon";
|
||||
CharacterCodes[CharacterCodes["singleQuote"] = 39] = "singleQuote";
|
||||
CharacterCodes[CharacterCodes["slash"] = 47] = "slash";
|
||||
CharacterCodes[CharacterCodes["tilde"] = 126] = "tilde";
|
||||
CharacterCodes[CharacterCodes["backspace"] = 8] = "backspace";
|
||||
CharacterCodes[CharacterCodes["formFeed"] = 12] = "formFeed";
|
||||
CharacterCodes[CharacterCodes["byteOrderMark"] = 65279] = "byteOrderMark";
|
||||
CharacterCodes[CharacterCodes["tab"] = 9] = "tab";
|
||||
CharacterCodes[CharacterCodes["verticalTab"] = 11] = "verticalTab";
|
||||
})(CharacterCodes || (CharacterCodes = {}));
|
||||
//# sourceMappingURL=characterCodes.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"blake2b.js","sourceRoot":"","sources":["../src/blake2b.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,aAAa,CAAC;AAC7D,+DAA+D;AAC/D,MAAM,CAAC,MAAM,OAAO,GAAe,GAAG,CAAC;AACvC,+DAA+D;AAC/D,MAAM,CAAC,MAAM,OAAO,GAAe,GAAG,CAAC"}
|
||||
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
// THIS CODE WAS AUTOMATICALLY GENERATED
|
||||
// DO NOT EDIT THIS CODE BY HAND
|
||||
// SEE https://typescript-eslint.io/users/configs
|
||||
//
|
||||
// For developers working in the typescript-eslint monorepo:
|
||||
// You can regenerate it using `pnpm run generate-configs`
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const base_1 = __importDefault(require("./base"));
|
||||
const eslint_recommended_1 = __importDefault(require("./eslint-recommended"));
|
||||
/**
|
||||
* Rules considered to be best practice for modern TypeScript codebases, but that do not impact program logic.
|
||||
* @see {@link https://typescript-eslint.io/users/configs#stylistic}
|
||||
*/
|
||||
exports.default = (plugin, parser) => [
|
||||
(0, base_1.default)(plugin, parser),
|
||||
(0, eslint_recommended_1.default)(plugin, parser),
|
||||
{
|
||||
name: 'typescript-eslint/stylistic',
|
||||
rules: {
|
||||
'@typescript-eslint/adjacent-overload-signatures': 'error',
|
||||
'@typescript-eslint/array-type': 'error',
|
||||
'@typescript-eslint/ban-tslint-comment': 'error',
|
||||
'@typescript-eslint/class-literal-property-style': 'error',
|
||||
'@typescript-eslint/consistent-generic-constructors': 'error',
|
||||
'@typescript-eslint/consistent-indexed-object-style': 'error',
|
||||
'@typescript-eslint/consistent-type-assertions': 'error',
|
||||
'@typescript-eslint/consistent-type-definitions': 'error',
|
||||
'@typescript-eslint/no-confusing-non-null-assertion': 'error',
|
||||
'no-empty-function': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'error',
|
||||
'@typescript-eslint/no-inferrable-types': 'error',
|
||||
'@typescript-eslint/prefer-for-of': 'error',
|
||||
'@typescript-eslint/prefer-function-type': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,24 @@
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
|
||||
/// <reference no-default-lib="true"/>
|
||||
|
||||
/// <reference lib="es2018" />
|
||||
/// <reference lib="es2019.array" />
|
||||
/// <reference lib="es2019.object" />
|
||||
/// <reference lib="es2019.string" />
|
||||
/// <reference lib="es2019.symbol" />
|
||||
/// <reference lib="es2019.intl" />
|
||||
@@ -0,0 +1,52 @@
|
||||
'use strict'
|
||||
|
||||
const bench = require('fastbench')
|
||||
const pino = require('../')
|
||||
const bunyan = require('bunyan')
|
||||
const fs = require('node:fs')
|
||||
const dest = fs.createWriteStream('/dev/null')
|
||||
const plogNodeStream = pino(dest).child({ a: 'property' }).child({ sub: 'child' })
|
||||
delete require.cache[require.resolve('../')]
|
||||
const plogDest = require('../')(pino.destination('/dev/null')).child({ a: 'property' }).child({ sub: 'child' })
|
||||
delete require.cache[require.resolve('../')]
|
||||
const plogMinLength = require('../')(pino.destination({ dest: '/dev/null', sync: false, minLength: 4096 }))
|
||||
.child({ a: 'property' })
|
||||
.child({ sub: 'child' })
|
||||
|
||||
const max = 10
|
||||
const blog = bunyan.createLogger({
|
||||
name: 'myapp',
|
||||
streams: [{
|
||||
level: 'trace',
|
||||
stream: dest
|
||||
}]
|
||||
}).child({ a: 'property' }).child({ sub: 'child' })
|
||||
|
||||
const run = bench([
|
||||
function benchBunyanChildChild (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
blog.info({ hello: 'world' })
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchPinoChildChild (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
plogDest.info({ hello: 'world' })
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchPinoMinLengthChildChild (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
plogMinLength.info({ hello: 'world' })
|
||||
}
|
||||
setImmediate(cb)
|
||||
},
|
||||
function benchPinoNodeStreamChildChild (cb) {
|
||||
for (var i = 0; i < max; i++) {
|
||||
plogNodeStream.info({ hello: 'world' })
|
||||
}
|
||||
setImmediate(cb)
|
||||
}
|
||||
], 10000)
|
||||
|
||||
run(run)
|
||||
@@ -0,0 +1,4 @@
|
||||
function _objectDestructuringEmpty(t) {
|
||||
if (null == t) throw new TypeError("Cannot destructure " + t);
|
||||
}
|
||||
module.exports = _objectDestructuringEmpty, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,22 @@
|
||||
declare global {
|
||||
let suite: typeof import('vitest')['suite']
|
||||
let test: typeof import('vitest')['test']
|
||||
let chai: typeof import("vitest")["chai"]
|
||||
let describe: typeof import('vitest')['describe']
|
||||
let it: typeof import('vitest')['it']
|
||||
let expectTypeOf: typeof import('vitest')['expectTypeOf']
|
||||
let assertType: typeof import('vitest')['assertType']
|
||||
let expect: typeof import('vitest')['expect']
|
||||
let assert: typeof import('vitest')['assert']
|
||||
let vitest: typeof import('vitest')['vitest']
|
||||
let vi: typeof import('vitest')['vitest']
|
||||
let beforeAll: typeof import('vitest')['beforeAll']
|
||||
let afterAll: typeof import('vitest')['afterAll']
|
||||
let beforeEach: typeof import('vitest')['beforeEach']
|
||||
let afterEach: typeof import('vitest')['afterEach']
|
||||
let aroundEach: typeof import('vitest')['aroundEach']
|
||||
let aroundAll: typeof import('vitest')['aroundAll']
|
||||
let onTestFailed: typeof import('vitest')['onTestFailed']
|
||||
let onTestFinished: typeof import('vitest')['onTestFinished']
|
||||
}
|
||||
export {}
|
||||
@@ -0,0 +1,126 @@
|
||||
import { allProcessors } from "./json-schema-processors.js";
|
||||
import type * as JSONSchema from "./json-schema.js";
|
||||
import type { $ZodRegistry } from "./registries.js";
|
||||
import type * as schemas from "./schemas.js";
|
||||
import {
|
||||
type JSONSchemaGeneratorParams,
|
||||
type ProcessParams,
|
||||
type Seen,
|
||||
type ToJSONSchemaContext,
|
||||
extractDefs,
|
||||
finalize,
|
||||
initializeContext,
|
||||
process,
|
||||
} from "./to-json-schema.js";
|
||||
|
||||
/**
|
||||
* Parameters for the emit method of JSONSchemaGenerator.
|
||||
* @deprecated Use toJSONSchema function instead
|
||||
*/
|
||||
export type EmitParams = Pick<JSONSchemaGeneratorParams, "cycles" | "reused" | "external">;
|
||||
|
||||
/**
|
||||
* Parameters for JSONSchemaGenerator constructor.
|
||||
* @deprecated Use toJSONSchema function instead
|
||||
*/
|
||||
type JSONSchemaGeneratorConstructorParams = Pick<
|
||||
JSONSchemaGeneratorParams,
|
||||
"metadata" | "target" | "unrepresentable" | "override" | "io"
|
||||
>;
|
||||
|
||||
/**
|
||||
* Legacy class-based interface for JSON Schema generation.
|
||||
* This class wraps the new functional implementation to provide backward compatibility.
|
||||
*
|
||||
* @deprecated Use the `toJSONSchema` function instead for new code.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Legacy usage (still supported)
|
||||
* const gen = new JSONSchemaGenerator({ target: "draft-07" });
|
||||
* gen.process(schema);
|
||||
* const result = gen.emit(schema);
|
||||
*
|
||||
* // Preferred modern usage
|
||||
* const result = toJSONSchema(schema, { target: "draft-07" });
|
||||
* ```
|
||||
*/
|
||||
export class JSONSchemaGenerator {
|
||||
private ctx: ToJSONSchemaContext;
|
||||
|
||||
/** @deprecated Access via ctx instead */
|
||||
get metadataRegistry(): $ZodRegistry<Record<string, any>> {
|
||||
return this.ctx.metadataRegistry;
|
||||
}
|
||||
/** @deprecated Access via ctx instead */
|
||||
get target() {
|
||||
return this.ctx.target;
|
||||
}
|
||||
/** @deprecated Access via ctx instead */
|
||||
get unrepresentable() {
|
||||
return this.ctx.unrepresentable;
|
||||
}
|
||||
/** @deprecated Access via ctx instead */
|
||||
get override() {
|
||||
return this.ctx.override;
|
||||
}
|
||||
/** @deprecated Access via ctx instead */
|
||||
get io() {
|
||||
return this.ctx.io;
|
||||
}
|
||||
/** @deprecated Access via ctx instead */
|
||||
get counter() {
|
||||
return this.ctx.counter;
|
||||
}
|
||||
set counter(value: number) {
|
||||
this.ctx.counter = value;
|
||||
}
|
||||
/** @deprecated Access via ctx instead */
|
||||
get seen(): Map<schemas.$ZodType, Seen> {
|
||||
return this.ctx.seen;
|
||||
}
|
||||
|
||||
constructor(params?: JSONSchemaGeneratorConstructorParams) {
|
||||
// Normalize target for internal context
|
||||
let normalizedTarget: ToJSONSchemaContext["target"] = params?.target ?? "draft-2020-12";
|
||||
if (normalizedTarget === "draft-4") normalizedTarget = "draft-04";
|
||||
if (normalizedTarget === "draft-7") normalizedTarget = "draft-07";
|
||||
|
||||
this.ctx = initializeContext({
|
||||
processors: allProcessors,
|
||||
target: normalizedTarget,
|
||||
...(params?.metadata && { metadata: params.metadata }),
|
||||
...(params?.unrepresentable && { unrepresentable: params.unrepresentable }),
|
||||
...(params?.override && { override: params.override as any }),
|
||||
...(params?.io && { io: params.io }),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a schema to prepare it for JSON Schema generation.
|
||||
* This must be called before emit().
|
||||
*/
|
||||
process(schema: schemas.$ZodType, _params: ProcessParams = { path: [], schemaPath: [] }): JSONSchema.BaseSchema {
|
||||
return process(schema, this.ctx, _params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the final JSON Schema after processing.
|
||||
* Must call process() first.
|
||||
*/
|
||||
emit(schema: schemas.$ZodType, _params?: EmitParams): JSONSchema.BaseSchema {
|
||||
// Apply emit params to the context
|
||||
if (_params) {
|
||||
if (_params.cycles) this.ctx.cycles = _params.cycles;
|
||||
if (_params.reused) this.ctx.reused = _params.reused;
|
||||
if (_params.external) this.ctx.external = _params.external;
|
||||
}
|
||||
|
||||
extractDefs(this.ctx, schema);
|
||||
const result = finalize(this.ctx, schema);
|
||||
|
||||
// Strip ~standard property to match old implementation's return type
|
||||
const { "~standard": _, ...plainResult } = result as any;
|
||||
return plainResult as JSONSchema.BaseSchema;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,513 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const regexpp_1 = require("@eslint-community/regexpp");
|
||||
const utils_1 = require("@typescript-eslint/utils");
|
||||
const util_1 = require("../util");
|
||||
const EQ_OPERATORS = /^[=!]=/;
|
||||
const regexpp = new regexpp_1.RegExpParser();
|
||||
exports.default = (0, util_1.createRule)({
|
||||
name: 'prefer-string-starts-ends-with',
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description: 'Enforce using `String#startsWith` and `String#endsWith` over other equivalent methods of checking substrings',
|
||||
recommended: 'stylistic',
|
||||
requiresTypeChecking: true,
|
||||
},
|
||||
fixable: 'code',
|
||||
messages: {
|
||||
preferEndsWith: "Use the 'String#endsWith' method instead.",
|
||||
preferStartsWith: "Use 'String#startsWith' method instead.",
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
allowSingleElementEquality: {
|
||||
type: 'string',
|
||||
description: 'Whether to allow equality checks against the first or last element of a string.',
|
||||
enum: ['always', 'never'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
defaultOptions: [{ allowSingleElementEquality: 'never' }],
|
||||
create(context, [{ allowSingleElementEquality }]) {
|
||||
const globalScope = context.sourceCode.getScope(context.sourceCode.ast);
|
||||
const services = (0, util_1.getParserServices)(context);
|
||||
const checker = services.program.getTypeChecker();
|
||||
/**
|
||||
* Check if a given node is a string.
|
||||
* @param node The node to check.
|
||||
*/
|
||||
function isStringType(node) {
|
||||
const objectType = services.getTypeAtLocation(node);
|
||||
return (0, util_1.getTypeName)(checker, objectType) === 'string';
|
||||
}
|
||||
/**
|
||||
* Check if a given node is a `Literal` node that is null.
|
||||
* @param node The node to check.
|
||||
*/
|
||||
function isNull(node) {
|
||||
const evaluated = (0, util_1.getStaticValue)(node, globalScope);
|
||||
return evaluated != null && evaluated.value == null;
|
||||
}
|
||||
/**
|
||||
* Check if a given node is a `Literal` node that is a given value.
|
||||
* @param node The node to check.
|
||||
* @param value The expected value of the `Literal` node.
|
||||
*/
|
||||
function isNumber(node, value) {
|
||||
const evaluated = (0, util_1.getStaticValue)(node, globalScope);
|
||||
return evaluated?.value === value;
|
||||
}
|
||||
/**
|
||||
* Check if a given node is a `Literal` node that is a character.
|
||||
* @param node The node to check.
|
||||
*/
|
||||
function isCharacter(node) {
|
||||
const evaluated = (0, util_1.getStaticValue)(node, globalScope);
|
||||
return (evaluated != null &&
|
||||
typeof evaluated.value === 'string' &&
|
||||
// checks if the string is a character long
|
||||
evaluated.value[0] === evaluated.value);
|
||||
}
|
||||
/**
|
||||
* Check if a given node is `==`, `===`, `!=`, or `!==`.
|
||||
* @param node The node to check.
|
||||
*/
|
||||
function isEqualityComparison(node) {
|
||||
return (node.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
|
||||
EQ_OPERATORS.test(node.operator));
|
||||
}
|
||||
/**
|
||||
* Check if two given nodes are the same meaning.
|
||||
* @param node1 A node to compare.
|
||||
* @param node2 Another node to compare.
|
||||
*/
|
||||
function isSameTokens(node1, node2) {
|
||||
const tokens1 = context.sourceCode.getTokens(node1);
|
||||
const tokens2 = context.sourceCode.getTokens(node2);
|
||||
if (tokens1.length !== tokens2.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < tokens1.length; ++i) {
|
||||
const token1 = tokens1[i];
|
||||
const token2 = tokens2[i];
|
||||
if (token1.type !== token2.type || token1.value !== token2.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Check if a given node is the expression of the length of a string.
|
||||
*
|
||||
* - If `length` property access of `expectedObjectNode`, it's `true`.
|
||||
* E.g., `foo` → `foo.length` / `"foo"` → `"foo".length`
|
||||
* - If `expectedObjectNode` is a string literal, `node` can be a number.
|
||||
* E.g., `"foo"` → `3`
|
||||
*
|
||||
* @param node The node to check.
|
||||
* @param expectedObjectNode The node which is expected as the receiver of `length` property.
|
||||
*/
|
||||
function isLengthExpression(node, expectedObjectNode) {
|
||||
if (node.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
||||
return ((0, util_1.getPropertyName)(node, globalScope) === 'length' &&
|
||||
isSameTokens(node.object, expectedObjectNode));
|
||||
}
|
||||
const evaluatedLength = (0, util_1.getStaticValue)(node, globalScope);
|
||||
const evaluatedString = (0, util_1.getStaticValue)(expectedObjectNode, globalScope);
|
||||
return (evaluatedLength != null &&
|
||||
evaluatedString != null &&
|
||||
typeof evaluatedLength.value === 'number' &&
|
||||
typeof evaluatedString.value === 'string' &&
|
||||
evaluatedLength.value === evaluatedString.value.length);
|
||||
}
|
||||
/**
|
||||
* Returns true if `node` is `-substring.length` or
|
||||
* `parentString.length - substring.length`
|
||||
*/
|
||||
function isLengthAheadOfEnd(node, substring, parentString) {
|
||||
return ((node.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
|
||||
node.operator === '-' &&
|
||||
isLengthExpression(node.argument, substring)) ||
|
||||
(node.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
|
||||
node.operator === '-' &&
|
||||
isLengthExpression(node.left, parentString) &&
|
||||
isLengthExpression(node.right, substring)));
|
||||
}
|
||||
/**
|
||||
* Check if a given node is the expression of the last index.
|
||||
*
|
||||
* E.g. `foo.length - 1`
|
||||
*
|
||||
* @param node The node to check.
|
||||
* @param expectedObjectNode The node which is expected as the receiver of `length` property.
|
||||
*/
|
||||
function isLastIndexExpression(node, expectedObjectNode) {
|
||||
return (node.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
|
||||
node.operator === '-' &&
|
||||
isLengthExpression(node.left, expectedObjectNode) &&
|
||||
isNumber(node.right, 1));
|
||||
}
|
||||
/**
|
||||
* Get the range of the property of a given `MemberExpression` node.
|
||||
*
|
||||
* - `obj[foo]` → the range of `[foo]`
|
||||
* - `obf.foo` → the range of `.foo`
|
||||
* - `(obj).foo` → the range of `.foo`
|
||||
*
|
||||
* @param node The member expression node to get.
|
||||
*/
|
||||
function getPropertyRange(node) {
|
||||
const dotOrOpenBracket = (0, util_1.nullThrows)(context.sourceCode.getTokenAfter(node.object, util_1.isNotClosingParenToken), util_1.NullThrowsReasons.MissingToken('closing parenthesis', 'member'));
|
||||
return [dotOrOpenBracket.range[0], node.range[1]];
|
||||
}
|
||||
/**
|
||||
* Parse a given `RegExp` pattern to that string if it's a static string.
|
||||
* @param pattern The RegExp pattern text to parse.
|
||||
* @param unicode Whether the RegExp is unicode.
|
||||
*/
|
||||
function parseRegExpText(pattern, unicode) {
|
||||
// Parse it.
|
||||
const ast = regexpp.parsePattern(pattern, undefined, undefined, {
|
||||
unicode,
|
||||
});
|
||||
if (ast.alternatives.length !== 1) {
|
||||
return null;
|
||||
}
|
||||
// Drop `^`/`$` assertion.
|
||||
const chars = ast.alternatives[0].elements;
|
||||
const first = chars[0];
|
||||
if (first.type === 'Assertion' && first.kind === 'start') {
|
||||
chars.shift();
|
||||
}
|
||||
else {
|
||||
chars.pop();
|
||||
}
|
||||
// Check if it can determine a unique string.
|
||||
if (!chars.every(c => c.type === 'Character')) {
|
||||
return null;
|
||||
}
|
||||
// To string.
|
||||
return String.fromCodePoint(...chars.map(c => c.value));
|
||||
}
|
||||
/**
|
||||
* Parse a given node if it's a `RegExp` instance.
|
||||
* @param node The node to parse.
|
||||
*/
|
||||
function parseRegExp(node) {
|
||||
const evaluated = (0, util_1.getStaticValue)(node, globalScope);
|
||||
if (evaluated == null || !(evaluated.value instanceof RegExp)) {
|
||||
return null;
|
||||
}
|
||||
const { flags, source } = evaluated.value;
|
||||
const isStartsWith = source.startsWith('^');
|
||||
// ends with a $ preceded by an even number of backslashes (or zero)
|
||||
const isEndsWith = /[^\\](\\\\)*\$$/.test(source);
|
||||
if (isStartsWith === isEndsWith ||
|
||||
flags.includes('i') ||
|
||||
flags.includes('m')) {
|
||||
return null;
|
||||
}
|
||||
const text = parseRegExpText(source, flags.includes('u'));
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
return { isEndsWith, isStartsWith, text };
|
||||
}
|
||||
function getLeftNode(init) {
|
||||
const node = (0, util_1.skipChainExpression)(init);
|
||||
const leftNode = node.type === utils_1.AST_NODE_TYPES.CallExpression ? node.callee : node;
|
||||
if (leftNode.type !== utils_1.AST_NODE_TYPES.MemberExpression) {
|
||||
throw new Error(`Expected a MemberExpression, got ${leftNode.type}`);
|
||||
}
|
||||
return leftNode;
|
||||
}
|
||||
/**
|
||||
* Fix code with using the right operand as the search string.
|
||||
* For example: `foo.slice(0, 3) === 'bar'` → `foo.startsWith('bar')`
|
||||
* @param fixer The rule fixer.
|
||||
* @param node The node which was reported.
|
||||
* @param kind The kind of the report.
|
||||
* @param isNegative The flag to fix to negative condition.
|
||||
*/
|
||||
function* fixWithRightOperand(fixer, node, kind, isNegative, isOptional) {
|
||||
// left is CallExpression or MemberExpression.
|
||||
const leftNode = getLeftNode(node.left);
|
||||
const propertyRange = getPropertyRange(leftNode);
|
||||
if (isNegative) {
|
||||
yield fixer.insertTextBefore(node, '!');
|
||||
}
|
||||
yield fixer.replaceTextRange([propertyRange[0], node.right.range[0]], `${isOptional ? '?.' : '.'}${kind}sWith(`);
|
||||
yield fixer.replaceTextRange([node.right.range[1], node.range[1]], ')');
|
||||
}
|
||||
/**
|
||||
* Fix code with using the first argument as the search string.
|
||||
* For example: `foo.indexOf('bar') === 0` → `foo.startsWith('bar')`
|
||||
* @param fixer The rule fixer.
|
||||
* @param node The node which was reported.
|
||||
* @param kind The kind of the report.
|
||||
* @param negative The flag to fix to negative condition.
|
||||
*/
|
||||
function* fixWithArgument(fixer, node, callNode, calleeNode, kind, negative, isOptional) {
|
||||
if (negative) {
|
||||
yield fixer.insertTextBefore(node, '!');
|
||||
}
|
||||
yield fixer.replaceTextRange(getPropertyRange(calleeNode), `${isOptional ? '?.' : '.'}${kind}sWith`);
|
||||
yield fixer.removeRange([callNode.range[1], node.range[1]]);
|
||||
}
|
||||
function getParent(node) {
|
||||
return node.parent.type === utils_1.AST_NODE_TYPES.ChainExpression
|
||||
? node.parent.parent
|
||||
: node.parent;
|
||||
}
|
||||
return {
|
||||
// foo[0] === "a"
|
||||
// foo.charAt(0) === "a"
|
||||
// foo[foo.length - 1] === "a"
|
||||
// foo.charAt(foo.length - 1) === "a"
|
||||
[[
|
||||
'BinaryExpression > MemberExpression.left[computed=true]',
|
||||
'BinaryExpression > CallExpression.left > MemberExpression.callee[property.name="charAt"][computed=false]',
|
||||
'BinaryExpression > ChainExpression.left > MemberExpression[computed=true]',
|
||||
'BinaryExpression > ChainExpression.left > CallExpression > MemberExpression.callee[property.name="charAt"][computed=false]',
|
||||
].join(', ')](node) {
|
||||
let parentNode = getParent(node);
|
||||
let indexNode = null;
|
||||
if (parentNode.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
||||
if (parentNode.arguments.length === 1) {
|
||||
indexNode = parentNode.arguments[0];
|
||||
}
|
||||
parentNode = getParent(parentNode);
|
||||
}
|
||||
else {
|
||||
indexNode = node.property;
|
||||
}
|
||||
if (indexNode == null ||
|
||||
!isEqualityComparison(parentNode) ||
|
||||
!isStringType(node.object)) {
|
||||
return;
|
||||
}
|
||||
const isEndsWith = isLastIndexExpression(indexNode, node.object);
|
||||
if (allowSingleElementEquality === 'always' && isEndsWith) {
|
||||
return;
|
||||
}
|
||||
const isStartsWith = !isEndsWith && isNumber(indexNode, 0);
|
||||
if ((allowSingleElementEquality === 'always' && isStartsWith) ||
|
||||
(!isStartsWith && !isEndsWith)) {
|
||||
return;
|
||||
}
|
||||
const eqNode = parentNode;
|
||||
context.report({
|
||||
node: parentNode,
|
||||
messageId: isStartsWith ? 'preferStartsWith' : 'preferEndsWith',
|
||||
fix(fixer) {
|
||||
// Don't fix if it can change the behavior.
|
||||
if (!isCharacter(eqNode.right)) {
|
||||
return null;
|
||||
}
|
||||
return fixWithRightOperand(fixer, eqNode, isStartsWith ? 'start' : 'end', eqNode.operator.startsWith('!'), node.optional);
|
||||
},
|
||||
});
|
||||
},
|
||||
// foo.indexOf('bar') === 0
|
||||
[[
|
||||
'BinaryExpression > CallExpression.left > MemberExpression.callee[property.name="indexOf"][computed=false]',
|
||||
'BinaryExpression > ChainExpression.left > CallExpression > MemberExpression.callee[property.name="indexOf"][computed=false]',
|
||||
].join(', ')](node) {
|
||||
const callNode = getParent(node);
|
||||
const parentNode = getParent(callNode);
|
||||
if (callNode.arguments.length !== 1 ||
|
||||
!isEqualityComparison(parentNode) ||
|
||||
!isNumber(parentNode.right, 0) ||
|
||||
!isStringType(node.object)) {
|
||||
return;
|
||||
}
|
||||
context.report({
|
||||
node: parentNode,
|
||||
messageId: 'preferStartsWith',
|
||||
fix(fixer) {
|
||||
return fixWithArgument(fixer, parentNode, callNode, node, 'start', parentNode.operator.startsWith('!'), node.optional);
|
||||
},
|
||||
});
|
||||
},
|
||||
// foo.lastIndexOf('bar') === foo.length - 3
|
||||
// foo.lastIndexOf(bar) === foo.length - bar.length
|
||||
[[
|
||||
'BinaryExpression > CallExpression.left > MemberExpression.callee[property.name="lastIndexOf"][computed=false]',
|
||||
'BinaryExpression > ChainExpression.left > CallExpression > MemberExpression.callee[property.name="lastIndexOf"][computed=false]',
|
||||
].join(', ')](node) {
|
||||
const callNode = getParent(node);
|
||||
const parentNode = getParent(callNode);
|
||||
if (callNode.arguments.length !== 1 ||
|
||||
!isEqualityComparison(parentNode) ||
|
||||
parentNode.right.type !== utils_1.AST_NODE_TYPES.BinaryExpression ||
|
||||
parentNode.right.operator !== '-' ||
|
||||
!isLengthExpression(parentNode.right.left, node.object) ||
|
||||
!isLengthExpression(parentNode.right.right, callNode.arguments[0]) ||
|
||||
!isStringType(node.object)) {
|
||||
return;
|
||||
}
|
||||
context.report({
|
||||
node: parentNode,
|
||||
messageId: 'preferEndsWith',
|
||||
fix(fixer) {
|
||||
return fixWithArgument(fixer, parentNode, callNode, node, 'end', parentNode.operator.startsWith('!'), node.optional);
|
||||
},
|
||||
});
|
||||
},
|
||||
// foo.match(/^bar/) === null
|
||||
// foo.match(/bar$/) === null
|
||||
[[
|
||||
'BinaryExpression > CallExpression.left > MemberExpression.callee[property.name="match"][computed=false]',
|
||||
'BinaryExpression > ChainExpression.left > CallExpression > MemberExpression.callee[property.name="match"][computed=false]',
|
||||
].join(', ')](node) {
|
||||
const callNode = getParent(node);
|
||||
const parentNode = getParent(callNode);
|
||||
if (!isNull(parentNode.right) || !isStringType(node.object)) {
|
||||
return;
|
||||
}
|
||||
const parsed = callNode.arguments.length === 1
|
||||
? parseRegExp(callNode.arguments[0])
|
||||
: null;
|
||||
if (parsed == null) {
|
||||
return;
|
||||
}
|
||||
const { isStartsWith, text } = parsed;
|
||||
context.report({
|
||||
node: callNode,
|
||||
messageId: isStartsWith ? 'preferStartsWith' : 'preferEndsWith',
|
||||
*fix(fixer) {
|
||||
if (!parentNode.operator.startsWith('!')) {
|
||||
yield fixer.insertTextBefore(parentNode, '!');
|
||||
}
|
||||
yield fixer.replaceTextRange(getPropertyRange(node), `${node.optional ? '?.' : '.'}${isStartsWith ? 'start' : 'end'}sWith`);
|
||||
yield fixer.replaceText(callNode.arguments[0], JSON.stringify(text));
|
||||
yield fixer.removeRange([callNode.range[1], parentNode.range[1]]);
|
||||
},
|
||||
});
|
||||
},
|
||||
// foo.slice(0, 3) === 'bar'
|
||||
// foo.slice(-3) === 'bar'
|
||||
// foo.slice(-3, foo.length) === 'bar'
|
||||
// foo.substring(0, 3) === 'bar'
|
||||
// foo.substring(foo.length - 3) === 'bar'
|
||||
// foo.substring(foo.length - 3, foo.length) === 'bar'
|
||||
[[
|
||||
'BinaryExpression > CallExpression.left > MemberExpression',
|
||||
'BinaryExpression > ChainExpression.left > CallExpression > MemberExpression',
|
||||
].join(', ')](node) {
|
||||
if (!(0, util_1.isStaticMemberAccessOfValue)(node, context, 'slice', 'substring')) {
|
||||
return;
|
||||
}
|
||||
const callNode = getParent(node);
|
||||
const parentNode = getParent(callNode);
|
||||
if (!isEqualityComparison(parentNode) || !isStringType(node.object)) {
|
||||
return;
|
||||
}
|
||||
let isEndsWith = false;
|
||||
let isStartsWith = false;
|
||||
if (callNode.arguments.length === 1) {
|
||||
if (
|
||||
// foo.slice(-bar.length) === bar
|
||||
// foo.slice(foo.length - bar.length) === bar
|
||||
isLengthAheadOfEnd(callNode.arguments[0], parentNode.right, node.object)) {
|
||||
isEndsWith = true;
|
||||
}
|
||||
}
|
||||
else if (callNode.arguments.length === 2) {
|
||||
if (
|
||||
// foo.slice(0, bar.length) === bar
|
||||
isNumber(callNode.arguments[0], 0) &&
|
||||
isLengthExpression(callNode.arguments[1], parentNode.right)) {
|
||||
isStartsWith = true;
|
||||
}
|
||||
else if (
|
||||
// foo.slice(foo.length - bar.length, foo.length) === bar
|
||||
// foo.slice(foo.length - bar.length, 0) === bar
|
||||
// foo.slice(-bar.length, foo.length) === bar
|
||||
// foo.slice(-bar.length, 0) === bar
|
||||
(isLengthExpression(callNode.arguments[1], node.object) ||
|
||||
isNumber(callNode.arguments[1], 0)) &&
|
||||
isLengthAheadOfEnd(callNode.arguments[0], parentNode.right, node.object)) {
|
||||
isEndsWith = true;
|
||||
}
|
||||
}
|
||||
if (!isStartsWith && !isEndsWith) {
|
||||
return;
|
||||
}
|
||||
const eqNode = parentNode;
|
||||
const negativeIndexSupported = node.property.name === 'slice';
|
||||
context.report({
|
||||
node: parentNode,
|
||||
messageId: isStartsWith ? 'preferStartsWith' : 'preferEndsWith',
|
||||
fix(fixer) {
|
||||
// Don't fix if it can change the behavior.
|
||||
if (eqNode.operator.length === 2 &&
|
||||
(eqNode.right.type !== utils_1.AST_NODE_TYPES.Literal ||
|
||||
typeof eqNode.right.value !== 'string')) {
|
||||
return null;
|
||||
}
|
||||
// code being checked is likely mistake:
|
||||
// unequal length of strings being checked for equality
|
||||
// or reliant on behavior of substring (negative indices interpreted as 0)
|
||||
if (isStartsWith) {
|
||||
if (!isLengthExpression(callNode.arguments[1], eqNode.right)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const posNode = callNode.arguments[0];
|
||||
const posNodeIsAbsolutelyValid = (posNode.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
|
||||
posNode.operator === '-' &&
|
||||
isLengthExpression(posNode.left, node.object) &&
|
||||
isLengthExpression(posNode.right, eqNode.right)) ||
|
||||
(negativeIndexSupported &&
|
||||
posNode.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
|
||||
posNode.operator === '-' &&
|
||||
isLengthExpression(posNode.argument, eqNode.right));
|
||||
if (!posNodeIsAbsolutelyValid) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return fixWithRightOperand(fixer, parentNode, isStartsWith ? 'start' : 'end', parentNode.operator.startsWith('!'), node.optional);
|
||||
},
|
||||
});
|
||||
},
|
||||
// /^bar/.test(foo)
|
||||
// /bar$/.test(foo)
|
||||
'CallExpression > MemberExpression.callee[property.name="test"][computed=false]'(node) {
|
||||
const callNode = getParent(node);
|
||||
const parsed = callNode.arguments.length === 1 ? parseRegExp(node.object) : null;
|
||||
if (parsed == null) {
|
||||
return;
|
||||
}
|
||||
const { isStartsWith, text } = parsed;
|
||||
const messageId = isStartsWith ? 'preferStartsWith' : 'preferEndsWith';
|
||||
const methodName = isStartsWith ? 'startsWith' : 'endsWith';
|
||||
context.report({
|
||||
node: callNode,
|
||||
messageId,
|
||||
*fix(fixer) {
|
||||
const argNode = callNode.arguments[0];
|
||||
const needsParen = argNode.type !== utils_1.AST_NODE_TYPES.Literal &&
|
||||
argNode.type !== utils_1.AST_NODE_TYPES.TemplateLiteral &&
|
||||
argNode.type !== utils_1.AST_NODE_TYPES.Identifier &&
|
||||
argNode.type !== utils_1.AST_NODE_TYPES.MemberExpression &&
|
||||
argNode.type !== utils_1.AST_NODE_TYPES.CallExpression;
|
||||
yield fixer.removeRange([callNode.range[0], argNode.range[0]]);
|
||||
if (needsParen) {
|
||||
yield fixer.insertTextBefore(argNode, '(');
|
||||
yield fixer.insertTextAfter(argNode, ')');
|
||||
}
|
||||
yield fixer.insertTextAfter(argNode, `${node.optional ? '?.' : '.'}${methodName}(${JSON.stringify(text)}`);
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
'use strict'
|
||||
|
||||
// just pre-load all the stuff that index.js lazily exports
|
||||
const internalRe = require('./internal/re')
|
||||
const constants = require('./internal/constants')
|
||||
const SemVer = require('./classes/semver')
|
||||
const identifiers = require('./internal/identifiers')
|
||||
const parse = require('./functions/parse')
|
||||
const valid = require('./functions/valid')
|
||||
const clean = require('./functions/clean')
|
||||
const inc = require('./functions/inc')
|
||||
const diff = require('./functions/diff')
|
||||
const major = require('./functions/major')
|
||||
const minor = require('./functions/minor')
|
||||
const patch = require('./functions/patch')
|
||||
const prerelease = require('./functions/prerelease')
|
||||
const compare = require('./functions/compare')
|
||||
const rcompare = require('./functions/rcompare')
|
||||
const compareLoose = require('./functions/compare-loose')
|
||||
const compareBuild = require('./functions/compare-build')
|
||||
const sort = require('./functions/sort')
|
||||
const rsort = require('./functions/rsort')
|
||||
const gt = require('./functions/gt')
|
||||
const lt = require('./functions/lt')
|
||||
const eq = require('./functions/eq')
|
||||
const neq = require('./functions/neq')
|
||||
const gte = require('./functions/gte')
|
||||
const lte = require('./functions/lte')
|
||||
const cmp = require('./functions/cmp')
|
||||
const coerce = require('./functions/coerce')
|
||||
const truncate = require('./functions/truncate')
|
||||
const Comparator = require('./classes/comparator')
|
||||
const Range = require('./classes/range')
|
||||
const satisfies = require('./functions/satisfies')
|
||||
const toComparators = require('./ranges/to-comparators')
|
||||
const maxSatisfying = require('./ranges/max-satisfying')
|
||||
const minSatisfying = require('./ranges/min-satisfying')
|
||||
const minVersion = require('./ranges/min-version')
|
||||
const validRange = require('./ranges/valid')
|
||||
const outside = require('./ranges/outside')
|
||||
const gtr = require('./ranges/gtr')
|
||||
const ltr = require('./ranges/ltr')
|
||||
const intersects = require('./ranges/intersects')
|
||||
const simplifyRange = require('./ranges/simplify')
|
||||
const subset = require('./ranges/subset')
|
||||
module.exports = {
|
||||
parse,
|
||||
valid,
|
||||
clean,
|
||||
inc,
|
||||
diff,
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
prerelease,
|
||||
compare,
|
||||
rcompare,
|
||||
compareLoose,
|
||||
compareBuild,
|
||||
sort,
|
||||
rsort,
|
||||
gt,
|
||||
lt,
|
||||
eq,
|
||||
neq,
|
||||
gte,
|
||||
lte,
|
||||
cmp,
|
||||
coerce,
|
||||
truncate,
|
||||
Comparator,
|
||||
Range,
|
||||
satisfies,
|
||||
toComparators,
|
||||
maxSatisfying,
|
||||
minSatisfying,
|
||||
minVersion,
|
||||
validRange,
|
||||
outside,
|
||||
gtr,
|
||||
ltr,
|
||||
intersects,
|
||||
simplifyRange,
|
||||
subset,
|
||||
SemVer,
|
||||
re: internalRe.re,
|
||||
src: internalRe.src,
|
||||
tokens: internalRe.t,
|
||||
SEMVER_SPEC_VERSION: constants.SEMVER_SPEC_VERSION,
|
||||
RELEASE_TYPES: constants.RELEASE_TYPES,
|
||||
compareIdentifiers: identifiers.compareIdentifiers,
|
||||
rcompareIdentifiers: identifiers.rcompareIdentifiers,
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
import {Buffer} from 'buffer';
|
||||
import * as BufferLayout from '@solana/buffer-layout';
|
||||
|
||||
import {PublicKey} from './publickey';
|
||||
import {Transaction, PACKET_DATA_SIZE} from './transaction';
|
||||
import {MS_PER_SLOT} from './timing';
|
||||
import {SYSVAR_RENT_PUBKEY} from './sysvar';
|
||||
import {sendAndConfirmTransaction} from './utils/send-and-confirm-transaction';
|
||||
import {sleep} from './utils/sleep';
|
||||
import type {Connection} from './connection';
|
||||
import type {Signer} from './keypair';
|
||||
import {SystemProgram} from './programs/system';
|
||||
import {IInstructionInputData} from './instruction';
|
||||
|
||||
// Keep program chunks under PACKET_DATA_SIZE, leaving enough room for the
|
||||
// rest of the Transaction fields
|
||||
//
|
||||
// TODO: replace 300 with a proper constant for the size of the other
|
||||
// Transaction fields
|
||||
const CHUNK_SIZE = PACKET_DATA_SIZE - 300;
|
||||
|
||||
/**
|
||||
* Program loader interface
|
||||
*/
|
||||
export class Loader {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* Amount of program data placed in each load Transaction
|
||||
*/
|
||||
static chunkSize: number = CHUNK_SIZE;
|
||||
|
||||
/**
|
||||
* Minimum number of signatures required to load a program not including
|
||||
* retries
|
||||
*
|
||||
* Can be used to calculate transaction fees
|
||||
*/
|
||||
static getMinNumSignatures(dataLength: number): number {
|
||||
return (
|
||||
2 * // Every transaction requires two signatures (payer + program)
|
||||
(Math.ceil(dataLength / Loader.chunkSize) +
|
||||
1 + // Add one for Create transaction
|
||||
1) // Add one for Finalize transaction
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a generic program
|
||||
*
|
||||
* @param connection The connection to use
|
||||
* @param payer System account that pays to load the program
|
||||
* @param program Account to load the program into
|
||||
* @param programId Public key that identifies the loader
|
||||
* @param data Program octets
|
||||
* @return true if program was loaded successfully, false if program was already loaded
|
||||
*/
|
||||
static async load(
|
||||
connection: Connection,
|
||||
payer: Signer,
|
||||
program: Signer,
|
||||
programId: PublicKey,
|
||||
data: Buffer | Uint8Array | Array<number>,
|
||||
): Promise<boolean> {
|
||||
{
|
||||
const balanceNeeded = await connection.getMinimumBalanceForRentExemption(
|
||||
data.length,
|
||||
);
|
||||
|
||||
// Fetch program account info to check if it has already been created
|
||||
const programInfo = await connection.getAccountInfo(
|
||||
program.publicKey,
|
||||
'confirmed',
|
||||
);
|
||||
|
||||
let transaction: Transaction | null = null;
|
||||
if (programInfo !== null) {
|
||||
if (programInfo.executable) {
|
||||
console.error('Program load failed, account is already executable');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (programInfo.data.length !== data.length) {
|
||||
transaction = transaction || new Transaction();
|
||||
transaction.add(
|
||||
SystemProgram.allocate({
|
||||
accountPubkey: program.publicKey,
|
||||
space: data.length,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (!programInfo.owner.equals(programId)) {
|
||||
transaction = transaction || new Transaction();
|
||||
transaction.add(
|
||||
SystemProgram.assign({
|
||||
accountPubkey: program.publicKey,
|
||||
programId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (programInfo.lamports < balanceNeeded) {
|
||||
transaction = transaction || new Transaction();
|
||||
transaction.add(
|
||||
SystemProgram.transfer({
|
||||
fromPubkey: payer.publicKey,
|
||||
toPubkey: program.publicKey,
|
||||
lamports: balanceNeeded - programInfo.lamports,
|
||||
}),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
transaction = new Transaction().add(
|
||||
SystemProgram.createAccount({
|
||||
fromPubkey: payer.publicKey,
|
||||
newAccountPubkey: program.publicKey,
|
||||
lamports: balanceNeeded > 0 ? balanceNeeded : 1,
|
||||
space: data.length,
|
||||
programId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// If the account is already created correctly, skip this step
|
||||
// and proceed directly to loading instructions
|
||||
if (transaction !== null) {
|
||||
await sendAndConfirmTransaction(
|
||||
connection,
|
||||
transaction,
|
||||
[payer, program],
|
||||
{
|
||||
commitment: 'confirmed',
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const dataLayout = BufferLayout.struct<
|
||||
Readonly<{
|
||||
bytes: number[];
|
||||
bytesLength: number;
|
||||
bytesLengthPadding: number;
|
||||
instruction: number;
|
||||
offset: number;
|
||||
}>
|
||||
>([
|
||||
BufferLayout.u32('instruction'),
|
||||
BufferLayout.u32('offset'),
|
||||
BufferLayout.u32('bytesLength'),
|
||||
BufferLayout.u32('bytesLengthPadding'),
|
||||
BufferLayout.seq(
|
||||
BufferLayout.u8('byte'),
|
||||
BufferLayout.offset(BufferLayout.u32(), -8),
|
||||
'bytes',
|
||||
),
|
||||
]);
|
||||
|
||||
const chunkSize = Loader.chunkSize;
|
||||
let offset = 0;
|
||||
let array = data;
|
||||
let transactions = [];
|
||||
while (array.length > 0) {
|
||||
const bytes = array.slice(0, chunkSize);
|
||||
const data = Buffer.alloc(chunkSize + 16);
|
||||
dataLayout.encode(
|
||||
{
|
||||
instruction: 0, // Load instruction
|
||||
offset,
|
||||
bytes: bytes as number[],
|
||||
bytesLength: 0,
|
||||
bytesLengthPadding: 0,
|
||||
},
|
||||
data,
|
||||
);
|
||||
|
||||
const transaction = new Transaction().add({
|
||||
keys: [{pubkey: program.publicKey, isSigner: true, isWritable: true}],
|
||||
programId,
|
||||
data,
|
||||
});
|
||||
transactions.push(
|
||||
sendAndConfirmTransaction(connection, transaction, [payer, program], {
|
||||
commitment: 'confirmed',
|
||||
}),
|
||||
);
|
||||
|
||||
// Delay between sends in an attempt to reduce rate limit errors
|
||||
if (connection._rpcEndpoint.includes('solana.com')) {
|
||||
const REQUESTS_PER_SECOND = 4;
|
||||
await sleep(1000 / REQUESTS_PER_SECOND);
|
||||
}
|
||||
|
||||
offset += chunkSize;
|
||||
array = array.slice(chunkSize);
|
||||
}
|
||||
await Promise.all(transactions);
|
||||
|
||||
// Finalize the account loaded with program data for execution
|
||||
{
|
||||
const dataLayout = BufferLayout.struct<IInstructionInputData>([
|
||||
BufferLayout.u32('instruction'),
|
||||
]);
|
||||
|
||||
const data = Buffer.alloc(dataLayout.span);
|
||||
dataLayout.encode(
|
||||
{
|
||||
instruction: 1, // Finalize instruction
|
||||
},
|
||||
data,
|
||||
);
|
||||
|
||||
const transaction = new Transaction().add({
|
||||
keys: [
|
||||
{pubkey: program.publicKey, isSigner: true, isWritable: true},
|
||||
{pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false},
|
||||
],
|
||||
programId,
|
||||
data,
|
||||
});
|
||||
const deployCommitment = 'processed';
|
||||
const finalizeSignature = await connection.sendTransaction(
|
||||
transaction,
|
||||
[payer, program],
|
||||
{preflightCommitment: deployCommitment},
|
||||
);
|
||||
const {context, value} = await connection.confirmTransaction(
|
||||
{
|
||||
signature: finalizeSignature,
|
||||
lastValidBlockHeight: transaction.lastValidBlockHeight!,
|
||||
blockhash: transaction.recentBlockhash!,
|
||||
},
|
||||
deployCommitment,
|
||||
);
|
||||
if (value.err) {
|
||||
throw new Error(
|
||||
`Transaction ${finalizeSignature} failed (${JSON.stringify(value)})`,
|
||||
);
|
||||
}
|
||||
// We prevent programs from being usable until the slot after their deployment.
|
||||
// See https://github.com/solana-labs/solana/pull/29654
|
||||
while (
|
||||
true // eslint-disable-line no-constant-condition
|
||||
) {
|
||||
try {
|
||||
const currentSlot = await connection.getSlot({
|
||||
commitment: deployCommitment,
|
||||
});
|
||||
if (currentSlot > context.slot) {
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
await new Promise(resolve =>
|
||||
setTimeout(resolve, Math.round(MS_PER_SLOT / 2)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getScriptKind = getScriptKind;
|
||||
exports.getLanguageVariant = getLanguageVariant;
|
||||
const node_path_1 = __importDefault(require("node:path"));
|
||||
const ts = __importStar(require("typescript"));
|
||||
function getScriptKind(filePath, jsx) {
|
||||
const extension = node_path_1.default.extname(filePath).toLowerCase();
|
||||
// note - we only respect the user's jsx setting for unknown extensions
|
||||
// this is so that we always match TS's internal script kind logic, preventing
|
||||
// weird errors due to a mismatch.
|
||||
// https://github.com/microsoft/TypeScript/blob/da00ba67ed1182ad334f7c713b8254fba174aeba/src/compiler/utilities.ts#L6948-L6968
|
||||
switch (extension) {
|
||||
case ts.Extension.Cjs:
|
||||
case ts.Extension.Js:
|
||||
case ts.Extension.Mjs:
|
||||
return ts.ScriptKind.JS;
|
||||
case ts.Extension.Cts:
|
||||
case ts.Extension.Mts:
|
||||
case ts.Extension.Ts:
|
||||
return ts.ScriptKind.TS;
|
||||
case ts.Extension.Json:
|
||||
return ts.ScriptKind.JSON;
|
||||
case ts.Extension.Jsx:
|
||||
return ts.ScriptKind.JSX;
|
||||
case ts.Extension.Tsx:
|
||||
return ts.ScriptKind.TSX;
|
||||
default:
|
||||
// unknown extension, force typescript to ignore the file extension, and respect the user's setting
|
||||
return jsx ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
|
||||
}
|
||||
}
|
||||
function getLanguageVariant(scriptKind) {
|
||||
// https://github.com/microsoft/TypeScript/blob/d6e483b8dabd8fd37c00954c3f2184bb7f1eb90c/src/compiler/utilities.ts#L6281-L6285
|
||||
switch (scriptKind) {
|
||||
case ts.ScriptKind.JS:
|
||||
case ts.ScriptKind.JSON:
|
||||
case ts.ScriptKind.JSX:
|
||||
case ts.ScriptKind.TSX:
|
||||
return ts.LanguageVariant.JSX;
|
||||
default:
|
||||
return ts.LanguageVariant.Standard;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* @fileoverview Rule to spot scenarios where a newline looks like it is ending a statement, but is not.
|
||||
* @author Glen Mailer
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "problem",
|
||||
|
||||
docs: {
|
||||
description: "Disallow confusing multiline expressions",
|
||||
recommended: true,
|
||||
url: "https://eslint.org/docs/latest/rules/no-unexpected-multiline",
|
||||
},
|
||||
|
||||
schema: [],
|
||||
messages: {
|
||||
function:
|
||||
"Unexpected newline between function and ( of function call.",
|
||||
property:
|
||||
"Unexpected newline between object and [ of property access.",
|
||||
taggedTemplate:
|
||||
"Unexpected newline between template tag and template literal.",
|
||||
division:
|
||||
"Unexpected newline between numerator and division operator.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const REGEX_FLAG_MATCHER = /^[gimsuy]+$/u;
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Check to see if there is a newline between the node and the following open bracket
|
||||
* line's expression
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @param {string} messageId The error messageId to use.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkForBreakAfter(node, messageId) {
|
||||
const openParen = sourceCode.getTokenAfter(
|
||||
node,
|
||||
astUtils.isNotClosingParenToken,
|
||||
);
|
||||
const nodeExpressionEnd = sourceCode.getTokenBefore(openParen);
|
||||
|
||||
if (openParen.loc.start.line !== nodeExpressionEnd.loc.end.line) {
|
||||
context.report({
|
||||
node,
|
||||
loc: openParen.loc,
|
||||
messageId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public API
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
MemberExpression(node) {
|
||||
if (!node.computed || node.optional) {
|
||||
return;
|
||||
}
|
||||
checkForBreakAfter(node.object, "property");
|
||||
},
|
||||
|
||||
TaggedTemplateExpression(node) {
|
||||
const { quasi } = node;
|
||||
|
||||
// handles common tags, parenthesized tags, and typescript's generic type arguments
|
||||
const tokenBefore = sourceCode.getTokenBefore(quasi);
|
||||
|
||||
if (tokenBefore.loc.end.line !== quasi.loc.start.line) {
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: quasi.loc.start,
|
||||
end: {
|
||||
line: quasi.loc.start.line,
|
||||
column: quasi.loc.start.column + 1,
|
||||
},
|
||||
},
|
||||
messageId: "taggedTemplate",
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
CallExpression(node) {
|
||||
if (node.arguments.length === 0 || node.optional) {
|
||||
return;
|
||||
}
|
||||
checkForBreakAfter(node.callee, "function");
|
||||
},
|
||||
|
||||
"BinaryExpression[operator='/'] > BinaryExpression[operator='/'].left"(
|
||||
node,
|
||||
) {
|
||||
const secondSlash = sourceCode.getTokenAfter(
|
||||
node,
|
||||
token => token.value === "/",
|
||||
);
|
||||
const tokenAfterOperator =
|
||||
sourceCode.getTokenAfter(secondSlash);
|
||||
|
||||
if (
|
||||
tokenAfterOperator.type === "Identifier" &&
|
||||
REGEX_FLAG_MATCHER.test(tokenAfterOperator.value) &&
|
||||
secondSlash.range[1] === tokenAfterOperator.range[0]
|
||||
) {
|
||||
checkForBreakAfter(node.left, "division");
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var gulp = require('gulp'),
|
||||
git = require('gulp-git'),
|
||||
bump = require('gulp-bump'),
|
||||
filter = require('gulp-filter'),
|
||||
tagVersion = require('gulp-tag-version');
|
||||
|
||||
var TEST = [ 'test/*.js' ];
|
||||
var POWERED = [ 'powered-test/*.js' ];
|
||||
var SOURCE = [ 'src/**/*.js' ];
|
||||
|
||||
/**
|
||||
* Bumping version number and tagging the repository with it.
|
||||
* Please read http://semver.org/
|
||||
*
|
||||
* You can use the commands
|
||||
*
|
||||
* gulp patch # makes v0.1.0 -> v0.1.1
|
||||
* gulp feature # makes v0.1.1 -> v0.2.0
|
||||
* gulp release # makes v0.2.1 -> v1.0.0
|
||||
*
|
||||
* To bump the version numbers accordingly after you did a patch,
|
||||
* introduced a feature or made a backwards-incompatible release.
|
||||
*/
|
||||
|
||||
function inc(importance) {
|
||||
// get all the files to bump version in
|
||||
return gulp.src(['./package.json'])
|
||||
// bump the version number in those files
|
||||
.pipe(bump({type: importance}))
|
||||
// save it back to filesystem
|
||||
.pipe(gulp.dest('./'))
|
||||
// commit the changed version number
|
||||
.pipe(git.commit('Bumps package version'))
|
||||
// read only one file to get the version number
|
||||
.pipe(filter('package.json'))
|
||||
// **tag it in the repository**
|
||||
.pipe(tagVersion({
|
||||
prefix: ''
|
||||
}));
|
||||
}
|
||||
|
||||
gulp.task('patch', [ ], function () { return inc('patch'); })
|
||||
gulp.task('minor', [ ], function () { return inc('minor'); })
|
||||
gulp.task('major', [ ], function () { return inc('major'); })
|
||||
@@ -0,0 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = isObject
|
||||
|
||||
function isObject (input) {
|
||||
return Object.prototype.toString.apply(input) === '[object Object]'
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @fileoverview Reports useless `catch` clauses that just rethrow their error.
|
||||
* @author Teddy Katz
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Disallow unnecessary `catch` clauses",
|
||||
recommended: true,
|
||||
url: "https://eslint.org/docs/latest/rules/no-useless-catch",
|
||||
},
|
||||
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
unnecessaryCatchClause: "Unnecessary catch clause.",
|
||||
unnecessaryCatch: "Unnecessary try/catch wrapper.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
return {
|
||||
CatchClause(node) {
|
||||
if (
|
||||
node.param &&
|
||||
node.param.type === "Identifier" &&
|
||||
node.body.body.length &&
|
||||
node.body.body[0].type === "ThrowStatement" &&
|
||||
node.body.body[0].argument.type === "Identifier" &&
|
||||
node.body.body[0].argument.name === node.param.name
|
||||
) {
|
||||
if (node.parent.finalizer) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unnecessaryCatchClause",
|
||||
});
|
||||
} else {
|
||||
context.report({
|
||||
node: node.parent,
|
||||
messageId: "unnecessaryCatch",
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
export {}; // Make this a module
|
||||
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
type TypedArray =
|
||||
| Uint8Array
|
||||
| Uint8ClampedArray
|
||||
| Uint16Array
|
||||
| Uint32Array
|
||||
| Int8Array
|
||||
| Int16Array
|
||||
| Int32Array
|
||||
| BigUint64Array
|
||||
| BigInt64Array
|
||||
| Float16Array
|
||||
| Float32Array
|
||||
| Float64Array;
|
||||
type ArrayBufferView = TypedArray | DataView;
|
||||
|
||||
type NonSharedUint8Array = Uint8Array;
|
||||
type NonSharedUint8ClampedArray = Uint8ClampedArray;
|
||||
type NonSharedUint16Array = Uint16Array;
|
||||
type NonSharedUint32Array = Uint32Array;
|
||||
type NonSharedInt8Array = Int8Array;
|
||||
type NonSharedInt16Array = Int16Array;
|
||||
type NonSharedInt32Array = Int32Array;
|
||||
type NonSharedBigUint64Array = BigUint64Array;
|
||||
type NonSharedBigInt64Array = BigInt64Array;
|
||||
type NonSharedFloat16Array = Float16Array;
|
||||
type NonSharedFloat32Array = Float32Array;
|
||||
type NonSharedFloat64Array = Float64Array;
|
||||
type NonSharedDataView = DataView;
|
||||
type NonSharedTypedArray = TypedArray;
|
||||
type NonSharedArrayBufferView = ArrayBufferView;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
function _isNativeReflectConstruct() {
|
||||
try {
|
||||
var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
|
||||
} catch (t) {}
|
||||
return (_isNativeReflectConstruct = function _isNativeReflectConstruct() {
|
||||
return !!t;
|
||||
})();
|
||||
}
|
||||
export { _isNativeReflectConstruct as default };
|
||||
@@ -0,0 +1,554 @@
|
||||
/**
|
||||
* @fileoverview A rule to suggest using of const declaration for variables that are never reassigned after declared.
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const FixTracker = require("./utils/fix-tracker");
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @typedef {import("eslint-scope").Scope} Scope */
|
||||
/** @typedef {import("eslint-scope").Variable} Variable */
|
||||
/** @typedef {import("eslint-scope").Reference} Reference */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const PATTERN_TYPE =
|
||||
/^(?:.+?Pattern|RestElement|SpreadProperty|ExperimentalRestProperty|Property)$/u;
|
||||
const DECLARATION_HOST_TYPE =
|
||||
/^(?:Program|BlockStatement|StaticBlock|SwitchCase)$/u;
|
||||
const DESTRUCTURING_HOST_TYPE =
|
||||
/^(?:VariableDeclarator|AssignmentExpression)$/u;
|
||||
|
||||
/**
|
||||
* Checks whether a given node is located at `ForStatement.init` or not.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} `true` if the node is located at `ForStatement.init`.
|
||||
*/
|
||||
function isInitOfForStatement(node) {
|
||||
return node.parent.type === "ForStatement" && node.parent.init === node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given Identifier node becomes a VariableDeclaration or not.
|
||||
* @param {ASTNode} identifier An Identifier node to check.
|
||||
* @returns {boolean} `true` if the node can become a VariableDeclaration.
|
||||
*/
|
||||
function canBecomeVariableDeclaration(identifier) {
|
||||
let node = identifier.parent;
|
||||
|
||||
while (PATTERN_TYPE.test(node.type)) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return (
|
||||
node.type === "VariableDeclarator" ||
|
||||
(node.type === "AssignmentExpression" &&
|
||||
node.parent.type === "ExpressionStatement" &&
|
||||
DECLARATION_HOST_TYPE.test(node.parent.parent.type))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an property or element is from outer scope or function parameters
|
||||
* in destructing pattern.
|
||||
* @param {string} name A variable name to be checked.
|
||||
* @param {Scope} initScope A scope to start find.
|
||||
* @returns {boolean} Indicates if the variable is from outer scope or function parameters.
|
||||
*/
|
||||
function isOuterVariableInDestructing(name, initScope) {
|
||||
if (
|
||||
initScope.through.some(
|
||||
ref => ref.resolved && ref.resolved.name === name,
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const variable = astUtils.getVariableByName(initScope, name);
|
||||
|
||||
if (variable !== null) {
|
||||
return variable.defs.some(def => def.type === "Parameter");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the VariableDeclarator/AssignmentExpression node that a given reference
|
||||
* belongs to.
|
||||
* This is used to detect a mix of reassigned and never reassigned in a
|
||||
* destructuring.
|
||||
* @param {Reference} reference A reference to get.
|
||||
* @returns {ASTNode|null} A VariableDeclarator/AssignmentExpression node or
|
||||
* null.
|
||||
*/
|
||||
function getDestructuringHost(reference) {
|
||||
if (!reference.isWrite()) {
|
||||
return null;
|
||||
}
|
||||
let node = reference.identifier.parent;
|
||||
|
||||
while (PATTERN_TYPE.test(node.type)) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
if (!DESTRUCTURING_HOST_TYPE.test(node.type)) {
|
||||
return null;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a destructuring assignment node contains
|
||||
* any MemberExpression nodes. This is used to determine if a
|
||||
* variable that is only written once using destructuring can be
|
||||
* safely converted into a const declaration.
|
||||
* @param {ASTNode} node The ObjectPattern or ArrayPattern node to check.
|
||||
* @returns {boolean} True if the destructuring pattern contains
|
||||
* a MemberExpression, false if not.
|
||||
*/
|
||||
function hasMemberExpressionAssignment(node) {
|
||||
switch (node.type) {
|
||||
case "ObjectPattern":
|
||||
return node.properties.some(prop => {
|
||||
if (prop) {
|
||||
/*
|
||||
* Spread elements have an argument property while
|
||||
* others have a value property. Because different
|
||||
* parsers use different node types for spread elements,
|
||||
* we just check if there is an argument property.
|
||||
*/
|
||||
return hasMemberExpressionAssignment(
|
||||
prop.argument || prop.value,
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
case "ArrayPattern":
|
||||
return node.elements.some(element => {
|
||||
if (element) {
|
||||
return hasMemberExpressionAssignment(element);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
case "AssignmentPattern":
|
||||
return hasMemberExpressionAssignment(node.left);
|
||||
|
||||
case "MemberExpression":
|
||||
return true;
|
||||
|
||||
// no default
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an identifier node of a given variable.
|
||||
*
|
||||
* If the initialization exists or one or more reading references exist before
|
||||
* the first assignment, the identifier node is the node of the declaration.
|
||||
* Otherwise, the identifier node is the node of the first assignment.
|
||||
*
|
||||
* If the variable should not change to const, this function returns null.
|
||||
* - If the variable is reassigned.
|
||||
* - If the variable is never initialized nor assigned.
|
||||
* - If the variable is initialized in a different scope from the declaration.
|
||||
* - If the unique assignment of the variable cannot change to a declaration.
|
||||
* e.g. `if (a) b = 1` / `return (b = 1)`
|
||||
* - If the variable is declared in the global scope and `eslintUsed` is `true`.
|
||||
* `/*exported foo` directive comment makes such variables. This rule does not
|
||||
* warn such variables because this rule cannot distinguish whether the
|
||||
* exported variables are reassigned or not.
|
||||
* @param {Variable} variable A variable to get.
|
||||
* @param {boolean} ignoreReadBeforeAssign
|
||||
* The value of `ignoreReadBeforeAssign` option.
|
||||
* @returns {ASTNode|null}
|
||||
* An Identifier node if the variable should change to const.
|
||||
* Otherwise, null.
|
||||
*/
|
||||
function getIdentifierIfShouldBeConst(variable, ignoreReadBeforeAssign) {
|
||||
if (variable.eslintUsed && variable.scope.type === "global") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Finds the unique WriteReference.
|
||||
let writer = null;
|
||||
let isReadBeforeInit = false;
|
||||
const references = variable.references;
|
||||
|
||||
for (let i = 0; i < references.length; ++i) {
|
||||
const reference = references[i];
|
||||
|
||||
if (reference.isWrite()) {
|
||||
const isReassigned =
|
||||
writer !== null && writer.identifier !== reference.identifier;
|
||||
|
||||
if (isReassigned) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const destructuringHost = getDestructuringHost(reference);
|
||||
|
||||
if (
|
||||
destructuringHost !== null &&
|
||||
destructuringHost.left !== void 0
|
||||
) {
|
||||
const leftNode = destructuringHost.left;
|
||||
let hasOuterVariables = false,
|
||||
hasNonIdentifiers = false;
|
||||
|
||||
if (leftNode.type === "ObjectPattern") {
|
||||
const properties = leftNode.properties;
|
||||
|
||||
hasOuterVariables = properties
|
||||
.filter(prop => prop.value)
|
||||
.map(prop => prop.value.name)
|
||||
.some(name =>
|
||||
isOuterVariableInDestructing(name, variable.scope),
|
||||
);
|
||||
|
||||
hasNonIdentifiers = hasMemberExpressionAssignment(leftNode);
|
||||
} else if (leftNode.type === "ArrayPattern") {
|
||||
const elements = leftNode.elements;
|
||||
|
||||
hasOuterVariables = elements
|
||||
.map(element => element && element.name)
|
||||
.some(name =>
|
||||
isOuterVariableInDestructing(name, variable.scope),
|
||||
);
|
||||
|
||||
hasNonIdentifiers = hasMemberExpressionAssignment(leftNode);
|
||||
}
|
||||
|
||||
if (hasOuterVariables || hasNonIdentifiers) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
writer = reference;
|
||||
} else if (reference.isRead() && writer === null) {
|
||||
if (ignoreReadBeforeAssign) {
|
||||
return null;
|
||||
}
|
||||
isReadBeforeInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the assignment is from a different scope, ignore it.
|
||||
* If the assignment cannot change to a declaration, ignore it.
|
||||
*/
|
||||
const shouldBeConst =
|
||||
writer !== null &&
|
||||
writer.from === variable.scope &&
|
||||
canBecomeVariableDeclaration(writer.identifier);
|
||||
|
||||
if (!shouldBeConst) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isReadBeforeInit) {
|
||||
return variable.defs[0].name;
|
||||
}
|
||||
|
||||
return writer.identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups by the VariableDeclarator/AssignmentExpression node that each
|
||||
* reference of given variables belongs to.
|
||||
* This is used to detect a mix of reassigned and never reassigned in a
|
||||
* destructuring.
|
||||
* @param {Variable[]} variables Variables to group by destructuring.
|
||||
* @param {boolean} ignoreReadBeforeAssign
|
||||
* The value of `ignoreReadBeforeAssign` option.
|
||||
* @returns {Map<ASTNode, ASTNode[]>} Grouped identifier nodes.
|
||||
*/
|
||||
function groupByDestructuring(variables, ignoreReadBeforeAssign) {
|
||||
const identifierMap = new Map();
|
||||
|
||||
for (let i = 0; i < variables.length; ++i) {
|
||||
const variable = variables[i];
|
||||
const references = variable.references;
|
||||
const identifier = getIdentifierIfShouldBeConst(
|
||||
variable,
|
||||
ignoreReadBeforeAssign,
|
||||
);
|
||||
let prevId = null;
|
||||
|
||||
for (let j = 0; j < references.length; ++j) {
|
||||
const reference = references[j];
|
||||
const id = reference.identifier;
|
||||
|
||||
/*
|
||||
* Avoid counting a reference twice or more for default values of
|
||||
* destructuring.
|
||||
*/
|
||||
if (id === prevId) {
|
||||
continue;
|
||||
}
|
||||
prevId = id;
|
||||
|
||||
// Add the identifier node into the destructuring group.
|
||||
const group = getDestructuringHost(reference);
|
||||
|
||||
if (group) {
|
||||
if (identifierMap.has(group)) {
|
||||
identifierMap.get(group).push(identifier);
|
||||
} else {
|
||||
identifierMap.set(group, [identifier]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return identifierMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the nearest parent of node with a given type.
|
||||
* @param {ASTNode} node The node to search from.
|
||||
* @param {string} type The type field of the parent node.
|
||||
* @param {Function} shouldStop A predicate that returns true if the traversal should stop, and false otherwise.
|
||||
* @returns {ASTNode} The closest ancestor with the specified type; null if no such ancestor exists.
|
||||
*/
|
||||
function findUp(node, type, shouldStop) {
|
||||
if (!node || shouldStop(node)) {
|
||||
return null;
|
||||
}
|
||||
if (node.type === type) {
|
||||
return node;
|
||||
}
|
||||
return findUp(node.parent, type, shouldStop);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
defaultOptions: [
|
||||
{
|
||||
destructuring: "any",
|
||||
ignoreReadBeforeAssign: false,
|
||||
},
|
||||
],
|
||||
|
||||
docs: {
|
||||
description:
|
||||
"Require `const` declarations for variables that are never reassigned after declared",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/prefer-const",
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
destructuring: { enum: ["any", "all"] },
|
||||
ignoreReadBeforeAssign: { type: "boolean" },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
messages: {
|
||||
useConst: "'{{name}}' is never reassigned. Use 'const' instead.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const [{ destructuring, ignoreReadBeforeAssign }] = context.options;
|
||||
const shouldMatchAnyDestructuredVariable = destructuring !== "all";
|
||||
const sourceCode = context.sourceCode;
|
||||
const variables = [];
|
||||
let reportCount = 0;
|
||||
let checkedId = null;
|
||||
let checkedName = "";
|
||||
|
||||
/**
|
||||
* Reports given identifier nodes if all of the nodes should be declared
|
||||
* as const.
|
||||
*
|
||||
* The argument 'nodes' is an array of Identifier nodes.
|
||||
* This node is the result of 'getIdentifierIfShouldBeConst()', so it's
|
||||
* nullable. In simple declaration or assignment cases, the length of
|
||||
* the array is 1. In destructuring cases, the length of the array can
|
||||
* be 2 or more.
|
||||
* @param {(Reference|null)[]} nodes
|
||||
* References which are grouped by destructuring to report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkGroup(nodes) {
|
||||
const nodesToReport = nodes.filter(Boolean);
|
||||
|
||||
if (
|
||||
nodes.length &&
|
||||
(shouldMatchAnyDestructuredVariable ||
|
||||
nodesToReport.length === nodes.length)
|
||||
) {
|
||||
const varDeclParent = findUp(
|
||||
nodes[0],
|
||||
"VariableDeclaration",
|
||||
parentNode => parentNode.type.endsWith("Statement"),
|
||||
);
|
||||
const isVarDecParentNull = varDeclParent === null;
|
||||
|
||||
if (
|
||||
!isVarDecParentNull &&
|
||||
varDeclParent.declarations.length > 0
|
||||
) {
|
||||
const firstDeclaration = varDeclParent.declarations[0];
|
||||
|
||||
if (firstDeclaration.init) {
|
||||
const firstDecParent = firstDeclaration.init.parent;
|
||||
|
||||
/*
|
||||
* First we check the declaration type and then depending on
|
||||
* if the type is a "VariableDeclarator" or its an "ObjectPattern"
|
||||
* we compare the name and id from the first identifier, if the names are different
|
||||
* we assign the new name, id and reset the count of reportCount and nodeCount in
|
||||
* order to check each block for the number of reported errors and base our fix
|
||||
* based on comparing nodes.length and nodesToReport.length.
|
||||
*/
|
||||
|
||||
if (firstDecParent.type === "VariableDeclarator") {
|
||||
if (firstDecParent.id.name !== checkedName) {
|
||||
checkedName = firstDecParent.id.name;
|
||||
reportCount = 0;
|
||||
}
|
||||
|
||||
if (firstDecParent.id.type === "ObjectPattern") {
|
||||
if (firstDecParent.init.name !== checkedName) {
|
||||
checkedName = firstDecParent.init.name;
|
||||
reportCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstDecParent.id !== checkedId) {
|
||||
checkedId = firstDecParent.id;
|
||||
reportCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let shouldFix =
|
||||
varDeclParent &&
|
||||
// Don't do a fix unless all variables in the declarations are initialized (or it's in a for-in or for-of loop)
|
||||
(varDeclParent.parent.type === "ForInStatement" ||
|
||||
varDeclParent.parent.type === "ForOfStatement" ||
|
||||
varDeclParent.declarations.every(
|
||||
declaration => declaration.init,
|
||||
)) &&
|
||||
/*
|
||||
* If options.destructuring is "all", then this warning will not occur unless
|
||||
* every assignment in the destructuring should be const. In that case, it's safe
|
||||
* to apply the fix.
|
||||
*/
|
||||
nodesToReport.length === nodes.length;
|
||||
|
||||
if (
|
||||
!isVarDecParentNull &&
|
||||
varDeclParent.declarations &&
|
||||
varDeclParent.declarations.length !== 1
|
||||
) {
|
||||
if (
|
||||
varDeclParent &&
|
||||
varDeclParent.declarations &&
|
||||
varDeclParent.declarations.length >= 1
|
||||
) {
|
||||
/*
|
||||
* Add nodesToReport.length to a count, then comparing the count to the length
|
||||
* of the declarations in the current block.
|
||||
*/
|
||||
|
||||
reportCount += nodesToReport.length;
|
||||
|
||||
let totalDeclarationsCount = 0;
|
||||
|
||||
varDeclParent.declarations.forEach(declaration => {
|
||||
if (declaration.id.type === "ObjectPattern") {
|
||||
totalDeclarationsCount +=
|
||||
declaration.id.properties.length;
|
||||
} else if (declaration.id.type === "ArrayPattern") {
|
||||
totalDeclarationsCount +=
|
||||
declaration.id.elements.length;
|
||||
} else {
|
||||
totalDeclarationsCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
shouldFix =
|
||||
shouldFix && reportCount === totalDeclarationsCount;
|
||||
}
|
||||
}
|
||||
|
||||
nodesToReport.forEach(node => {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "useConst",
|
||||
data: node,
|
||||
fix: shouldFix
|
||||
? fixer => {
|
||||
const letKeywordToken =
|
||||
sourceCode.getFirstToken(
|
||||
varDeclParent,
|
||||
t => t.value === varDeclParent.kind,
|
||||
);
|
||||
|
||||
/**
|
||||
* Extend the replacement range to the whole declaration,
|
||||
* in order to prevent other fixes in the same pass
|
||||
* https://github.com/eslint/eslint/issues/13899
|
||||
*/
|
||||
return new FixTracker(fixer, sourceCode)
|
||||
.retainRange(varDeclParent.range)
|
||||
.replaceTextRange(
|
||||
letKeywordToken.range,
|
||||
"const",
|
||||
);
|
||||
}
|
||||
: null,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"Program:exit"() {
|
||||
groupByDestructuring(variables, ignoreReadBeforeAssign).forEach(
|
||||
checkGroup,
|
||||
);
|
||||
},
|
||||
|
||||
VariableDeclaration(node) {
|
||||
if (node.kind === "let" && !isInitOfForStatement(node)) {
|
||||
variables.push(...sourceCode.getDeclaredVariables(node));
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
|
||||
var _crypto = _interopRequireDefault(require("crypto"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function sha1(bytes) {
|
||||
if (Array.isArray(bytes)) {
|
||||
bytes = Buffer.from(bytes);
|
||||
} else if (typeof bytes === 'string') {
|
||||
bytes = Buffer.from(bytes, 'utf8');
|
||||
}
|
||||
|
||||
return _crypto.default.createHash('sha1').update(bytes).digest();
|
||||
}
|
||||
|
||||
var _default = sha1;
|
||||
exports.default = _default;
|
||||
@@ -0,0 +1,102 @@
|
||||
export type IncludeIgnoreFileOptionsObject = {
|
||||
/**
|
||||
* Whether to interpret the contents of an ignore file relative to the config file or the ignore file.
|
||||
* - gitignoreResolution: false (default): Interprets ignore patterns relative to the config file
|
||||
* - gitignoreResolution: true: Interprets the ignore patterns in a file relative to the ignore file
|
||||
*/
|
||||
gitignoreResolution?: boolean;
|
||||
/**
|
||||
* The name to give the output config object(s).
|
||||
*/
|
||||
name?: string;
|
||||
};
|
||||
/**
|
||||
* Options for `includeIgnoreFile()`. May be provided as an object or, for
|
||||
* legacy compatibility with `@eslint/compat`, as a string which is treated as
|
||||
* the `name` option.
|
||||
*/
|
||||
export type IncludeIgnoreFileOptions = IncludeIgnoreFileOptionsObject | string;
|
||||
export type ConfigObject = $eslintcore.ConfigObject;
|
||||
export type LegacyConfig = $eslintcore.LegacyConfigObject;
|
||||
export type Plugin = $eslintcore.Plugin;
|
||||
export type RuleConfig = $eslintcore.RuleConfig;
|
||||
export type Config = $typests.Config;
|
||||
export type ExtendsElement = $typests.ExtendsElement;
|
||||
export type ExtensionConfigObject = $typests.ExtensionConfigObject;
|
||||
export type SimpleExtendsElement = $typests.SimpleExtendsElement;
|
||||
export type ConfigWithExtends = $typests.ConfigWithExtends;
|
||||
export type InfiniteConfigArray = $typests.InfiniteArray<ConfigObject>;
|
||||
export type ConfigWithExtendsArray = $typests.ConfigWithExtendsArray;
|
||||
/**
|
||||
* @fileoverview Ignore file utilities for the config-helpers package.
|
||||
* This file was forked from the source code for the compat package.
|
||||
*
|
||||
* @author Nicholas C. Zakas
|
||||
* @author Kirk Waiblinger
|
||||
*/
|
||||
/**
|
||||
* @typedef {object} IncludeIgnoreFileOptionsObject
|
||||
* @property {boolean} [gitignoreResolution] Whether to interpret the contents of an ignore file relative to the config file or the ignore file.
|
||||
* - gitignoreResolution: false (default): Interprets ignore patterns relative to the config file
|
||||
* - gitignoreResolution: true: Interprets the ignore patterns in a file relative to the ignore file
|
||||
* @property {string} [name] The name to give the output config object(s).
|
||||
*/
|
||||
/**
|
||||
* Options for `includeIgnoreFile()`. May be provided as an object or, for
|
||||
* legacy compatibility with `@eslint/compat`, as a string which is treated as
|
||||
* the `name` option.
|
||||
* @typedef {IncludeIgnoreFileOptionsObject | string} IncludeIgnoreFileOptions
|
||||
*/
|
||||
/**
|
||||
* Converts an ESLint ignore pattern to a minimatch pattern.
|
||||
* @param {string} pattern The .eslintignore or .gitignore pattern to convert.
|
||||
* @returns {string} The converted pattern.
|
||||
*/
|
||||
export function convertIgnorePatternToMinimatch(pattern: string): string;
|
||||
/**
|
||||
* Helper function to define a config array.
|
||||
* @param {ConfigWithExtendsArray} args The arguments to the function.
|
||||
* @returns {ConfigObject[]} The config array.
|
||||
* @throws {TypeError} If no arguments are provided or if an argument is not an object.
|
||||
*/
|
||||
export function defineConfig(...args: ConfigWithExtendsArray): ConfigObject[];
|
||||
/**
|
||||
* Creates a global ignores config with the given patterns.
|
||||
* @param {string[]} ignorePatterns The ignore patterns.
|
||||
* @param {string} [name] The name of the global ignores config.
|
||||
* @returns {ConfigObject} The global ignores config.
|
||||
* @throws {TypeError} If ignorePatterns is not an array or if it is empty.
|
||||
*/
|
||||
export function globalIgnores(ignorePatterns: string[], name?: string): ConfigObject;
|
||||
/**
|
||||
* @overload
|
||||
*
|
||||
* Reads ignore files and returns objects with the ignore patterns.
|
||||
*
|
||||
* @param {string[]} ignoreFilePathArg The paths of ignore files to include.
|
||||
* @param {IncludeIgnoreFileOptions} [options]
|
||||
* @returns {ConfigObject[]}
|
||||
*/
|
||||
export function includeIgnoreFile(ignoreFilePathArg: string[], options?: IncludeIgnoreFileOptions): ConfigObject[];
|
||||
/**
|
||||
* @overload
|
||||
*
|
||||
* Reads an ignore file and returns an object with the ignore patterns.
|
||||
*
|
||||
* @param {string} ignoreFilePathArg The path of the ignore file to include.
|
||||
* @param {IncludeIgnoreFileOptions} [options]
|
||||
* @returns {ConfigObject}
|
||||
*/
|
||||
export function includeIgnoreFile(ignoreFilePathArg: string, options?: IncludeIgnoreFileOptions): ConfigObject;
|
||||
/**
|
||||
* @overload
|
||||
*
|
||||
* Reads an ignore file(s) and returns an object(s) with the ignore patterns.
|
||||
*
|
||||
* @param {string[] | string} ignoreFilePathArg The path(s) of the ignore file(s) to include.
|
||||
* @param {IncludeIgnoreFileOptions} [options]
|
||||
* @returns {ConfigObject[] | ConfigObject}
|
||||
*/
|
||||
export function includeIgnoreFile(ignoreFilePathArg: string[] | string, options?: IncludeIgnoreFileOptions): ConfigObject[] | ConfigObject;
|
||||
import type * as $eslintcore from "@eslint/core";
|
||||
import type * as $typests from "./types.cts";
|
||||
Reference in New Issue
Block a user