Website Structure

This commit is contained in:
supalerk-ar66 2026-01-13 10:46:40 +07:00
parent 62812f2090
commit 71f0676a62
22365 changed files with 4265753 additions and 791 deletions

View file

@ -0,0 +1,33 @@
import type { Rule } from "eslint";
import type { ArrayPattern, CallExpression, Expression, ForOfStatement, Identifier, MemberExpression, ObjectPattern } from "estree";
export type ExpressionReference = {
type: "unused";
node: Expression;
} | {
type: "unknown";
node: Expression;
} | {
type: "exported";
node: Expression;
} | {
type: "member";
node: Expression;
memberExpression: MemberExpression;
} | {
type: "destructuring";
node: Expression;
pattern: ObjectPattern | ArrayPattern;
} | {
type: "argument";
node: Expression;
callExpression: CallExpression;
} | {
type: "call";
node: Expression;
} | {
type: "iteration";
node: Expression;
for: ForOfStatement;
};
export declare function extractExpressionReferences(node: Expression, context: Rule.RuleContext): Iterable<ExpressionReference>;
export declare function extractExpressionReferencesForVariable(node: Identifier, context: Rule.RuleContext): Iterable<ExpressionReference>;

View file

@ -0,0 +1,171 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractExpressionReferences = extractExpressionReferences;
exports.extractExpressionReferencesForVariable = extractExpressionReferencesForVariable;
const utils_1 = require("./utils");
function* extractExpressionReferences(node, context) {
yield* iterateReferencesForExpression(node, context, {
variables: new Set(),
functions: new Map(),
});
}
function* extractExpressionReferencesForVariable(node, context) {
yield* iterateReferencesForVariable(node, context, {
variables: new Set(),
functions: new Map(),
});
}
function* iterateReferencesForExpression(expression, context, alreadyChecked) {
let node = expression;
let parent = (0, utils_1.getParent)(node);
while ((parent === null || parent === void 0 ? void 0 : parent.type) === "ChainExpression" ||
(parent === null || parent === void 0 ? void 0 : parent.type) === "TSNonNullExpression" ||
(parent === null || parent === void 0 ? void 0 : parent.type) === "TSAsExpression") {
node = parent;
parent = (0, utils_1.getParent)(node);
}
if (!parent || parent.type === "ExpressionStatement") {
yield { node, type: "unused" };
return;
}
if (parent.type === "MemberExpression") {
if (parent.object === node) {
yield { node, type: "member", memberExpression: parent };
}
else {
yield { node, type: "unknown" };
}
}
else if (parent.type === "AssignmentExpression") {
if (parent.right === node && parent.operator === "=") {
yield* iterateReferencesForESPattern(node, parent.left, context, alreadyChecked);
}
else {
yield { node, type: "unknown" };
}
}
else if (parent.type === "VariableDeclarator") {
if (parent.init === node) {
const pp = (0, utils_1.getParent)((0, utils_1.getParent)(parent));
if ((pp === null || pp === void 0 ? void 0 : pp.type) === "ExportNamedDeclaration") {
yield { node, type: "exported" };
}
yield* iterateReferencesForESPattern(node, parent.id, context, alreadyChecked);
}
else {
yield { node, type: "unknown" };
}
}
else if (parent.type === "CallExpression") {
const argIndex = parent.arguments.indexOf(node);
if (argIndex > -1) {
if (parent.callee.type === "Identifier") {
const fn = (0, utils_1.findFunction)(context, parent.callee);
if (fn) {
yield* iterateReferencesForFunctionArgument(node, fn, argIndex, context, alreadyChecked);
return;
}
}
yield { node, type: "argument", callExpression: parent };
}
else {
yield { node, type: "call" };
}
}
else if (parent.type === "ExportSpecifier" ||
parent.type === "ExportDefaultDeclaration") {
yield { node, type: "exported" };
}
else if (parent.type === "ForOfStatement") {
if (parent.right === node) {
yield { node, type: "iteration", for: parent };
}
else {
yield { node, type: "unknown" };
}
}
else if (parent.type === "IfStatement" ||
parent.type === "ConditionalExpression" ||
parent.type === "LogicalExpression" ||
parent.type === "UnaryExpression") {
if (isUsedInTest(parent, node)) {
}
else {
yield { node, type: "unknown" };
}
}
else {
yield { node, type: "unknown" };
}
}
function isUsedInTest(parent, node) {
if (parent.type === "IfStatement") {
return parent.test === node;
}
if (parent.type === "ConditionalExpression") {
return parent.test === node;
}
if (parent.type === "LogicalExpression") {
return parent.operator === "&&" && parent.left === node;
}
if (parent.type === "UnaryExpression") {
return parent.operator === "!" && parent.argument === node;
}
return false;
}
function* iterateReferencesForESPattern(expression, pattern, context, alreadyChecked) {
let target = pattern;
while (target.type === "AssignmentPattern") {
target = target.left;
}
if (target.type === "Identifier") {
yield* iterateReferencesForVariable(target, context, alreadyChecked);
}
else if (target.type === "ObjectPattern" ||
target.type === "ArrayPattern") {
yield { node: expression, type: "destructuring", pattern: target };
}
else {
yield { node: expression, type: "unknown" };
}
}
function* iterateReferencesForVariable(identifier, context, alreadyChecked) {
const variable = (0, utils_1.findVariable)(context, identifier);
if (!variable) {
yield { node: identifier, type: "unknown" };
return;
}
if (alreadyChecked.variables.has(variable)) {
return;
}
alreadyChecked.variables.add(variable);
if (variable.eslintUsed) {
yield { node: identifier, type: "exported" };
}
const readReferences = variable.references.filter((ref) => ref.isRead());
if (!readReferences.length) {
yield { node: identifier, type: "unused" };
return;
}
for (const reference of readReferences) {
yield* iterateReferencesForExpression(reference.identifier, context, alreadyChecked);
}
}
function* iterateReferencesForFunctionArgument(expression, fn, argIndex, context, alreadyChecked) {
let alreadyIndexes = alreadyChecked.functions.get(fn);
if (!alreadyIndexes) {
alreadyIndexes = new Set();
alreadyChecked.functions.set(fn, alreadyIndexes);
}
if (alreadyIndexes.has(argIndex)) {
return;
}
alreadyIndexes.add(argIndex);
const params = fn.params.slice(0, argIndex + 1);
const argNode = params[argIndex];
if (!argNode || params.some((param) => (param === null || param === void 0 ? void 0 : param.type) === "RestElement")) {
yield { node: expression, type: "unknown" };
return;
}
yield* iterateReferencesForESPattern(expression, argNode, context, alreadyChecked);
}

View file

@ -0,0 +1,23 @@
import type { Rule } from "eslint";
import type { ForOfStatement, AssignmentProperty, Expression, MemberExpression, Pattern } from "estree";
export type PropertyReference = {
type: "unknown";
node: Pattern | AssignmentProperty | Expression | ForOfStatement;
extractPropertyReferences?: () => Iterable<PropertyReference>;
} | {
type: "member";
name: string;
node: MemberExpression;
extractPropertyReferences: () => Iterable<PropertyReference>;
} | {
type: "destructuring";
name: string;
node: AssignmentProperty | Pattern;
extractPropertyReferences: () => Iterable<PropertyReference>;
} | {
type: "iteration";
node: ForOfStatement;
extractPropertyReferences: () => Iterable<PropertyReference>;
};
export declare function extractPropertyReferences(node: Expression, context: Rule.RuleContext): Iterable<PropertyReference>;
export declare function extractPropertyReferencesForPattern(node: Pattern, context: Rule.RuleContext): Iterable<PropertyReference>;

View file

@ -0,0 +1,231 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractPropertyReferences = extractPropertyReferences;
exports.extractPropertyReferencesForPattern = extractPropertyReferencesForPattern;
const extract_expression_references_1 = require("./extract-expression-references");
const utils_1 = require("./utils");
function* extractPropertyReferences(node, context) {
if (isShallowCopy(node)) {
yield* iteratePropertyReferencesForShallowCopy(node, context);
return;
}
for (const ref of (0, extract_expression_references_1.extractExpressionReferences)(node, context)) {
if (ref.type === "member") {
yield* iteratePropertyReferencesForMemberExpression(ref.memberExpression, context);
}
else if (ref.type === "destructuring") {
yield* iteratePropertyReferencesForPattern(ref.pattern, context);
}
else if (ref.type === "iteration") {
yield* iteratePropertyReferencesForForOf(ref.for, context);
}
else {
if (ref.node !== node && isShallowCopy(ref.node)) {
yield* iteratePropertyReferencesForShallowCopy(ref.node, context);
return;
}
yield { type: "unknown", node: ref.node };
}
}
}
function* extractPropertyReferencesForPattern(node, context) {
yield* iteratePropertyReferencesForPattern(node, context);
}
function isShallowCopy(node) {
const parent = (0, utils_1.getParent)(node);
if ((parent === null || parent === void 0 ? void 0 : parent.type) === "SpreadElement") {
const spreadParent = (0, utils_1.getParent)(parent);
if ((spreadParent === null || spreadParent === void 0 ? void 0 : spreadParent.type) === "ObjectExpression" ||
(spreadParent === null || spreadParent === void 0 ? void 0 : spreadParent.type) === "ArrayExpression") {
return true;
}
}
return false;
}
function* iteratePropertyReferencesForMemberExpression(node, context) {
const property = getProperty(node, context);
if (property == null) {
yield {
type: "unknown",
node,
*extractPropertyReferences() {
yield* extractPropertyReferences(node, context);
},
};
return;
}
yield {
type: "member",
name: property,
node,
*extractPropertyReferences() {
yield* extractPropertyReferences(node, context);
},
};
}
function* iteratePropertyReferencesForObjectPattern(node, context) {
for (const prop of node.properties) {
if (prop.type === "RestElement") {
yield* iteratePropertyReferencesForPattern(prop.argument, context);
continue;
}
const property = getProperty(prop, context);
if (property == null) {
yield {
type: "unknown",
node,
*extractPropertyReferences() {
yield* iteratePropertyReferencesForPattern(prop.value, context);
},
};
continue;
}
yield {
type: "destructuring",
name: property,
node: prop,
*extractPropertyReferences() {
yield* iteratePropertyReferencesForPattern(prop.value, context);
},
};
}
}
function* iteratePropertyReferencesForArrayPattern(node, context) {
let index = 0;
for (; index < node.elements.length; index++) {
const element = node.elements[index];
if (!element) {
continue;
}
if (element.type === "RestElement") {
for (const ref of iteratePropertyReferencesForPattern(element.argument, context)) {
yield offsetRef(ref, index);
}
index++;
break;
}
yield {
type: "destructuring",
name: String(index),
node: element,
*extractPropertyReferences() {
yield* iteratePropertyReferencesForPattern(element, context);
},
};
}
for (; index < node.elements.length; index++) {
const element = node.elements[index];
if (!element) {
continue;
}
yield {
type: "unknown",
node: element,
*extractPropertyReferences() {
yield* iteratePropertyReferencesForPattern(element, context);
},
};
}
}
function* iteratePropertyReferencesForForOf(node, context) {
yield {
type: "iteration",
node,
*extractPropertyReferences() {
let left = node.left;
if (left.type === "VariableDeclaration") {
left = left.declarations[0].id;
}
yield* iteratePropertyReferencesForPattern(left, context);
},
};
}
function* iteratePropertyReferencesForPattern(node, context) {
let target = node;
while (target.type === "AssignmentPattern") {
target = target.left;
}
if (target.type === "Identifier") {
for (const exprRef of (0, extract_expression_references_1.extractExpressionReferencesForVariable)(target, context)) {
yield* extractPropertyReferences(exprRef.node, context);
}
}
else if (target.type === "ObjectPattern") {
yield* iteratePropertyReferencesForObjectPattern(target, context);
}
else if (target.type === "ArrayPattern") {
yield* iteratePropertyReferencesForArrayPattern(target, context);
}
else {
yield { type: "unknown", node: target };
}
}
function* iteratePropertyReferencesForShallowCopy(node, context) {
const spread = node.parent;
const spreadParent = spread.parent;
if (spreadParent.type === "ObjectExpression") {
yield* extractPropertyReferences(spreadParent, context);
}
else if (spreadParent.type === "ArrayExpression") {
const index = spreadParent.elements.indexOf(spread);
if (index === 0) {
yield* extractPropertyReferences(spreadParent, context);
return;
}
const hasSpread = spreadParent.elements
.slice(0, index)
.some((e) => (e === null || e === void 0 ? void 0 : e.type) === "SpreadElement");
if (hasSpread) {
for (const ref of extractPropertyReferences(spreadParent, context)) {
yield {
type: "unknown",
node: ref.node,
extractPropertyReferences: ref.extractPropertyReferences,
};
}
}
else {
for (const ref of extractPropertyReferences(spreadParent, context)) {
yield offsetRef(ref, -index);
}
}
}
}
function getProperty(node, context) {
if (node.type === "MemberExpression") {
if (node.computed) {
if (node.property.type === "Literal") {
if (typeof node.property.value === "string" ||
typeof node.property.value === "number")
return String(node.property.value);
}
return (0, utils_1.getStringIfConstant)(context, node.property);
}
else if (node.property.type === "Identifier") {
return node.property.name;
}
}
if (node.type === "Property") {
if (node.key.type === "Literal") {
if (typeof node.key.value === "string" ||
typeof node.key.value === "number")
return String(node.key.value);
}
if (node.computed) {
return (0, utils_1.getStringIfConstant)(context, node.key);
}
else if (node.key.type === "Identifier") {
return node.key.name;
}
}
return null;
}
function offsetRef(ref, offset) {
if (ref.type === "member" || ref.type === "destructuring") {
const num = Number(ref.name) + offset;
if (!Number.isNaN(num)) {
return { ...ref, name: String(num) };
}
}
return ref;
}

View file

@ -0,0 +1,7 @@
export { getParent, findVariable, getStringIfConstant, getStaticValue, getScope, isKnownMethodCall, parseReplacements, } from "./utils";
export type { KnownMethodCall, ReferenceElement } from "./utils";
export { extractExpressionReferences } from "./extract-expression-references";
export type { ExpressionReference } from "./extract-expression-references";
export { extractPropertyReferences } from "./extract-property-references";
export * from "./regex";
export type { PropertyReference } from "./extract-property-references";

View file

@ -0,0 +1,30 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractPropertyReferences = exports.extractExpressionReferences = exports.parseReplacements = exports.isKnownMethodCall = exports.getScope = exports.getStaticValue = exports.getStringIfConstant = exports.findVariable = exports.getParent = void 0;
var utils_1 = require("./utils");
Object.defineProperty(exports, "getParent", { enumerable: true, get: function () { return utils_1.getParent; } });
Object.defineProperty(exports, "findVariable", { enumerable: true, get: function () { return utils_1.findVariable; } });
Object.defineProperty(exports, "getStringIfConstant", { enumerable: true, get: function () { return utils_1.getStringIfConstant; } });
Object.defineProperty(exports, "getStaticValue", { enumerable: true, get: function () { return utils_1.getStaticValue; } });
Object.defineProperty(exports, "getScope", { enumerable: true, get: function () { return utils_1.getScope; } });
Object.defineProperty(exports, "isKnownMethodCall", { enumerable: true, get: function () { return utils_1.isKnownMethodCall; } });
Object.defineProperty(exports, "parseReplacements", { enumerable: true, get: function () { return utils_1.parseReplacements; } });
var extract_expression_references_1 = require("./extract-expression-references");
Object.defineProperty(exports, "extractExpressionReferences", { enumerable: true, get: function () { return extract_expression_references_1.extractExpressionReferences; } });
var extract_property_references_1 = require("./extract-property-references");
Object.defineProperty(exports, "extractPropertyReferences", { enumerable: true, get: function () { return extract_property_references_1.extractPropertyReferences; } });
__exportStar(require("./regex"), exports);

View file

@ -0,0 +1,43 @@
import type { Rule, AST, SourceCode } from "eslint";
import type { Expression, Literal, RegExpLiteral } from "estree";
export interface PatternRange {
readonly start: number;
readonly end: number;
}
export declare class PatternReplaceRange {
range: AST.Range;
type: "RegExp" | "SingleQuotedString" | "DoubleQuotedString";
constructor(range: AST.Range, type: PatternReplaceRange["type"]);
static fromLiteral(node: Literal, sourceCode: SourceCode, nodeRange: PatternRange, range: PatternRange): PatternReplaceRange | null;
getAstLocation(sourceCode: SourceCode): AST.SourceLocation;
escape(text: string): string;
replace(fixer: Rule.RuleFixer, text: string): Rule.Fix;
remove(fixer: Rule.RuleFixer): Rule.Fix;
insertAfter(fixer: Rule.RuleFixer, text: string): Rule.Fix;
insertBefore(fixer: Rule.RuleFixer, text: string): Rule.Fix;
}
export interface RegExpValue {
readonly source: string;
readonly flags: string;
readonly ownedNode: RegExpLiteral | null;
}
export declare class PatternSource {
private readonly sourceCode;
readonly node: Expression;
readonly value: string;
private readonly segments;
readonly regexpValue: RegExpValue | null;
isStringValue(): this is PatternSource & {
readonly regexpValue: null;
};
private constructor();
static fromExpression(context: Rule.RuleContext, expression: Expression): PatternSource | null;
private static fromRegExpObject;
static fromRegExpLiteral(context: Rule.RuleContext, expression: RegExpLiteral): PatternSource;
private getSegment;
private getSegments;
getReplaceRange(range: PatternRange): PatternReplaceRange | null;
getAstRange(range: PatternRange): AST.Range;
getAstLocation(range: PatternRange): AST.SourceLocation;
getOwnedRegExpLiterals(): readonly RegExpLiteral[];
}

View file

@ -0,0 +1,226 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PatternSource = exports.PatternReplaceRange = void 0;
const utils_1 = require("./utils");
class PatternReplaceRange {
constructor(range, type) {
if (!range || range[0] < 0 || range[0] > range[1]) {
throw new Error(`Invalid range: ${JSON.stringify(range)}`);
}
this.range = range;
this.type = type;
}
static fromLiteral(node, sourceCode, nodeRange, range) {
if (!node.range) {
return null;
}
const start = range.start - nodeRange.start;
const end = range.end - nodeRange.start;
if ((0, utils_1.isRegexpLiteral)(node)) {
const nodeStart = node.range[0] + "/".length;
return new PatternReplaceRange([nodeStart + start, nodeStart + end], "RegExp");
}
if ((0, utils_1.isStringLiteral)(node)) {
const astRange = (0, utils_1.getStringValueRange)(sourceCode, node, start, end);
if (astRange) {
const quote = sourceCode.text[node.range[0]];
return new PatternReplaceRange(astRange, quote === "'" ? "SingleQuotedString" : "DoubleQuotedString");
}
}
return null;
}
getAstLocation(sourceCode) {
return (0, utils_1.astRangeToLocation)(sourceCode, this.range);
}
escape(text) {
if (this.type === "DoubleQuotedString" ||
this.type === "SingleQuotedString") {
const base = text
.replace(/\\/gu, "\\\\")
.replace(/\n/gu, "\\n")
.replace(/\r/gu, "\\r")
.replace(/\t/gu, "\\t");
if (this.type === "DoubleQuotedString") {
return base.replace(/"/gu, '\\"');
}
return base.replace(/'/gu, "\\'");
}
return text.replace(/\n/gu, "\\n").replace(/\r/gu, "\\r");
}
replace(fixer, text) {
return fixer.replaceTextRange(this.range, this.escape(text));
}
remove(fixer) {
return fixer.removeRange(this.range);
}
insertAfter(fixer, text) {
return fixer.insertTextAfterRange(this.range, this.escape(text));
}
insertBefore(fixer, text) {
return fixer.insertTextBeforeRange(this.range, this.escape(text));
}
}
exports.PatternReplaceRange = PatternReplaceRange;
class PatternSegment {
constructor(sourceCode, node, value, start) {
this.sourceCode = sourceCode;
this.node = node;
this.value = value;
this.start = start;
this.end = start + value.length;
}
contains(range) {
return this.start <= range.start && range.end <= this.end;
}
getOwnedRegExpLiteral() {
if ((0, utils_1.isRegexpLiteral)(this.node)) {
return this.node;
}
if (this.node.type === "MemberExpression" &&
this.node.object.type !== "Super" &&
(0, utils_1.isRegexpLiteral)(this.node.object) &&
(0, utils_1.getPropertyName)(this.node) === "source") {
return this.node.object;
}
return null;
}
getReplaceRange(range) {
if (!this.contains(range)) {
return null;
}
const regexp = this.getOwnedRegExpLiteral();
if (regexp) {
return PatternReplaceRange.fromLiteral(regexp, this.sourceCode, this, range);
}
if (this.node.type === "Literal") {
return PatternReplaceRange.fromLiteral(this.node, this.sourceCode, this, range);
}
return null;
}
getAstRange(range) {
const replaceRange = this.getReplaceRange(range);
if (replaceRange) {
return replaceRange.range;
}
return this.node.range;
}
}
class PatternSource {
isStringValue() {
return this.regexpValue === null;
}
constructor(sourceCode, node, value, segments, regexpValue) {
this.sourceCode = sourceCode;
this.node = node;
this.value = value;
this.segments = segments;
this.regexpValue = regexpValue;
}
static fromExpression(context, expression) {
expression = (0, utils_1.dereferenceOwnedVariable)(context, expression);
if ((0, utils_1.isRegexpLiteral)(expression)) {
return PatternSource.fromRegExpLiteral(context, expression);
}
const sourceCode = context.sourceCode;
const flat = flattenPlus(context, expression);
const items = [];
let value = "";
for (const e of flat) {
if (e.type === "PrivateIdentifier")
return null;
const staticValue = (0, utils_1.getStaticValue)(context, e);
if (!staticValue) {
return null;
}
if (flat.length === 1 && staticValue.value instanceof RegExp) {
return PatternSource.fromRegExpObject(context, e, staticValue.value.source, staticValue.value.flags);
}
if (typeof staticValue.value !== "string") {
return null;
}
items.push(new PatternSegment(sourceCode, e, staticValue.value, value.length));
value += staticValue.value;
}
return new PatternSource(sourceCode, expression, value, items, null);
}
static fromRegExpObject(context, expression, source, flags) {
const sourceCode = context.sourceCode;
return new PatternSource(sourceCode, expression, source, [new PatternSegment(sourceCode, expression, source, 0)], {
source,
flags,
ownedNode: null,
});
}
static fromRegExpLiteral(context, expression) {
const sourceCode = context.sourceCode;
return new PatternSource(sourceCode, expression, expression.regex.pattern, [
new PatternSegment(sourceCode, expression, expression.regex.pattern, 0),
], {
source: expression.regex.pattern,
flags: expression.regex.flags,
ownedNode: expression,
});
}
getSegment(range) {
const segments = this.getSegments(range);
if (segments.length === 1) {
return segments[0];
}
return null;
}
getSegments(range) {
return this.segments.filter((item) => item.start < range.end && range.start < item.end);
}
getReplaceRange(range) {
const segment = this.getSegment(range);
if (segment) {
return segment.getReplaceRange(range);
}
return null;
}
getAstRange(range) {
const overlapping = this.getSegments(range);
if (overlapping.length === 1) {
return overlapping[0].getAstRange(range);
}
let min = Infinity;
let max = -Infinity;
for (const item of overlapping) {
min = Math.min(min, item.node.range[0]);
max = Math.max(max, item.node.range[1]);
}
if (min > max) {
return this.node.range;
}
return [min, max];
}
getAstLocation(range) {
return (0, utils_1.astRangeToLocation)(this.sourceCode, this.getAstRange(range));
}
getOwnedRegExpLiterals() {
const literals = [];
for (const segment of this.segments) {
const regexp = segment.getOwnedRegExpLiteral();
if (regexp) {
literals.push(regexp);
}
}
return literals;
}
}
exports.PatternSource = PatternSource;
function flattenPlus(context, e) {
if (e.type === "BinaryExpression" && e.operator === "+") {
return [
...(e.left.type !== "PrivateIdentifier"
? flattenPlus(context, e.left)
: [e.left]),
...flattenPlus(context, e.right),
];
}
const deRef = (0, utils_1.dereferenceOwnedVariable)(context, e);
if (deRef !== e) {
return flattenPlus(context, deRef);
}
return [e];
}

View file

@ -0,0 +1,7 @@
import type { AST, SourceCode } from "eslint";
import type * as ESTree from "estree";
export declare function getFlagsRange(flagsNode: ESTree.RegExpLiteral): AST.Range;
export declare function getFlagsRange(flagsNode: ESTree.Expression | null): AST.Range | null;
export declare function getFlagsLocation(sourceCode: SourceCode, regexpNode: ESTree.CallExpression | ESTree.RegExpLiteral, flagsNode: ESTree.Expression | null): AST.SourceLocation;
export declare function getFlagRange(sourceCode: SourceCode, flagsNode: ESTree.Expression | null, flag: string): AST.Range | null;
export declare function getFlagLocation(sourceCode: SourceCode, regexpNode: ESTree.CallExpression | ESTree.RegExpLiteral, flagsNode: ESTree.Expression | null, flag: string): AST.SourceLocation;

View file

@ -0,0 +1,68 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFlagsRange = getFlagsRange;
exports.getFlagsLocation = getFlagsLocation;
exports.getFlagRange = getFlagRange;
exports.getFlagLocation = getFlagLocation;
const utils_1 = require("./utils");
function getFlagsRange(flagsNode) {
if (!flagsNode) {
return null;
}
if ((0, utils_1.isRegexpLiteral)(flagsNode)) {
return [
flagsNode.range[1] - flagsNode.regex.flags.length,
flagsNode.range[1],
];
}
if ((0, utils_1.isStringLiteral)(flagsNode)) {
return [flagsNode.range[0] + 1, flagsNode.range[1] - 1];
}
return null;
}
function getFlagsLocation(sourceCode, regexpNode, flagsNode) {
var _a;
const range = getFlagsRange(flagsNode);
if (range == null) {
return (_a = flagsNode === null || flagsNode === void 0 ? void 0 : flagsNode.loc) !== null && _a !== void 0 ? _a : regexpNode.loc;
}
if (range[0] === range[1]) {
range[0]--;
}
return {
start: sourceCode.getLocFromIndex(range[0]),
end: sourceCode.getLocFromIndex(range[1]),
};
}
function getFlagRange(sourceCode, flagsNode, flag) {
if (!flagsNode || !flag) {
return null;
}
if ((0, utils_1.isRegexpLiteral)(flagsNode)) {
const index = flagsNode.regex.flags.indexOf(flag);
if (index === -1) {
return null;
}
const start = flagsNode.range[1] - flagsNode.regex.flags.length + index;
return [start, start + 1];
}
if ((0, utils_1.isStringLiteral)(flagsNode)) {
const index = flagsNode.value.indexOf(flag);
if (index === -1) {
return null;
}
return (0, utils_1.getStringValueRange)(sourceCode, flagsNode, index, index + 1);
}
return null;
}
function getFlagLocation(sourceCode, regexpNode, flagsNode, flag) {
var _a;
const range = getFlagRange(sourceCode, flagsNode, flag);
if (range == null) {
return (_a = flagsNode === null || flagsNode === void 0 ? void 0 : flagsNode.loc) !== null && _a !== void 0 ? _a : regexpNode.loc;
}
return {
start: sourceCode.getLocFromIndex(range[0]),
end: sourceCode.getLocFromIndex(range[1]),
};
}

View file

@ -0,0 +1,54 @@
import type { Rule, SourceCode, AST, Scope } from "eslint";
import type { ArrowFunctionExpression, CallExpression, Expression, FunctionDeclaration, FunctionExpression, Identifier, Literal, MemberExpression, Node, RegExpLiteral } from "estree";
export declare function getParent<E extends Node>(node: Node | null): E | null;
export declare function findVariable(context: Rule.RuleContext, node: Identifier): Scope.Variable | null;
export declare function getStringIfConstant(context: Rule.RuleContext, node: Node): string | null;
type GetStaticValueResult = {
value: unknown;
} | {
value: undefined;
optional?: true;
};
export declare function getStaticValue(context: Rule.RuleContext, node: Node): GetStaticValueResult | null;
export declare function getScope(context: Rule.RuleContext, currentNode: Node): Scope.Scope;
export declare function findFunction(context: Rule.RuleContext, id: Identifier): FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | null;
export type KnownMethodCall = CallExpression & {
callee: MemberExpression & {
object: Expression;
property: Identifier;
};
arguments: Expression[];
parent: Node;
};
export declare function isKnownMethodCall(node: CallExpression, methods: Record<string, number>): node is KnownMethodCall;
interface BaseElement {
type: string;
range: [number, number];
}
export type ReplacementElement = CharacterElement | DollarElement | ReferenceElement;
export interface CharacterElement extends BaseElement {
type: "CharacterElement";
value: string;
}
export interface DollarElement extends BaseElement {
type: "DollarElement";
kind: "$" | "&" | "`" | "'";
}
export interface ReferenceElement extends BaseElement {
type: "ReferenceElement";
ref: number | string;
refText: string;
}
export declare function parseReplacements(context: Rule.RuleContext, node: Literal): ReplacementElement[];
export declare function getStringValueRange(sourceCode: SourceCode, node: Literal & {
value: string;
}, startOffset: number, endOffset: number): AST.Range | null;
export declare function isRegexpLiteral(node: Expression): node is RegExpLiteral;
export declare function isStringLiteral(node: Expression): node is Literal & {
value: string;
};
export declare function getPropertyName(node: MemberExpression, context?: Rule.RuleContext): string | null;
export declare function astRangeToLocation(sourceCode: SourceCode, range: AST.Range): AST.SourceLocation;
export declare function dereferenceOwnedVariable(context: Rule.RuleContext, expression: Expression): Expression;
export declare function dereferenceVariable(context: Rule.RuleContext, expression: Expression): Expression;
export {};

View file

@ -0,0 +1,316 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getParent = getParent;
exports.findVariable = findVariable;
exports.getStringIfConstant = getStringIfConstant;
exports.getStaticValue = getStaticValue;
exports.getScope = getScope;
exports.findFunction = findFunction;
exports.isKnownMethodCall = isKnownMethodCall;
exports.parseReplacements = parseReplacements;
exports.getStringValueRange = getStringValueRange;
exports.isRegexpLiteral = isRegexpLiteral;
exports.isStringLiteral = isStringLiteral;
exports.getPropertyName = getPropertyName;
exports.astRangeToLocation = astRangeToLocation;
exports.dereferenceOwnedVariable = dereferenceOwnedVariable;
exports.dereferenceVariable = dereferenceVariable;
const replacements_utils_1 = require("../replacements-utils");
const string_literal_parser_1 = require("../string-literal-parser");
const eslintUtils = __importStar(require("@eslint-community/eslint-utils"));
function getParent(node) {
if (!node) {
return null;
}
return node.parent;
}
function findVariable(context, node) {
return eslintUtils.findVariable(getScope(context, node), node);
}
function findSimpleVariable(context, identifier) {
const variable = findVariable(context, identifier);
if (!variable || variable.defs.length !== 1) {
return null;
}
const def = variable.defs[0];
if (def.type !== "Variable" || def.node.id.type !== "Identifier") {
return null;
}
return variable;
}
function getStringIfConstant(context, node) {
if (node.type === "BinaryExpression" ||
node.type === "MemberExpression" ||
node.type === "Identifier" ||
node.type === "TemplateLiteral") {
const evaluated = getStaticValue(context, node);
return evaluated && String(evaluated.value);
}
return eslintUtils.getStringIfConstant(node, getScope(context, node));
}
function getStaticValue(context, node) {
if (node.type === "BinaryExpression") {
if (node.operator === "+") {
const left = getStaticValue(context, node.left);
if (left == null) {
return null;
}
const right = getStaticValue(context, node.right);
if (right == null) {
return null;
}
return {
value: left.value + right.value,
};
}
}
else if (node.type === "MemberExpression") {
const propName = getPropertyName(node, context);
if (propName === "source") {
const object = getStaticValue(context, node.object);
if (object && object.value instanceof RegExp) {
return { value: object.value.source };
}
}
}
else if (node.type === "TemplateLiteral") {
const expressions = [];
for (const expr of node.expressions) {
const exprValue = getStaticValue(context, expr);
if (!exprValue) {
return null;
}
expressions.push(exprValue);
}
let value = node.quasis[0].value.cooked;
for (let i = 0; i < expressions.length; ++i) {
value += String(expressions[i].value);
value += node.quasis[i + 1].value.cooked;
}
return { value };
}
else if (node.type === "Identifier") {
const deRef = dereferenceVariable(context, node);
if (deRef !== node) {
return getStaticValue(context, deRef);
}
}
return eslintUtils.getStaticValue(node, getScope(context, node));
}
function getScope(context, currentNode) {
const scopeManager = context.sourceCode.scopeManager;
let node = currentNode;
for (; node; node = node.parent || null) {
const scope = scopeManager.acquire(node, false);
if (scope) {
if (scope.type === "function-expression-name") {
return scope.childScopes[0];
}
return scope;
}
}
return scopeManager.scopes[0];
}
function findFunction(context, id) {
let target = id;
const set = new Set();
for (;;) {
if (set.has(target)) {
return null;
}
set.add(target);
const calleeVariable = findVariable(context, target);
if (!calleeVariable) {
return null;
}
if (calleeVariable.defs.length === 1) {
const def = calleeVariable.defs[0];
if (def.node.type === "FunctionDeclaration") {
return def.node;
}
if (def.type === "Variable" &&
def.parent.kind === "const" &&
def.node.init) {
if (def.node.init.type === "FunctionExpression" ||
def.node.init.type === "ArrowFunctionExpression") {
return def.node.init;
}
if (def.node.init.type === "Identifier") {
target = def.node.init;
continue;
}
}
}
return null;
}
}
function isKnownMethodCall(node, methods) {
const mem = node.callee;
if (mem.type !== "MemberExpression" ||
mem.computed ||
mem.property.type !== "Identifier") {
return false;
}
const argLength = methods[mem.property.name];
if (node.arguments.length !== argLength) {
return false;
}
if (node.arguments.some((arg) => arg.type === "SpreadElement")) {
return false;
}
const object = mem.object;
if (object.type === "Super") {
return false;
}
return true;
}
function parseReplacements(context, node) {
const stringLiteral = (0, string_literal_parser_1.parseStringLiteral)(context.sourceCode.text, {
start: node.range[0],
end: node.range[1],
});
const tokens = stringLiteral.tokens.filter((t) => t.value);
return (0, replacements_utils_1.baseParseReplacements)(tokens, (start, end) => {
return {
range: [start.range[0], end.range[1]],
};
});
}
function getStringValueRange(sourceCode, node, startOffset, endOffset) {
if (!node.range) {
return null;
}
if (node.value.length < endOffset) {
return null;
}
try {
const raw = sourceCode.text.slice(node.range[0] + 1, node.range[1] - 1);
let valueIndex = 0;
let start = null;
for (const t of (0, string_literal_parser_1.parseStringTokens)(raw)) {
const endIndex = valueIndex + t.value.length;
if (start == null &&
valueIndex <= startOffset &&
startOffset < endIndex) {
start = t.range[0];
}
if (start != null &&
valueIndex < endOffset &&
endOffset <= endIndex) {
const end = t.range[1];
const nodeStart = node.range[0] + 1;
return [nodeStart + start, nodeStart + end];
}
valueIndex = endIndex;
}
}
catch (_a) {
}
return null;
}
function isRegexpLiteral(node) {
return node.type === "Literal" && "regex" in node;
}
function isStringLiteral(node) {
return node.type === "Literal" && typeof node.value === "string";
}
function getPropertyName(node, context) {
const prop = node.property;
if (prop.type === "PrivateIdentifier") {
return null;
}
if (!node.computed) {
return prop.name;
}
if (context) {
return getStringIfConstant(context, prop);
}
if (isStringLiteral(prop)) {
return prop.value;
}
return null;
}
function astRangeToLocation(sourceCode, range) {
return {
start: sourceCode.getLocFromIndex(range[0]),
end: sourceCode.getLocFromIndex(range[1]),
};
}
function dereferenceOwnedVariable(context, expression) {
if (expression.type === "Identifier") {
const variable = findSimpleVariable(context, expression);
if (!variable) {
return expression;
}
const def = variable.defs[0];
const grandParent = getParent(def.parent);
if (grandParent && grandParent.type === "ExportNamedDeclaration") {
return expression;
}
if (variable.references.length !== 2) {
return expression;
}
const [initRef, thisRef] = variable.references;
if (!(initRef.init &&
initRef.writeExpr &&
initRef.writeExpr === def.node.init) ||
thisRef.identifier !== expression) {
return expression;
}
return dereferenceOwnedVariable(context, def.node.init);
}
return expression;
}
function dereferenceVariable(context, expression) {
if (expression.type === "Identifier") {
const variable = findSimpleVariable(context, expression);
if (variable) {
const def = variable.defs[0];
if (def.node.init) {
if (def.parent.kind === "const") {
return dereferenceVariable(context, def.node.init);
}
const refs = variable.references;
const inits = refs.filter((r) => r.init).length;
const reads = refs.filter((r) => r.isReadOnly()).length;
if (inits === 1 && reads + inits === refs.length) {
return dereferenceVariable(context, def.node.init);
}
}
}
}
return expression;
}

View file

@ -0,0 +1,6 @@
import type { Rule } from "eslint";
import type { JSONSchema4 } from "json-schema";
import type { CharRange } from "refa";
export declare function getAllowedCharRanges(allowedByRuleOption: string | readonly string[] | undefined, context: Rule.RuleContext): readonly CharRange[];
export declare function getAllowedCharValueSchema(): JSONSchema4;
export declare function inRange(ranges: Iterable<CharRange>, min: number, max?: number): boolean;

View file

@ -0,0 +1,82 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAllowedCharRanges = getAllowedCharRanges;
exports.getAllowedCharValueSchema = getAllowedCharValueSchema;
exports.inRange = inRange;
const unicode_1 = require("./unicode");
const ALL_RANGES = [{ min: 0, max: 0x10ffff }];
const ALPHANUMERIC_RANGES = [
{ min: unicode_1.CP_DIGIT_ZERO, max: unicode_1.CP_DIGIT_NINE },
{ min: unicode_1.CP_CAPITAL_A, max: unicode_1.CP_CAPITAL_Z },
{ min: unicode_1.CP_SMALL_A, max: unicode_1.CP_SMALL_Z },
];
function getAllowedCharRanges(allowedByRuleOption, context) {
var _a;
let target = allowedByRuleOption ||
((_a = context.settings.regexp) === null || _a === void 0 ? void 0 : _a.allowedCharacterRanges);
if (!target) {
return ALPHANUMERIC_RANGES;
}
if (typeof target === "string") {
target = [target];
}
const allowed = [];
for (const range of target) {
if (range === "all") {
return ALL_RANGES;
}
else if (range === "alphanumeric") {
if (target.length === 1) {
return ALPHANUMERIC_RANGES;
}
allowed.push(...ALPHANUMERIC_RANGES);
}
else {
const chars = [...range];
if (chars.length !== 3 || chars[1] !== "-") {
throw new Error(`Invalid format: The range ${JSON.stringify(range)} is not of the form \`<char>-<char>\`.`);
}
const min = chars[0].codePointAt(0);
const max = chars[2].codePointAt(0);
allowed.push({ min, max });
}
}
return allowed;
}
function getAllowedCharValueSchema() {
return {
anyOf: [
{ enum: ["all", "alphanumeric"] },
{
type: "array",
items: [{ enum: ["all", "alphanumeric"] }],
minItems: 1,
additionalItems: false,
},
{
type: "array",
items: {
anyOf: [
{ const: "alphanumeric" },
{
type: "string",
pattern: /^(?:[\ud800-\udbff][\udc00-\udfff]|[^\ud800-\udfff])-(?:[\ud800-\udbff][\udc00-\udfff]|[^\ud800-\udfff])$/
.source,
},
],
},
uniqueItems: true,
minItems: 1,
additionalItems: false,
},
],
};
}
function inRange(ranges, min, max = min) {
for (const range of ranges) {
if (range.min <= min && max <= range.max) {
return true;
}
}
return false;
}

View file

@ -0,0 +1,99 @@
import type { Rule } from "eslint";
import type { CallExpression, Expression, Pattern, RestElement } from "estree";
import type { ReadonlyFlags } from "regexp-ast-analysis";
import type { PropertyReference } from "./ast-utils";
import type { TypeTracker } from "./type-tracker";
export type UnknownUsage = {
type: "UnknownUsage";
node: Expression;
on?: "replace" | "replaceAll" | "matchAll";
};
export type WithoutRef = {
type: "WithoutRef";
node: Expression;
on: "search" | "test" | "match" | "replace" | "replaceAll" | "matchAll" | "exec";
};
export type ArrayRef = {
type: "ArrayRef";
kind: "index";
ref: number | null;
prop: PropertyReference & {
type: "member" | "destructuring";
};
} | {
type: "ArrayRef";
kind: "name";
ref: string;
prop: PropertyReference & {
type: "member" | "destructuring";
};
} | {
type: "ArrayRef";
kind: "name";
ref: null;
prop: PropertyReference & {
type: "unknown" | "iteration";
};
};
export type ReplacementRef = {
type: "ReplacementRef";
kind: "index";
ref: number;
range?: [number, number];
} | {
type: "ReplacementRef";
kind: "name";
ref: string;
range?: [number, number];
};
export type ReplacerFunctionRef = {
type: "ReplacerFunctionRef";
kind: "index";
ref: number;
arg: Pattern;
} | {
type: "ReplacerFunctionRef";
kind: "name";
ref: string;
prop: PropertyReference & {
type: "member" | "destructuring";
};
} | {
type: "ReplacerFunctionRef";
kind: "name";
ref: null;
prop: PropertyReference & {
type: "unknown" | "iteration";
};
arg: null;
} | {
type: "ReplacerFunctionRef";
kind: "name";
ref: null;
prop: null;
arg: Pattern;
} | {
type: "ReplacerFunctionRef";
kind: "unknown";
ref: null;
arg: Pattern;
};
export type Split = {
type: "Split";
node: CallExpression;
};
export type UnknownRef = {
type: "UnknownRef";
kind: "array";
prop: PropertyReference & {
type: "unknown" | "iteration";
};
} | {
type: "UnknownRef";
kind: "replacerFunction";
arg: RestElement;
};
export type CapturingGroupReference = ArrayRef | ReplacementRef | ReplacerFunctionRef | UnknownRef | WithoutRef | Split | UnknownUsage;
export declare function extractCapturingGroupReferences(node: Expression, flags: ReadonlyFlags, typeTracer: TypeTracker, countOfCapturingGroup: number, context: Rule.RuleContext, options: {
strictTypes: boolean;
}): Iterable<CapturingGroupReference>;

View file

@ -0,0 +1,511 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractCapturingGroupReferences = extractCapturingGroupReferences;
const ast_utils_1 = require("./ast-utils");
const extract_property_references_1 = require("./ast-utils/extract-property-references");
const replacements_utils_1 = require("./replacements-utils");
const WELL_KNOWN_ARRAY_METHODS = {
toString: {},
toLocaleString: {},
pop: { result: "element" },
push: {},
concat: { result: "array" },
join: {},
reverse: { result: "array" },
shift: { result: "element" },
slice: { result: "array" },
sort: { elementParameters: [0, 1], result: "array" },
splice: { result: "array" },
unshift: {},
indexOf: {},
lastIndexOf: {},
every: { elementParameters: [0] },
some: { elementParameters: [0] },
forEach: { elementParameters: [0] },
map: { elementParameters: [0] },
filter: { elementParameters: [0], result: "array" },
reduce: { elementParameters: [1] },
reduceRight: { elementParameters: [1] },
find: { elementParameters: [0], result: "element" },
findIndex: { elementParameters: [0] },
fill: {},
copyWithin: { result: "array" },
entries: {},
keys: {},
values: { result: "iterator" },
includes: {},
flatMap: { elementParameters: [0] },
flat: {},
at: { result: "element" },
findLast: { elementParameters: [0], result: "element" },
findLastIndex: { elementParameters: [0] },
toReversed: { result: "array" },
toSorted: { elementParameters: [0, 1], result: "array" },
toSpliced: { result: "array" },
with: { result: "array" },
};
function* extractCapturingGroupReferences(node, flags, typeTracer, countOfCapturingGroup, context, options) {
const ctx = {
flags,
countOfCapturingGroup,
context,
isString: options.strictTypes
? (n) => typeTracer.isString(n)
: (n) => typeTracer.maybeString(n),
};
for (const ref of (0, ast_utils_1.extractExpressionReferences)(node, context)) {
if (ref.type === "argument") {
yield* iterateForArgument(ref.callExpression, ref.node, ctx);
}
else if (ref.type === "member") {
yield* iterateForMember(ref.memberExpression, ref.node, ctx);
}
else {
yield {
type: "UnknownUsage",
node: ref.node,
};
}
}
}
function* iterateForArgument(callExpression, argument, ctx) {
if (!(0, ast_utils_1.isKnownMethodCall)(callExpression, {
match: 1,
search: 1,
replace: 2,
replaceAll: 2,
matchAll: 1,
split: 1,
})) {
return;
}
if (callExpression.arguments[0] !== argument) {
return;
}
if (!ctx.isString(callExpression.callee.object)) {
yield {
type: "UnknownUsage",
node: argument,
};
return;
}
if (callExpression.callee.property.name === "match") {
yield* iterateForStringMatch(callExpression, argument, ctx);
}
else if (callExpression.callee.property.name === "search") {
yield {
type: "WithoutRef",
node: argument,
on: "search",
};
}
else if (callExpression.callee.property.name === "replace" ||
callExpression.callee.property.name === "replaceAll") {
yield* iterateForStringReplace(callExpression, argument, ctx, callExpression.callee.property.name);
}
else if (callExpression.callee.property.name === "matchAll") {
yield* iterateForStringMatchAll(callExpression, argument, ctx);
}
else if (callExpression.callee.property.name === "split") {
yield {
type: "Split",
node: callExpression,
};
}
}
function* iterateForMember(memberExpression, object, ctx) {
const parent = getCallExpressionFromCalleeExpression(memberExpression);
if (!parent ||
!(0, ast_utils_1.isKnownMethodCall)(parent, {
test: 1,
exec: 1,
})) {
return;
}
if (parent.callee.property.name === "test") {
yield {
type: "WithoutRef",
node: object,
on: "test",
};
}
else if (parent.callee.property.name === "exec") {
yield* iterateForRegExpExec(parent, object, ctx);
}
}
function* iterateForStringMatch(node, argument, ctx) {
if (ctx.flags.global) {
yield {
type: "WithoutRef",
node: argument,
on: "match",
};
}
else {
let useRet = false;
for (const ref of iterateForExecResult(node, ctx)) {
useRet = true;
yield ref;
}
if (!useRet) {
yield {
type: "WithoutRef",
node: argument,
on: "match",
};
}
}
}
function* iterateForStringReplace(node, argument, ctx, on) {
const replacementNode = node.arguments[1];
if (replacementNode.type === "FunctionExpression" ||
replacementNode.type === "ArrowFunctionExpression") {
yield* iterateForReplacerFunction(replacementNode, argument, on, ctx);
}
else {
const replacement = node.arguments[1];
if (!replacement) {
yield {
type: "UnknownUsage",
node: argument,
on,
};
return;
}
if (replacement.type === "Literal") {
yield* verifyForReplaceReplacementLiteral(replacement, argument, on, ctx);
}
else {
const evaluated = (0, ast_utils_1.getStaticValue)(ctx.context, replacement);
if (!evaluated || typeof evaluated.value !== "string") {
yield {
type: "UnknownUsage",
node: argument,
on,
};
return;
}
yield* verifyForReplaceReplacement(evaluated.value, argument, on);
}
}
}
function* iterateForStringMatchAll(node, argument, ctx) {
let useRet = false;
for (const iterationRef of (0, ast_utils_1.extractPropertyReferences)(node, ctx.context)) {
if (!iterationRef.extractPropertyReferences) {
useRet = true;
yield {
type: "UnknownUsage",
node: argument,
on: "matchAll",
};
return;
}
if (hasNameRef(iterationRef)) {
if (iterationRef.type === "member" &&
isWellKnownArrayMethodName(iterationRef.name)) {
const call = getCallExpressionFromCalleeExpression(iterationRef.node);
if (call) {
for (const cgRef of iterateForArrayMethodOfStringMatchAll(call, iterationRef.name, argument, ctx)) {
useRet = true;
yield cgRef;
if (cgRef.type === "UnknownRef") {
return;
}
}
}
continue;
}
if (Number.isNaN(Number(iterationRef.name))) {
continue;
}
}
for (const ref of iterationRef.extractPropertyReferences()) {
for (const cgRef of iterateForRegExpMatchArrayReference(ref)) {
useRet = true;
yield cgRef;
if (cgRef.type === "UnknownRef") {
return;
}
}
}
}
if (!useRet) {
yield {
type: "WithoutRef",
node: argument,
on: "matchAll",
};
}
}
function* iterateForRegExpExec(node, object, ctx) {
let useRet = false;
for (const ref of iterateForExecResult(node, ctx)) {
useRet = true;
yield ref;
}
if (!useRet) {
yield {
type: "WithoutRef",
node: object,
on: "exec",
};
}
}
function* iterateForExecResult(node, ctx) {
for (const ref of (0, ast_utils_1.extractPropertyReferences)(node, ctx.context)) {
for (const cgRef of iterateForRegExpMatchArrayReference(ref)) {
yield cgRef;
if (cgRef.type === "UnknownRef") {
return;
}
}
}
}
function* verifyForReplaceReplacementLiteral(substr, argument, on, ctx) {
let useReplacement = false;
for (const replacement of (0, ast_utils_1.parseReplacements)(ctx.context, substr)) {
if (replacement.type === "ReferenceElement") {
useReplacement = true;
if (typeof replacement.ref === "number") {
yield {
type: "ReplacementRef",
kind: "index",
ref: replacement.ref,
range: replacement.range,
};
}
else {
yield {
type: "ReplacementRef",
kind: "name",
ref: replacement.ref,
range: replacement.range,
};
}
}
}
if (!useReplacement) {
yield {
type: "WithoutRef",
node: argument,
on,
};
}
}
function* verifyForReplaceReplacement(substr, argument, on) {
let useReplacement = false;
for (const replacement of (0, replacements_utils_1.parseReplacementsForString)(substr)) {
if (replacement.type === "ReferenceElement") {
useReplacement = true;
if (typeof replacement.ref === "number") {
yield {
type: "ReplacementRef",
kind: "index",
ref: replacement.ref,
};
}
else {
yield {
type: "ReplacementRef",
kind: "name",
ref: replacement.ref,
};
}
}
}
if (!useReplacement) {
yield {
type: "WithoutRef",
node: argument,
on,
};
}
}
function* iterateForReplacerFunction(replacementNode, argument, on, ctx) {
if (replacementNode.params.length < 2 &&
!replacementNode.params.some((arg) => arg.type === "RestElement")) {
yield {
type: "WithoutRef",
node: argument,
on,
};
return;
}
for (let index = 0; index < replacementNode.params.length; index++) {
const arg = replacementNode.params[index];
if (arg.type === "RestElement") {
yield {
type: "UnknownRef",
kind: "replacerFunction",
arg,
};
return;
}
if (index === 0) {
continue;
}
else if (index <= ctx.countOfCapturingGroup) {
yield {
type: "ReplacerFunctionRef",
kind: "index",
ref: index,
arg,
};
}
else if (ctx.countOfCapturingGroup + 3 === index) {
if (arg.type === "Identifier" || arg.type === "ObjectPattern") {
for (const ref of (0, extract_property_references_1.extractPropertyReferencesForPattern)(arg, ctx.context)) {
if (hasNameRef(ref)) {
yield {
type: "ReplacerFunctionRef",
kind: "name",
ref: ref.name,
prop: ref,
};
}
else {
yield {
type: "ReplacerFunctionRef",
kind: "name",
ref: null,
prop: ref,
arg: null,
};
}
}
}
else {
yield {
type: "ReplacerFunctionRef",
kind: "name",
ref: null,
arg,
prop: null,
};
}
}
}
}
function* iterateForRegExpMatchArrayReference(ref) {
if (hasNameRef(ref)) {
if (ref.name === "groups") {
for (const namedRef of ref.extractPropertyReferences()) {
yield getNamedArrayRef(namedRef);
}
}
else if (ref.name === "indices") {
for (const indicesRef of ref.extractPropertyReferences()) {
yield* iterateForRegExpIndicesArrayReference(indicesRef);
}
}
else {
if (ref.name === "input" || ref.name === "index") {
return;
}
yield getIndexArrayRef(ref);
}
}
else {
yield {
type: "UnknownRef",
kind: "array",
prop: ref,
};
}
}
function* iterateForRegExpIndicesArrayReference(ref) {
if (hasNameRef(ref)) {
if (ref.name === "groups") {
for (const namedRef of ref.extractPropertyReferences()) {
yield getNamedArrayRef(namedRef);
}
}
else {
yield getIndexArrayRef(ref);
}
}
else {
yield {
type: "UnknownRef",
kind: "array",
prop: ref,
};
}
}
function* iterateForArrayMethodOfStringMatchAll(node, methodsName, argument, ctx) {
const arrayMethod = WELL_KNOWN_ARRAY_METHODS[methodsName];
if (arrayMethod.elementParameters &&
node.arguments[0] &&
(node.arguments[0].type === "FunctionExpression" ||
node.arguments[0].type === "ArrowFunctionExpression")) {
const fnNode = node.arguments[0];
for (const index of arrayMethod.elementParameters) {
const param = fnNode.params[index];
if (param) {
for (const ref of (0, extract_property_references_1.extractPropertyReferencesForPattern)(param, ctx.context)) {
yield* iterateForRegExpMatchArrayReference(ref);
}
}
}
}
if (arrayMethod.result) {
if (arrayMethod.result === "element") {
for (const ref of (0, ast_utils_1.extractPropertyReferences)(node, ctx.context)) {
yield* iterateForRegExpMatchArrayReference(ref);
}
}
else if (arrayMethod.result === "array" ||
arrayMethod.result === "iterator") {
yield* iterateForStringMatchAll(node, argument, ctx);
}
}
}
function hasNameRef(ref) {
return ref.type === "destructuring" || ref.type === "member";
}
function getIndexArrayRef(ref) {
const numRef = Number(ref.name);
if (Number.isFinite(numRef)) {
return {
type: "ArrayRef",
kind: "index",
ref: numRef,
prop: ref,
};
}
return {
type: "ArrayRef",
kind: "index",
ref: null,
prop: ref,
};
}
function getNamedArrayRef(namedRef) {
if (hasNameRef(namedRef)) {
return {
type: "ArrayRef",
kind: "name",
ref: namedRef.name,
prop: namedRef,
};
}
return {
type: "ArrayRef",
kind: "name",
ref: null,
prop: namedRef,
};
}
function getCallExpressionFromCalleeExpression(expression) {
const parent = (0, ast_utils_1.getParent)(expression);
if (!parent ||
parent.type !== "CallExpression" ||
parent.callee !== expression) {
return null;
}
return parent;
}
function isWellKnownArrayMethodName(name) {
return Boolean(WELL_KNOWN_ARRAY_METHODS[name]);
}

View file

@ -0,0 +1,5 @@
import type { Quantifier } from "@eslint-community/regexpp/ast";
import type { Rule } from "eslint";
import type { CanSimplify } from "./regexp-ast";
import type { RegExpContext } from ".";
export declare function fixSimplifyQuantifier(quantifier: Quantifier, result: CanSimplify, { fixReplaceNode }: RegExpContext): [replacement: string, fix: (fixer: Rule.RuleFixer) => Rule.Fix | null];

View file

@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.fixSimplifyQuantifier = fixSimplifyQuantifier;
const regexp_ast_analysis_1 = require("regexp-ast-analysis");
const regexp_ast_1 = require("./regexp-ast");
function fixSimplifyQuantifier(quantifier, result, { fixReplaceNode }) {
const ancestor = (0, regexp_ast_analysis_1.getClosestAncestor)(quantifier, ...result.dependencies);
let replacement;
if (quantifier.min === 0) {
replacement = "";
}
else if (quantifier.min === 1) {
replacement = quantifier.element.raw;
}
else {
replacement =
quantifier.element.raw +
(0, regexp_ast_1.quantToString)({
min: quantifier.min,
max: quantifier.min,
greedy: true,
});
}
return [
replacement,
fixReplaceNode(ancestor, () => {
return (ancestor.raw.slice(0, quantifier.start - ancestor.start) +
replacement +
ancestor.raw.slice(quantifier.end - ancestor.start));
}),
];
}

View file

@ -0,0 +1,9 @@
import type { Rule } from "eslint";
import type { Expression } from "estree";
export declare enum UsageOfPattern {
partial = 0,
whole = 1,
mixed = 2,
unknown = 3
}
export declare function getUsageOfPattern(node: Expression, context: Rule.RuleContext): UsageOfPattern;

View file

@ -0,0 +1,110 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UsageOfPattern = void 0;
exports.getUsageOfPattern = getUsageOfPattern;
const ast_utils_1 = require("./ast-utils");
var UsageOfPattern;
(function (UsageOfPattern) {
UsageOfPattern[UsageOfPattern["partial"] = 0] = "partial";
UsageOfPattern[UsageOfPattern["whole"] = 1] = "whole";
UsageOfPattern[UsageOfPattern["mixed"] = 2] = "mixed";
UsageOfPattern[UsageOfPattern["unknown"] = 3] = "unknown";
})(UsageOfPattern || (exports.UsageOfPattern = UsageOfPattern = {}));
function getUsageOfPattern(node, context) {
const usageSet = new Set();
for (const usage of iterateUsageOfPattern(node, context)) {
if (usage === UsageOfPattern.unknown) {
return UsageOfPattern.unknown;
}
usageSet.add(usage);
}
if (usageSet.has(UsageOfPattern.partial)) {
return usageSet.has(UsageOfPattern.whole)
? UsageOfPattern.mixed
: UsageOfPattern.partial;
}
return usageSet.has(UsageOfPattern.whole)
? UsageOfPattern.whole
: UsageOfPattern.unknown;
}
function* iterateUsageOfPattern(node, context) {
for (const ref of (0, ast_utils_1.extractExpressionReferences)(node, context)) {
if (ref.type === "member") {
yield* iterateUsageOfPatternForMemberExpression(ref.memberExpression, context);
}
else if (ref.type === "destructuring") {
if (ref.pattern.type === "ObjectPattern")
yield* iterateUsageOfPatternForObjectPattern(ref.pattern, context);
}
else if (ref.type === "unused") {
}
else if (ref.type === "argument") {
if (ref.callExpression.arguments[0] === ref.node &&
ref.callExpression.callee.type === "MemberExpression") {
const member = ref.callExpression.callee;
const propName = !member.computed
? member.property.name
: (0, ast_utils_1.getStringIfConstant)(context, member.property);
if (propName === "match" ||
propName === "matchAll" ||
propName === "split" ||
propName === "replace" ||
propName === "replaceAll" ||
propName === "search") {
yield UsageOfPattern.whole;
}
else {
yield UsageOfPattern.unknown;
}
}
else {
yield UsageOfPattern.unknown;
}
}
else {
yield UsageOfPattern.unknown;
}
}
}
function* iterateUsageOfPatternForMemberExpression(node, context) {
const propName = !node.computed
? node.property.name
: (0, ast_utils_1.getStringIfConstant)(context, node.property);
yield* iterateUsageOfPatternForPropName(propName);
}
function* iterateUsageOfPatternForPropName(propName) {
const regexpPropName = propName;
if (regexpPropName === "source") {
yield UsageOfPattern.partial;
return;
}
if (regexpPropName === "compile" ||
regexpPropName === "dotAll" ||
regexpPropName === "flags" ||
regexpPropName === "global" ||
regexpPropName === "ignoreCase" ||
regexpPropName === "multiline" ||
regexpPropName === "sticky" ||
regexpPropName === "unicode") {
return;
}
yield UsageOfPattern.whole;
}
function* iterateUsageOfPatternForObjectPattern(node, context) {
for (const prop of node.properties) {
if (prop.type === "RestElement") {
continue;
}
let propName;
if (!prop.computed) {
propName =
prop.key.type === "Identifier"
? prop.key.name
: String(prop.key.value);
}
else {
propName = (0, ast_utils_1.getStringIfConstant)(context, prop.key);
}
yield* iterateUsageOfPatternForPropName(propName);
}
}

View file

@ -0,0 +1,81 @@
import type { Alternative, CapturingGroup, CharacterClassElement, Element, Node, Pattern, Quantifier, StringAlternative } from "@eslint-community/regexpp/ast";
import type { RegExpVisitor } from "@eslint-community/regexpp/visitor";
import type { Rule, AST } from "eslint";
import type * as ESTree from "estree";
import type { ReadonlyFlags } from "regexp-ast-analysis";
import type { RuleListener, RuleModule, PartialRuleModule } from "../types";
import type { PatternRange } from "./ast-utils/pattern-source";
import { PatternSource } from "./ast-utils/pattern-source";
import type { CapturingGroupReference } from "./extract-capturing-group-references";
import type { UsageOfPattern } from "./get-usage-of-pattern";
import type { Quant } from "./regexp-ast";
export * from "./unicode";
type RegExpContextBase = {
getRegexpLocation: (regexpNode: PatternRange, offsets?: [number, number]) => AST.SourceLocation;
getFlagsLocation: () => AST.SourceLocation;
getFlagLocation: (flag: string) => AST.SourceLocation;
fixReplaceNode: (regexpNode: Node, replacement: string | (() => string | null)) => (fixer: Rule.RuleFixer) => Rule.Fix | null;
fixReplaceQuant: (quantifier: Quantifier, replacement: string | Quant | (() => string | Quant | null)) => (fixer: Rule.RuleFixer) => Rule.Fix | null;
fixReplaceFlags: (newFlags: string | (() => string | null), includePattern?: boolean) => (fixer: Rule.RuleFixer) => Rule.Fix[] | Rule.Fix | null;
getUsageOfPattern: () => UsageOfPattern;
getCapturingGroupReferences: (options?: {
strictTypes?: boolean;
}) => CapturingGroupReference[];
getAllCapturingGroups: () => CapturingGroup[];
pattern: string;
patternAst: Pattern;
patternSource: PatternSource;
flags: Required<ReadonlyFlags>;
};
export type RegExpContextForLiteral = {
node: ESTree.RegExpLiteral;
flagsString: string;
ownsFlags: true;
regexpNode: ESTree.RegExpLiteral;
} & RegExpContextBase;
export type RegExpContextForSource = {
node: ESTree.Expression;
flagsString: string | null;
ownsFlags: boolean;
regexpNode: ESTree.CallExpression;
} & RegExpContextBase;
export type RegExpContext = RegExpContextForLiteral | RegExpContextForSource;
type UnparsableRegExpContextBase = {
node: ESTree.Expression;
regexpNode: ESTree.RegExpLiteral | ESTree.CallExpression;
flags: Required<ReadonlyFlags>;
flagsString: string | null;
ownsFlags: boolean;
getFlagsLocation: () => AST.SourceLocation;
getFlagLocation: (flag: string) => AST.SourceLocation;
fixReplaceFlags: (newFlags: string | (() => string | null), includePattern?: boolean) => (fixer: Rule.RuleFixer) => Rule.Fix[] | Rule.Fix | null;
};
export type RegExpContextForInvalid = {
pattern: string;
patternSource: PatternSource;
error: SyntaxError;
} & UnparsableRegExpContextBase;
export type RegExpContextForUnknown = {
pattern: null;
patternSource: null;
} & UnparsableRegExpContextBase;
export type UnparsableRegExpContext = RegExpContextForInvalid | RegExpContextForUnknown;
type ParsableRegexpRule = {
createLiteralVisitor?: (context: RegExpContextForLiteral) => RegExpVisitor.Handlers;
createSourceVisitor?: (context: RegExpContextForSource) => RegExpVisitor.Handlers;
};
type UnparsableRegexpRule = {
visitInvalid?: (context: RegExpContextForInvalid) => void;
visitUnknown?: (context: RegExpContextForUnknown) => void;
};
export declare function createRule(ruleName: string, rule: PartialRuleModule): RuleModule;
type DefineRegexpVisitorRule = UnparsableRegexpRule & (ParsableRegexpRule | {
createVisitor: (context: RegExpContext) => RegExpVisitor.Handlers;
});
export declare function defineRegexpVisitor(context: Rule.RuleContext, rule: DefineRegexpVisitorRule): RuleListener;
export declare function compositingVisitors(visitor: RuleListener, ...visitors: RuleListener[]): RuleListener;
export declare function mightCreateNewElement(before: string, after: string): boolean;
export declare function fixRemoveCharacterClassElement(context: RegExpContext, element: CharacterClassElement): (fixer: Rule.RuleFixer) => Rule.Fix | null;
export declare function fixRemoveAlternative(context: RegExpContext, alternative: Alternative): (fixer: Rule.RuleFixer) => Rule.Fix | null;
export declare function fixRemoveStringAlternative(context: RegExpContext, alternative: StringAlternative): (fixer: Rule.RuleFixer) => Rule.Fix | null;
export declare function canUnwrapped(node: Element, text: string): boolean;

View file

@ -0,0 +1,574 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createRule = createRule;
exports.defineRegexpVisitor = defineRegexpVisitor;
exports.compositingVisitors = compositingVisitors;
exports.mightCreateNewElement = mightCreateNewElement;
exports.fixRemoveCharacterClassElement = fixRemoveCharacterClassElement;
exports.fixRemoveAlternative = fixRemoveAlternative;
exports.fixRemoveStringAlternative = fixRemoveStringAlternative;
exports.canUnwrapped = canUnwrapped;
const regexpp_1 = require("@eslint-community/regexpp");
const regexp_ast_analysis_1 = require("regexp-ast-analysis");
const ast_utils_1 = require("./ast-utils");
const pattern_source_1 = require("./ast-utils/pattern-source");
const utils_1 = require("./ast-utils/utils");
const extract_capturing_group_references_1 = require("./extract-capturing-group-references");
const get_usage_of_pattern_1 = require("./get-usage-of-pattern");
const regex_syntax_1 = require("./regex-syntax");
const regexp_ast_1 = require("./regexp-ast");
const type_tracker_1 = require("./type-tracker");
const util_1 = require("./util");
const eslint_utils_1 = require("@eslint-community/eslint-utils");
__exportStar(require("./unicode"), exports);
const regexpRules = new WeakMap();
function createRule(ruleName, rule) {
return {
meta: {
...rule.meta,
docs: {
...rule.meta.docs,
url: `https://ota-meshi.github.io/eslint-plugin-regexp/rules/${ruleName}.html`,
ruleId: `regexp/${ruleName}`,
ruleName,
},
},
create: rule.create,
};
}
function defineRegexpVisitor(context, rule) {
const programNode = context.sourceCode.ast;
let visitor;
let rules = regexpRules.get(programNode);
if (!rules) {
rules = [];
regexpRules.set(programNode, rules);
visitor = buildRegexpVisitor(context, rules, () => {
regexpRules.delete(programNode);
});
}
else {
visitor = {};
}
let createLiteralVisitor = undefined;
let createSourceVisitor = undefined;
if ("createVisitor" in rule) {
createLiteralVisitor = rule.createVisitor;
createSourceVisitor = rule.createVisitor;
}
else {
createLiteralVisitor = rule.createLiteralVisitor;
createSourceVisitor = rule.createSourceVisitor;
}
rules.push({
createLiteralVisitor,
createSourceVisitor,
visitInvalid: rule.visitInvalid,
visitUnknown: rule.visitUnknown,
});
return visitor;
}
function buildRegexpVisitor(context, rules, programExit) {
const parser = new regexpp_1.RegExpParser();
function verify(patternNode, flagsNode, regexpNode, patternSource, flagsString, ownsFlags, createVisitor) {
const flags = (0, regex_syntax_1.parseFlags)(flagsString || "");
if (!patternSource) {
visitUnknownForRules(rules, {
pattern: null,
patternSource: null,
...buildUnparsableRegExpContextBase({
patternSource,
patternNode,
regexpNode,
context,
flags,
flagsString,
flagsNode,
ownsFlags,
}),
});
return;
}
let parsedPattern;
try {
parsedPattern = parser.parsePattern(patternSource.value, 0, patternSource.value.length, flags);
}
catch (error) {
if (error instanceof SyntaxError) {
visitInvalidForRules(rules, {
pattern: patternSource.value,
patternSource,
error,
...buildUnparsableRegExpContextBase({
patternSource,
patternNode,
regexpNode,
context,
flags,
flagsString,
flagsNode,
ownsFlags,
}),
});
}
return;
}
const helpers = buildRegExpContextBase({
patternSource,
regexpNode,
flagsNode,
context,
flags,
parsedPattern,
});
(0, regexpp_1.visitRegExpAST)(parsedPattern, createVisitor(helpers));
}
const ownedRegExpLiterals = new Set();
return {
"Program:exit": programExit,
Literal(node) {
if (!(0, utils_1.isRegexpLiteral)(node) || ownedRegExpLiterals.has(node)) {
return;
}
const flagsString = node.regex.flags;
const patternSource = pattern_source_1.PatternSource.fromRegExpLiteral(context, node);
verify(node, node, node, patternSource, flagsString, true, (base) => {
return createLiteralVisitorFromRules(rules, {
node,
flagsString,
ownsFlags: true,
regexpNode: node,
...base,
});
});
},
Program(program) {
const tracker = new eslint_utils_1.ReferenceTracker(context.sourceCode.getScope(program));
const regexpDataList = [];
for (const { node } of tracker.iterateGlobalReferences({
RegExp: { [eslint_utils_1.CALL]: true, [eslint_utils_1.CONSTRUCT]: true },
})) {
const newOrCall = node;
const args = newOrCall.arguments;
const [patternArg, flagsArg] = args;
if (!patternArg || patternArg.type === "SpreadElement") {
continue;
}
const patternSource = pattern_source_1.PatternSource.fromExpression(context, patternArg);
patternSource === null || patternSource === void 0 ? void 0 : patternSource.getOwnedRegExpLiterals().forEach((n) => ownedRegExpLiterals.add(n));
let flagsNode = null;
let flagsString = null;
let ownsFlags = false;
if (flagsArg) {
if (flagsArg.type !== "SpreadElement") {
flagsNode = (0, utils_1.dereferenceOwnedVariable)(context, flagsArg);
flagsString = (0, ast_utils_1.getStringIfConstant)(context, flagsNode);
ownsFlags = (0, utils_1.isStringLiteral)(flagsNode);
}
}
else {
if (patternSource && patternSource.regexpValue) {
flagsString = patternSource.regexpValue.flags;
ownsFlags = Boolean(patternSource.regexpValue.ownedNode);
flagsNode = patternSource.regexpValue.ownedNode;
}
else {
flagsString = "";
ownsFlags = true;
}
}
regexpDataList.push({
call: newOrCall,
patternNode: patternArg,
patternSource,
flagsNode,
flagsString,
ownsFlags,
});
}
for (const { call, patternNode, patternSource, flagsNode, flagsString, ownsFlags, } of regexpDataList) {
verify(patternNode, flagsNode, call, patternSource, flagsString, ownsFlags, (base) => {
return createSourceVisitorFromRules(rules, {
node: patternNode,
flagsString,
ownsFlags,
regexpNode: call,
...base,
});
});
}
},
};
}
function createLiteralVisitorFromRules(rules, context) {
const handlers = [];
for (const rule of rules) {
if (rule.createLiteralVisitor) {
handlers.push(rule.createLiteralVisitor(context));
}
}
return composeRegExpVisitors(handlers);
}
function createSourceVisitorFromRules(rules, context) {
const handlers = [];
for (const rule of rules) {
if (rule.createSourceVisitor) {
handlers.push(rule.createSourceVisitor(context));
}
}
return composeRegExpVisitors(handlers);
}
function visitInvalidForRules(rules, context) {
var _a;
for (const rule of rules) {
(_a = rule.visitInvalid) === null || _a === void 0 ? void 0 : _a.call(rule, context);
}
}
function visitUnknownForRules(rules, context) {
var _a;
for (const rule of rules) {
(_a = rule.visitUnknown) === null || _a === void 0 ? void 0 : _a.call(rule, context);
}
}
function composeRegExpVisitors(handlers) {
const handler = {};
for (const visitor of handlers) {
const entries = Object.entries(visitor);
for (const [key, fn] of entries) {
const orig = handler[key];
if (orig) {
handler[key] = (node) => {
orig(node);
fn(node);
};
}
else {
handler[key] = fn;
}
}
}
return handler;
}
function compositingVisitors(visitor, ...visitors) {
for (const v of visitors) {
for (const key in v) {
const orig = visitor[key];
if (orig) {
visitor[key] = (...args) => {
orig(...args);
v[key](...args);
};
}
else {
visitor[key] = v[key];
}
}
}
return visitor;
}
function buildRegExpContextBase({ patternSource, regexpNode, flagsNode, context, flags, parsedPattern, }) {
const sourceCode = context.sourceCode;
let cacheUsageOfPattern = null;
const cacheCapturingGroupReferenceMap = new Map();
const getAllCapturingGroups = (0, util_1.lazy)(() => (0, regexp_ast_1.extractCaptures)(parsedPattern).groups);
return {
getRegexpLocation: (range, offsets) => {
if (offsets) {
return patternSource.getAstLocation({
start: range.start + offsets[0],
end: range.start + offsets[1],
});
}
return patternSource.getAstLocation(range);
},
getFlagsLocation: () => (0, ast_utils_1.getFlagsLocation)(sourceCode, regexpNode, flagsNode),
getFlagLocation: (flag) => (0, ast_utils_1.getFlagLocation)(sourceCode, regexpNode, flagsNode, flag),
fixReplaceNode: (node, replacement) => {
return fixReplaceNode(patternSource, node, replacement);
},
fixReplaceQuant: (qNode, replacement) => {
return fixReplaceQuant(patternSource, qNode, replacement);
},
fixReplaceFlags: (newFlags, includePattern) => {
return fixReplaceFlags(patternSource, regexpNode, flagsNode, newFlags, includePattern !== null && includePattern !== void 0 ? includePattern : true);
},
getUsageOfPattern: () => (cacheUsageOfPattern !== null && cacheUsageOfPattern !== void 0 ? cacheUsageOfPattern : (cacheUsageOfPattern = (0, get_usage_of_pattern_1.getUsageOfPattern)(regexpNode, context))),
getCapturingGroupReferences: (options) => {
var _a;
const strictTypes = Boolean((_a = options === null || options === void 0 ? void 0 : options.strictTypes) !== null && _a !== void 0 ? _a : true);
const cacheCapturingGroupReference = cacheCapturingGroupReferenceMap.get(strictTypes);
if (cacheCapturingGroupReference) {
return cacheCapturingGroupReference;
}
const countOfCapturingGroup = getAllCapturingGroups().length;
const capturingGroupReferences = [
...(0, extract_capturing_group_references_1.extractCapturingGroupReferences)(regexpNode, flags, (0, type_tracker_1.createTypeTracker)(context), countOfCapturingGroup, context, { strictTypes }),
];
cacheCapturingGroupReferenceMap.set(strictTypes, capturingGroupReferences);
return capturingGroupReferences;
},
getAllCapturingGroups,
pattern: parsedPattern.raw,
patternAst: parsedPattern,
patternSource,
flags: (0, regexp_ast_analysis_1.toCache)(flags),
};
}
function buildUnparsableRegExpContextBase({ patternSource, patternNode, regexpNode, context, flags: originalFlags, flagsString, flagsNode, ownsFlags, }) {
const sourceCode = context.sourceCode;
const flags = (0, regexp_ast_analysis_1.toCache)(originalFlags);
return {
regexpNode,
node: patternNode,
flags,
flagsString,
ownsFlags,
getFlagsLocation: () => (0, ast_utils_1.getFlagsLocation)(sourceCode, regexpNode, flagsNode),
getFlagLocation: (flag) => (0, ast_utils_1.getFlagLocation)(sourceCode, regexpNode, flagsNode, flag),
fixReplaceFlags: (newFlags, includePattern) => {
return fixReplaceFlags(patternSource, regexpNode, flagsNode, newFlags, includePattern !== null && includePattern !== void 0 ? includePattern : true);
},
};
}
function fixReplaceNode(patternSource, regexpNode, replacement) {
return (fixer) => {
const range = patternSource.getReplaceRange(regexpNode);
if (range == null) {
return null;
}
let text;
if (typeof replacement === "string") {
text = replacement;
}
else {
text = replacement();
if (text == null) {
return null;
}
}
return range.replace(fixer, text);
};
}
function fixReplaceQuant(patternSource, quantifier, replacement) {
return (fixer) => {
let text;
if (typeof replacement !== "function") {
text = replacement;
}
else {
text = replacement();
if (text == null) {
return null;
}
}
const offset = (0, regexp_ast_1.getQuantifierOffsets)(quantifier);
if (typeof text !== "string") {
if (text.greedy !== undefined &&
text.greedy !== quantifier.greedy) {
offset[1] += 1;
}
text = (0, regexp_ast_1.quantToString)(text);
}
const range = patternSource.getReplaceRange({
start: quantifier.start + offset[0],
end: quantifier.start + offset[1],
});
if (range == null) {
return null;
}
return range.replace(fixer, text);
};
}
function fixReplaceFlags(patternSource, regexpNode, flagsNode, replacement, includePattern) {
return (fixer) => {
let newFlags;
if (typeof replacement === "string") {
newFlags = replacement;
}
else {
newFlags = replacement();
if (newFlags == null) {
return null;
}
}
if (!/^[a-z]*$/iu.test(newFlags)) {
return null;
}
if (includePattern && (0, utils_1.isRegexpLiteral)(regexpNode)) {
return fixer.replaceText(regexpNode, `/${regexpNode.regex.pattern}/${newFlags}`);
}
let flagsFix;
if ((0, utils_1.isRegexpLiteral)(regexpNode)) {
flagsFix = fixer.replaceTextRange((0, ast_utils_1.getFlagsRange)(regexpNode), newFlags);
}
else if (flagsNode) {
const range = (0, ast_utils_1.getFlagsRange)(flagsNode);
if (range == null) {
return null;
}
flagsFix = fixer.replaceTextRange(range, newFlags);
}
else {
if (regexpNode.arguments.length !== 1) {
return null;
}
const end = regexpNode.range[1];
flagsFix = fixer.replaceTextRange([end - 1, end], `, "${newFlags}")`);
}
if (!includePattern) {
return flagsFix;
}
if (!patternSource) {
return null;
}
const patternRange = patternSource.getReplaceRange({
start: 0,
end: patternSource.value.length,
});
if (patternRange == null) {
return null;
}
const patternFix = patternRange.replace(fixer, patternSource.value);
return [patternFix, flagsFix];
};
}
function mightCreateNewElement(before, after) {
if (before.endsWith("\\c") && /^[a-z]/iu.test(after)) {
return true;
}
if (/(?:^|[^\\])(?:\\{2})*\\(?:x[\dA-Fa-f]?|u[\dA-Fa-f]{0,3})$/u.test(before) &&
/^[\da-f]/iu.test(after)) {
return true;
}
if ((/(?:^|[^\\])(?:\\{2})*\\u$/u.test(before) &&
/^\{[\da-f]*(?:\}[\s\S]*)?$/iu.test(after)) ||
(/(?:^|[^\\])(?:\\{2})*\\u\{[\da-f]*$/u.test(before) &&
/^(?:[\da-f]+\}?|\})/iu.test(after))) {
return true;
}
if ((/(?:^|[^\\])(?:\\{2})*\\0[0-7]?$/u.test(before) &&
/^[0-7]/u.test(after)) ||
(/(?:^|[^\\])(?:\\{2})*\\[1-7]$/u.test(before) && /^[0-7]/u.test(after))) {
return true;
}
if ((/(?:^|[^\\])(?:\\{2})*\\[1-9]\d*$/u.test(before) &&
/^\d/u.test(after)) ||
(/(?:^|[^\\])(?:\\{2})*\\k$/u.test(before) && after.startsWith("<")) ||
/(?:^|[^\\])(?:\\{2})*\\k<[^<>]*$/u.test(before)) {
return true;
}
if ((/(?:^|[^\\])(?:\\{2})*\\p$/iu.test(before) &&
/^\{[\w=]*(?:\}[\s\S]*)?$/u.test(after)) ||
(/(?:^|[^\\])(?:\\{2})*\\p\{[\w=]*$/iu.test(before) &&
/^[\w=]+(?:\}[\s\S]*)?$|^\}/u.test(after))) {
return true;
}
if ((/(?:^|[^\\])(?:\\{2})*\{\d*$/u.test(before) &&
/^[\d,}]/u.test(after)) ||
(/(?:^|[^\\])(?:\\{2})*\{\d+,$/u.test(before) &&
/^(?:\d+(?:\}|$)|\})/u.test(after)) ||
(/(?:^|[^\\])(?:\\{2})*\{\d+,\d*$/u.test(before) &&
after.startsWith("}"))) {
return true;
}
return false;
}
function fixRemoveCharacterClassElement(context, element) {
const cc = element.parent;
if (cc.type !== "CharacterClass") {
throw new Error("Only call this function for character class elements.");
}
return context.fixReplaceNode(element, () => {
const textBefore = cc.raw.slice(0, element.start - cc.start);
const textAfter = cc.raw.slice(element.end - cc.start);
if (mightCreateNewElement(textBefore, textAfter)) {
return null;
}
const elements = cc.elements;
const elementIndex = elements.indexOf(element);
const elementBefore = cc.elements[elementIndex - 1];
const elementAfter = cc.elements[elementIndex + 1];
if (elementBefore &&
elementAfter &&
elementBefore.type === "Character" &&
elementBefore.raw === "-" &&
elementAfter.type === "Character") {
return null;
}
if ((textAfter.startsWith("-") &&
elementBefore &&
elementBefore.type === "Character") ||
(textAfter.startsWith("^") && !cc.negate && !elementBefore)) {
return "\\";
}
return "";
});
}
function fixRemoveAlternative(context, alternative) {
const { parent } = alternative;
if (parent.alternatives.length === 1) {
return context.fixReplaceNode(alternative, "[]");
}
return context.fixReplaceNode(parent, () => {
let { start, end } = alternative;
if (parent.alternatives[0] === alternative) {
end++;
}
else {
start--;
}
const before = parent.raw.slice(0, start - parent.start);
const after = parent.raw.slice(end - parent.start);
return before + after;
});
}
function fixRemoveStringAlternative(context, alternative) {
const { parent } = alternative;
if (parent.alternatives.length === 1) {
return context.fixReplaceNode(parent, "[]");
}
return context.fixReplaceNode(parent, () => {
let { start, end } = alternative;
if (parent.alternatives[0] === alternative) {
end++;
}
else {
start--;
}
const before = parent.raw.slice(0, start - parent.start);
const after = parent.raw.slice(end - parent.start);
return before + after;
});
}
function canUnwrapped(node, text) {
let textBefore, textAfter;
const parent = node.parent;
if (parent.type === "Alternative") {
textBefore = parent.raw.slice(0, node.start - parent.start);
textAfter = parent.raw.slice(node.end - parent.start);
}
else if (parent.type === "Quantifier") {
const alt = parent.parent;
textBefore = alt.raw.slice(0, node.start - alt.start);
textAfter = alt.raw.slice(node.end - alt.start);
}
else {
return true;
}
return (!mightCreateNewElement(textBefore, text) &&
!mightCreateNewElement(text, textAfter));
}

View file

@ -0,0 +1,3 @@
import type { Word, JS } from "refa";
export declare function getLexicographicallySmallest(set: JS.UnicodeSet): Word | undefined;
export declare function getLexicographicallySmallestInConcatenation(elements: readonly JS.UnicodeSet[]): Word | undefined;

View file

@ -0,0 +1,65 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLexicographicallySmallest = getLexicographicallySmallest;
exports.getLexicographicallySmallestInConcatenation = getLexicographicallySmallestInConcatenation;
function findMin(array, compare) {
if (array.length === 0) {
return undefined;
}
let min = array[0];
for (let i = 1; i < array.length; i++) {
const item = array[i];
if (compare(item, min) < 0) {
min = item;
}
}
return min;
}
function compareWords(a, b) {
const l = Math.min(a.length, b.length);
for (let i = 0; i < l; i++) {
const diff = a[i] - b[i];
if (diff !== 0) {
return diff;
}
}
return a.length - b.length;
}
function getLexicographicallySmallest(set) {
if (set.accept.isEmpty) {
return set.chars.isEmpty ? undefined : [set.chars.ranges[0].min];
}
const words = set.accept.wordSets.map((w) => w.map((c) => c.ranges[0].min));
return findMin(words, compareWords);
}
function getLexicographicallySmallestInConcatenation(elements) {
if (elements.length === 1) {
return getLexicographicallySmallest(elements[0]);
}
let smallest = [];
for (let i = elements.length - 1; i >= 0; i--) {
const set = elements[i];
if (set.isEmpty) {
return undefined;
}
else if (set.accept.isEmpty) {
smallest.unshift(set.chars.ranges[0].min);
}
else {
let words = [
...(set.chars.isEmpty ? [] : [[set.chars]]),
...set.accept.wordSets,
].map((w) => w.map((c) => c.ranges[0].min));
const seenLengths = new Set();
words = words.sort(compareWords).filter((w) => {
if (seenLengths.has(w.length)) {
return false;
}
seenLengths.add(w.length);
return true;
});
smallest = findMin(words.map((w) => [...w, ...smallest]), compareWords);
}
}
return smallest;
}

View file

@ -0,0 +1,4 @@
import type { CharacterClassElement, Node } from "@eslint-community/regexpp/ast";
export declare function mentionChar(element: CharacterClassElement): string;
export declare function mention(element: Node | string): string;
export declare function joinEnglishList(list: readonly string[]): string;

View file

@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mentionChar = mentionChar;
exports.mention = mention;
exports.joinEnglishList = joinEnglishList;
function formatCodePoint(value) {
return `U+${value.toString(16).padStart(4, "0")}`;
}
function mentionChar(element) {
if (element.type === "Character") {
const value = formatCodePoint(element.value);
return `'${escape(element.raw)}' (${value})`;
}
if (element.type === "CharacterClassRange") {
const min = formatCodePoint(element.min.value);
const max = formatCodePoint(element.max.value);
return `'${escape(element.raw)}' (${min} - ${max})`;
}
return mention(element);
}
function mention(element) {
return `'${escape(typeof element === "string" ? element : element.raw)}'`;
}
function escape(value) {
return (value
.replace(/\\(?<char>[\s\S])/gu, (m, char) => {
if (char.charCodeAt(0) < 0x20) {
return escapeControl(char);
}
return m;
})
.replace(/[\0-\x1f]/gu, escapeControl));
}
function escapeControl(control) {
if (control === "\t")
return control;
if (control === "\n")
return "\\n";
if (control === "\r")
return "\\r";
return `\\x${control.charCodeAt(0).toString(16).padStart(2, "0")}`;
}
function joinEnglishList(list) {
if (list.length === 0)
return "none";
if (list.length === 1)
return list[0];
if (list.length === 2)
return `${list[0]} and ${list[1]}`;
let result = list[0];
for (let i = 1; i < list.length - 1; i++) {
result += `, ${list[i]}`;
}
result += `, and ${list[list.length - 1]}`;
return result;
}

View file

@ -0,0 +1,19 @@
import type { AST } from "@eslint-community/regexpp";
import type { Expression, NoParent } from "refa";
import { JS } from "refa";
export type NestedAlternative = AST.Alternative | AST.CharacterClassElement | AST.StringAlternative;
export declare class PartialParser {
private readonly parser;
private readonly options;
private readonly nativeCache;
constructor(parser: JS.Parser, options?: JS.ParseOptions);
parse(node: JS.ParsableElement, alternative: NestedAlternative): NoParent<Expression>;
private parseAlternatives;
private parseAlternative;
private parseStringAlternatives;
private parseStringAlternative;
private parseElement;
private parseCharacterClass;
private nativeParseElement;
private nativeParseElementUncached;
}

View file

@ -0,0 +1,188 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PartialParser = void 0;
const refa_1 = require("refa");
const util_1 = require("./util");
class Context {
constructor(alternative) {
this.alternative = alternative;
const ancestors = new Set();
for (let n = alternative; n; n = n.parent) {
ancestors.add(n);
}
this.ancestors = ancestors;
}
}
class PartialParser {
constructor(parser, options = {}) {
this.nativeCache = new WeakMap();
this.parser = parser;
this.options = options;
}
parse(node, alternative) {
switch (node.type) {
case "Pattern":
return {
type: "Expression",
alternatives: this.parseAlternatives(node.alternatives, new Context(alternative)),
};
case "Alternative":
return {
type: "Expression",
alternatives: [
this.parseAlternative(node, new Context(alternative)),
],
};
default:
return {
type: "Expression",
alternatives: [
{
type: "Concatenation",
elements: [
this.parseElement(node, new Context(alternative)),
],
},
],
};
}
}
parseAlternatives(alternatives, context) {
const ancestor = alternatives.find((a) => context.ancestors.has(a));
if (ancestor) {
return [this.parseAlternative(ancestor, context)];
}
const result = [];
for (const a of alternatives) {
result.push(...this.parser.parseElement(a, this.options).expression
.alternatives);
}
return result;
}
parseAlternative(alternative, context) {
return {
type: "Concatenation",
elements: alternative.elements.map((e) => this.parseElement(e, context)),
};
}
parseStringAlternatives(alternatives, context) {
const ancestor = alternatives.find((a) => context.ancestors.has(a));
if (ancestor) {
return [this.parseStringAlternative(ancestor)];
}
return alternatives.map((a) => this.parseStringAlternative(a));
}
parseStringAlternative(alternative) {
return {
type: "Concatenation",
elements: alternative.elements.map((e) => this.nativeParseElement(e)),
};
}
parseElement(element, context) {
if (!context.ancestors.has(element)) {
return this.nativeParseElement(element);
}
switch (element.type) {
case "Assertion":
case "Backreference":
case "Character":
case "CharacterSet":
case "ExpressionCharacterClass":
return this.nativeParseElement(element);
case "CharacterClassRange":
if (context.alternative === element.min) {
return this.nativeParseElement(context.alternative);
}
else if (context.alternative === element.max) {
return this.nativeParseElement(context.alternative);
}
return this.nativeParseElement(element);
case "CharacterClass":
return this.parseCharacterClass(element, context);
case "ClassStringDisjunction":
return {
type: "Alternation",
alternatives: this.parseStringAlternatives(element.alternatives, context),
};
case "Group":
case "CapturingGroup":
return {
type: "Alternation",
alternatives: this.parseAlternatives(element.alternatives, context),
};
case "Quantifier": {
const alternatives = element.element.type === "Group" ||
element.element.type === "CapturingGroup"
? this.parseAlternatives(element.element.alternatives, context)
: [
{
type: "Concatenation",
elements: [
this.parseElement(element.element, context),
],
},
];
return {
type: "Quantifier",
min: element.min,
max: element.max,
lazy: !element.greedy,
alternatives,
};
}
default:
throw (0, util_1.assertNever)(element);
}
}
parseCharacterClass(cc, context) {
if (cc.negate ||
!context.ancestors.has(cc) ||
context.alternative.type === "Alternative") {
return this.nativeParseElement(cc);
}
for (const e of cc.elements) {
if (context.ancestors.has(e)) {
return this.parseElement(e, context);
}
}
return this.nativeParseElement(cc);
}
nativeParseElement(element) {
let cached = this.nativeCache.get(element);
if (!cached) {
cached = this.nativeParseElementUncached(element);
this.nativeCache.set(element, cached);
}
return cached;
}
nativeParseElementUncached(element) {
if (element.type === "CharacterClassRange") {
const range = {
min: element.min.value,
max: element.max.value,
};
return {
type: "CharacterClass",
characters: refa_1.JS.createCharSet([range], this.parser.flags),
};
}
else if (element.type === "ClassStringDisjunction") {
return {
type: "Alternation",
alternatives: element.alternatives.map((a) => this.parseStringAlternative(a)),
};
}
const { expression } = this.parser.parseElement(element, this.options);
if (expression.alternatives.length === 1) {
const elements = expression.alternatives[0].elements;
if (elements.length === 1) {
return elements[0];
}
}
return {
type: "Alternation",
alternatives: expression.alternatives,
};
}
}
exports.PartialParser = PartialParser;

View file

@ -0,0 +1,8 @@
import type { CharSet } from "refa";
import { JS } from "refa";
import type { ReadonlyFlags } from "regexp-ast-analysis";
import type { RegExpContext } from ".";
export declare function getJSRegexppAst(context: RegExpContext, ignoreSticky?: boolean): JS.RegexppAst;
export declare const getParser: (key: RegExpContext) => JS.Parser;
export declare function assertValidFlags(flags: ReadonlyFlags): asserts flags is JS.Flags;
export declare function toCharSetSource(charSetOrChar: CharSet | number, flags: ReadonlyFlags): string;

View file

@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getParser = void 0;
exports.getJSRegexppAst = getJSRegexppAst;
exports.assertValidFlags = assertValidFlags;
exports.toCharSetSource = toCharSetSource;
const refa_1 = require("refa");
const util_1 = require("./util");
function getJSRegexppAst(context, ignoreSticky = false) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const { flags, flagsString, patternAst } = context;
return {
pattern: patternAst,
flags: {
type: "Flags",
raw: flagsString !== null && flagsString !== void 0 ? flagsString : "",
parent: null,
start: NaN,
end: NaN,
dotAll: (_a = flags.dotAll) !== null && _a !== void 0 ? _a : false,
global: (_b = flags.global) !== null && _b !== void 0 ? _b : false,
hasIndices: (_c = flags.hasIndices) !== null && _c !== void 0 ? _c : false,
ignoreCase: (_d = flags.ignoreCase) !== null && _d !== void 0 ? _d : false,
multiline: (_e = flags.multiline) !== null && _e !== void 0 ? _e : false,
sticky: !ignoreSticky && ((_f = flags.sticky) !== null && _f !== void 0 ? _f : false),
unicode: (_g = flags.unicode) !== null && _g !== void 0 ? _g : false,
unicodeSets: (_h = flags.unicodeSets) !== null && _h !== void 0 ? _h : false,
},
};
}
exports.getParser = (0, util_1.cachedFn)((context) => refa_1.JS.Parser.fromAst(getJSRegexppAst(context)));
function assertValidFlags(flags) {
if (!refa_1.JS.isFlags(flags)) {
throw new Error(`Invalid flags: ${JSON.stringify(flags)}`);
}
}
function toCharSetSource(charSetOrChar, flags) {
assertValidFlags(flags);
let charSet;
if (typeof charSetOrChar === "number") {
charSet = refa_1.JS.createCharSet([charSetOrChar], flags);
}
else {
charSet = charSetOrChar;
}
return refa_1.JS.toLiteral({
type: "Concatenation",
elements: [{ type: "CharacterClass", characters: charSet }],
}, { flags }).source;
}

View file

@ -0,0 +1,28 @@
import type { ReadonlyFlags } from "regexp-ast-analysis";
export declare const RESERVED_DOUBLE_PUNCTUATOR_CHARS: ReadonlySet<string>;
export declare const RESERVED_DOUBLE_PUNCTUATOR_CP: ReadonlySet<number>;
export declare const RESERVED_DOUBLE_PUNCTUATOR_PATTERN: RegExp;
export declare function isOctalEscape(raw: string): boolean;
export declare function isControlEscape(raw: string): boolean;
export declare function isHexadecimalEscape(raw: string): boolean;
export declare function isUnicodeEscape(raw: string): boolean;
export declare function isUnicodeCodePointEscape(raw: string): boolean;
export declare enum EscapeSequenceKind {
octal = "octal",
control = "control",
hexadecimal = "hexadecimal",
unicode = "unicode",
unicodeCodePoint = "unicode code point"
}
export declare function getEscapeSequenceKind(raw: string): EscapeSequenceKind | null;
export declare function isEscapeSequence(raw: string): boolean;
export declare function isHexLikeEscape(raw: string): boolean;
export declare const FLAG_GLOBAL = "g";
export declare const FLAG_DOT_ALL = "s";
export declare const FLAG_HAS_INDICES = "d";
export declare const FLAG_IGNORECASE = "i";
export declare const FLAG_MULTILINE = "m";
export declare const FLAG_STICKY = "y";
export declare const FLAG_UNICODE = "u";
export declare const FLAG_UNICODE_SETS = "v";
export declare function parseFlags(flags: string): ReadonlyFlags;

View file

@ -0,0 +1,96 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FLAG_UNICODE_SETS = exports.FLAG_UNICODE = exports.FLAG_STICKY = exports.FLAG_MULTILINE = exports.FLAG_IGNORECASE = exports.FLAG_HAS_INDICES = exports.FLAG_DOT_ALL = exports.FLAG_GLOBAL = exports.EscapeSequenceKind = exports.RESERVED_DOUBLE_PUNCTUATOR_PATTERN = exports.RESERVED_DOUBLE_PUNCTUATOR_CP = exports.RESERVED_DOUBLE_PUNCTUATOR_CHARS = void 0;
exports.isOctalEscape = isOctalEscape;
exports.isControlEscape = isControlEscape;
exports.isHexadecimalEscape = isHexadecimalEscape;
exports.isUnicodeEscape = isUnicodeEscape;
exports.isUnicodeCodePointEscape = isUnicodeCodePointEscape;
exports.getEscapeSequenceKind = getEscapeSequenceKind;
exports.isEscapeSequence = isEscapeSequence;
exports.isHexLikeEscape = isHexLikeEscape;
exports.parseFlags = parseFlags;
const util_1 = require("./util");
const RESERVED_DOUBLE_PUNCTUATORS = "&!#$%*+,.:;<=>?@^`~-";
exports.RESERVED_DOUBLE_PUNCTUATOR_CHARS = new Set(RESERVED_DOUBLE_PUNCTUATORS);
exports.RESERVED_DOUBLE_PUNCTUATOR_CP = new Set((0, util_1.toCodePoints)(RESERVED_DOUBLE_PUNCTUATORS));
exports.RESERVED_DOUBLE_PUNCTUATOR_PATTERN = /&&|!!|##|\$\$|%%|\*\*|\+\+|,,|\.\.|::|;;|<<|==|>>|\?\?|@@|\^\^|``|~~|--/u;
function isOctalEscape(raw) {
return /^\\[0-7]{1,3}$/u.test(raw);
}
function isControlEscape(raw) {
return /^\\c[A-Za-z]$/u.test(raw);
}
function isHexadecimalEscape(raw) {
return /^\\x[\dA-Fa-f]{2}$/u.test(raw);
}
function isUnicodeEscape(raw) {
return /^\\u[\dA-Fa-f]{4}$/u.test(raw);
}
function isUnicodeCodePointEscape(raw) {
return /^\\u\{[\dA-Fa-f]{1,8}\}$/u.test(raw);
}
var EscapeSequenceKind;
(function (EscapeSequenceKind) {
EscapeSequenceKind["octal"] = "octal";
EscapeSequenceKind["control"] = "control";
EscapeSequenceKind["hexadecimal"] = "hexadecimal";
EscapeSequenceKind["unicode"] = "unicode";
EscapeSequenceKind["unicodeCodePoint"] = "unicode code point";
})(EscapeSequenceKind || (exports.EscapeSequenceKind = EscapeSequenceKind = {}));
function getEscapeSequenceKind(raw) {
if (!raw.startsWith("\\")) {
return null;
}
if (isOctalEscape(raw)) {
return EscapeSequenceKind.octal;
}
if (isControlEscape(raw)) {
return EscapeSequenceKind.control;
}
if (isHexadecimalEscape(raw)) {
return EscapeSequenceKind.hexadecimal;
}
if (isUnicodeEscape(raw)) {
return EscapeSequenceKind.unicode;
}
if (isUnicodeCodePointEscape(raw)) {
return EscapeSequenceKind.unicodeCodePoint;
}
return null;
}
function isEscapeSequence(raw) {
return getEscapeSequenceKind(raw) !== null;
}
function isHexLikeEscape(raw) {
const kind = getEscapeSequenceKind(raw);
return (kind === EscapeSequenceKind.hexadecimal ||
kind === EscapeSequenceKind.unicode ||
kind === EscapeSequenceKind.unicodeCodePoint);
}
exports.FLAG_GLOBAL = "g";
exports.FLAG_DOT_ALL = "s";
exports.FLAG_HAS_INDICES = "d";
exports.FLAG_IGNORECASE = "i";
exports.FLAG_MULTILINE = "m";
exports.FLAG_STICKY = "y";
exports.FLAG_UNICODE = "u";
exports.FLAG_UNICODE_SETS = "v";
const flagsCache = new Map();
function parseFlags(flags) {
let cached = flagsCache.get(flags);
if (cached === undefined) {
cached = {
dotAll: flags.includes(exports.FLAG_DOT_ALL),
global: flags.includes(exports.FLAG_GLOBAL),
hasIndices: flags.includes(exports.FLAG_HAS_INDICES),
ignoreCase: flags.includes(exports.FLAG_IGNORECASE),
multiline: flags.includes(exports.FLAG_MULTILINE),
sticky: flags.includes(exports.FLAG_STICKY),
unicode: flags.includes(exports.FLAG_UNICODE),
unicodeSets: flags.includes(exports.FLAG_UNICODE_SETS),
};
flagsCache.set(flags, cached);
}
return cached;
}

View file

@ -0,0 +1,4 @@
import type { RegExpLiteral, Pattern } from "@eslint-community/regexpp/ast";
import type { Rule } from "eslint";
import type { Expression } from "estree";
export declare function getRegExpNodeFromExpression(node: Expression, context: Rule.RuleContext): RegExpLiteral | Pattern | null;

View file

@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRegExpNodeFromExpression = getRegExpNodeFromExpression;
const regexpp_1 = require("@eslint-community/regexpp");
const ast_utils_1 = require("../ast-utils");
const parser = new regexpp_1.RegExpParser();
function getRegExpNodeFromExpression(node, context) {
if (node.type === "Literal") {
if ("regex" in node && node.regex) {
try {
return parser.parsePattern(node.regex.pattern, 0, node.regex.pattern.length, {
unicode: node.regex.flags.includes("u"),
unicodeSets: node.regex.flags.includes("v"),
});
}
catch (_a) {
return null;
}
}
return null;
}
const evaluated = (0, ast_utils_1.getStaticValue)(context, node);
if (!evaluated || !(evaluated.value instanceof RegExp)) {
return null;
}
try {
return (0, regexpp_1.parseRegExpLiteral)(evaluated.value);
}
catch (_b) {
return null;
}
}

View file

@ -0,0 +1,5 @@
import type { Alternative, CharacterClassElement, Element, Pattern, StringAlternative } from "@eslint-community/regexpp/ast";
import type { ReadonlyFlags } from "regexp-ast-analysis";
export declare const getIgnoreCaseFlags: (key: ReadonlyFlags) => ReadonlyFlags;
export declare const getCaseSensitiveFlags: (key: ReadonlyFlags) => ReadonlyFlags;
export declare function isCaseVariant(element: Element | CharacterClassElement | StringAlternative | Alternative | Pattern, flags: ReadonlyFlags, wholeCharacterClass?: boolean): boolean;

View file

@ -0,0 +1,94 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCaseSensitiveFlags = exports.getIgnoreCaseFlags = void 0;
exports.isCaseVariant = isCaseVariant;
const regexp_ast_analysis_1 = require("regexp-ast-analysis");
const util_1 = require("../util");
exports.getIgnoreCaseFlags = (0, util_1.cachedFn)((flags) => {
return flags.ignoreCase
? flags
: (0, regexp_ast_analysis_1.toCache)({ ...flags, ignoreCase: true });
});
exports.getCaseSensitiveFlags = (0, util_1.cachedFn)((flags) => {
return flags.ignoreCase === false
? flags
: (0, regexp_ast_analysis_1.toCache)({ ...flags, ignoreCase: false });
});
function isCaseVariant(element, flags, wholeCharacterClass = true) {
const unicodeLike = Boolean(flags.unicode || flags.unicodeSets);
const iSet = (0, exports.getIgnoreCaseFlags)(flags);
const iUnset = (0, exports.getCaseSensitiveFlags)(flags);
function ccElementIsCaseVariant(e) {
switch (e.type) {
case "Character":
return (0, regexp_ast_analysis_1.toCharSet)(e, iSet).size !== 1;
case "CharacterClassRange":
return !(0, regexp_ast_analysis_1.toCharSet)(e, iSet).equals((0, regexp_ast_analysis_1.toCharSet)(e, iUnset));
case "CharacterSet":
switch (e.kind) {
case "word":
return unicodeLike;
case "property":
return !(0, regexp_ast_analysis_1.toUnicodeSet)(e, iSet).equals((0, regexp_ast_analysis_1.toUnicodeSet)(e, iUnset));
default:
return false;
}
case "CharacterClass":
if (!wholeCharacterClass) {
return e.elements.some(ccElementIsCaseVariant);
}
return !(0, regexp_ast_analysis_1.toUnicodeSet)(e, iSet).equals((0, regexp_ast_analysis_1.toUnicodeSet)(e, iUnset));
case "ExpressionCharacterClass":
return ccElementIsCaseVariant(e.expression);
case "ClassIntersection":
case "ClassSubtraction":
return !(0, regexp_ast_analysis_1.toUnicodeSet)(e, iSet).equals((0, regexp_ast_analysis_1.toUnicodeSet)(e, iUnset));
case "ClassStringDisjunction":
if (!wholeCharacterClass) {
return e.alternatives.some(ccElementIsCaseVariant);
}
return !(0, regexp_ast_analysis_1.toUnicodeSet)(e, iSet).equals((0, regexp_ast_analysis_1.toUnicodeSet)(e, iUnset));
case "StringAlternative":
return e.elements.some(ccElementIsCaseVariant);
default:
return (0, util_1.assertNever)(e);
}
}
return (0, regexp_ast_analysis_1.hasSomeDescendant)(element, (d) => {
switch (d.type) {
case "Assertion":
return unicodeLike && d.kind === "word";
case "Backreference": {
const outside = getReferencedGroupsFromBackreference(d).filter((resolved) => !(0, regexp_ast_analysis_1.hasSomeDescendant)(element, resolved));
if (outside.length === 0) {
return false;
}
return (!(0, regexp_ast_analysis_1.isEmptyBackreference)(d, flags) &&
outside.some((resolved) => isCaseVariant(resolved, flags)));
}
case "Character":
case "CharacterClassRange":
case "CharacterSet":
case "CharacterClass":
case "ExpressionCharacterClass":
case "ClassIntersection":
case "ClassSubtraction":
case "ClassStringDisjunction":
case "StringAlternative":
return ccElementIsCaseVariant(d);
default:
return false;
}
}, (d) => {
return (d.type !== "CharacterClass" &&
d.type !== "CharacterClassRange" &&
d.type !== "ExpressionCharacterClass" &&
d.type !== "ClassStringDisjunction");
});
}
function getReferencedGroupsFromBackreference(backRef) {
return [backRef.resolved].flat().filter((group) => {
const closestAncestor = (0, regexp_ast_analysis_1.getClosestAncestor)(backRef, group);
return (closestAncestor !== group && closestAncestor.type === "Alternative");
});
}

View file

@ -0,0 +1,11 @@
import type { Alternative, CapturingGroup, Element, Node, Pattern, RegExpLiteral } from "@eslint-community/regexpp/ast";
import type { FirstConsumedChar, MatchingDirection, ReadonlyFlags } from "regexp-ast-analysis";
export type ShortCircuit = (aNode: Node, bNode: Node) => boolean | null;
export declare function getFirstConsumedCharPlusAfter(element: Element | Alternative, direction: MatchingDirection, flags: ReadonlyFlags): FirstConsumedChar;
export interface CapturingGroups {
groups: CapturingGroup[];
names: Set<string>;
count: number;
}
export declare function extractCaptures(pattern: RegExpLiteral | Pattern): CapturingGroups;
export declare function hasCapturingGroup(node: Node): boolean;

View file

@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFirstConsumedCharPlusAfter = getFirstConsumedCharPlusAfter;
exports.extractCaptures = extractCaptures;
exports.hasCapturingGroup = hasCapturingGroup;
const regexpp_1 = require("@eslint-community/regexpp");
const regexp_ast_analysis_1 = require("regexp-ast-analysis");
function getFirstConsumedCharPlusAfter(element, direction, flags) {
const consumed = (0, regexp_ast_analysis_1.getFirstConsumedChar)(element, direction, flags);
if (!consumed.empty) {
return consumed;
}
return regexp_ast_analysis_1.FirstConsumedChars.concat([consumed, (0, regexp_ast_analysis_1.getFirstConsumedCharAfter)(element, direction, flags)], flags);
}
function extractCaptures(pattern) {
const groups = [];
(0, regexpp_1.visitRegExpAST)(pattern, {
onCapturingGroupEnter(group) {
groups.push(group);
},
});
groups.sort((a, b) => a.start - b.start);
const names = new Set();
for (const group of groups) {
if (group.name !== null) {
names.add(group.name);
}
}
return { groups, names, count: groups.length };
}
function hasCapturingGroup(node) {
return (0, regexp_ast_analysis_1.hasSomeDescendant)(node, (d) => d.type === "CapturingGroup");
}

View file

@ -0,0 +1,7 @@
export * from "./common";
export * from "./ast";
export * from "./is-covered";
export * from "./is-equals";
export * from "./quantifier";
export * from "./case-variation";
export * from "./simplify-quantifier";

View file

@ -0,0 +1,23 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./common"), exports);
__exportStar(require("./ast"), exports);
__exportStar(require("./is-covered"), exports);
__exportStar(require("./is-equals"), exports);
__exportStar(require("./quantifier"), exports);
__exportStar(require("./case-variation"), exports);
__exportStar(require("./simplify-quantifier"), exports);

View file

@ -0,0 +1,8 @@
import type { Node } from "@eslint-community/regexpp/ast";
import type { ReadonlyFlags } from "regexp-ast-analysis";
type Options = {
flags: ReadonlyFlags;
canOmitRight: boolean;
};
export declare function isCoveredNode(left: Node, right: Node, options: Options): boolean;
export {};

View file

@ -0,0 +1,370 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isCoveredNode = isCoveredNode;
const regexp_ast_analysis_1 = require("regexp-ast-analysis");
const util_1 = require("../util");
const is_equals_1 = require("./is-equals");
class NormalizedOther {
static fromNode(node) {
return new NormalizedOther(node);
}
constructor(node) {
this.type = "NormalizedOther";
this.node = node;
}
}
class NormalizedCharacter {
static fromElement(element, options) {
return new NormalizedCharacter((0, regexp_ast_analysis_1.toCharSet)(element, options.flags));
}
static fromChars(charSet) {
return new NormalizedCharacter(charSet);
}
constructor(charSet) {
this.type = "NormalizedCharacter";
this.charSet = charSet;
}
}
class NormalizedAlternative {
static fromAlternative(node, options) {
const normalizeElements = [
...NormalizedAlternative.normalizedElements(function* () {
for (const element of node.elements) {
const normal = normalizeNode(element, options);
if (normal.type === "NormalizedAlternative") {
yield* normal.elements;
}
else {
yield normal;
}
}
}),
];
if (normalizeElements.length === 1) {
return normalizeElements[0];
}
return new NormalizedAlternative(normalizeElements, node);
}
static fromQuantifier(node, options) {
const normalizeElements = [
...NormalizedAlternative.normalizedElements(function* () {
const normalizeElement = normalizeNode(node.element, options);
for (let index = 0; index < node.min; index++) {
yield normalizeElement;
}
}),
];
if (normalizeElements.length === 1) {
return normalizeElements[0];
}
return new NormalizedAlternative(normalizeElements, node);
}
static fromElements(elements, node) {
const normalizeElements = [
...NormalizedAlternative.normalizedElements(function* () {
yield* elements;
}),
];
return new NormalizedAlternative(normalizeElements, node);
}
static *normalizedElements(generate) {
for (const node of generate()) {
if (node.type === "NormalizedAlternative") {
yield* node.elements;
}
else {
yield node;
}
}
}
constructor(elements, node) {
this.type = "NormalizedAlternative";
this.raw = node.raw;
this.elements = elements;
}
}
class NormalizedDisjunctions {
static fromNode(node, options) {
if (node.alternatives.length === 1) {
return NormalizedAlternative.fromAlternative(node.alternatives[0], options);
}
return new NormalizedDisjunctions(node, () => {
return node.alternatives.map((alt) => {
const n = normalizeNode(alt, options);
if (n.type === "NormalizedAlternative") {
return n;
}
return NormalizedAlternative.fromElements([n], alt);
});
});
}
static fromAlternatives(alternatives, node) {
return new NormalizedDisjunctions(node, () => alternatives);
}
constructor(node, getAlternatives) {
this.type = "NormalizedDisjunctions";
this.raw = node.raw;
this.getAlternatives = getAlternatives;
}
get alternatives() {
if (!this.normalizedAlternatives) {
this.normalizedAlternatives = this.getAlternatives();
}
return this.normalizedAlternatives;
}
}
class NormalizedLookaroundAssertion {
static fromNode(node, options) {
return new NormalizedLookaroundAssertion(node, options);
}
constructor(node, options) {
this.type = "NormalizedLookaroundAssertion";
this.raw = node.raw;
this.node = node;
this.options = options;
}
get alternatives() {
if (this.normalizedAlternatives) {
return this.normalizedAlternatives;
}
this.normalizedAlternatives = [];
for (const alt of this.node.alternatives) {
const node = normalizeNode(alt, this.options);
if (node.type === "NormalizedAlternative") {
this.normalizedAlternatives.push(node);
}
else {
this.normalizedAlternatives.push(NormalizedAlternative.fromElements([node], alt));
}
}
return this.normalizedAlternatives;
}
get kind() {
return this.node.kind;
}
get negate() {
return this.node.negate;
}
}
class NormalizedOptional {
static fromQuantifier(node, options) {
let alt = null;
if (node.min > 0) {
alt = NormalizedAlternative.fromQuantifier(node, options);
}
const max = node.max - node.min;
if (max > 0) {
const optional = new NormalizedOptional(node, options, max);
if (alt) {
if (alt.type === "NormalizedAlternative") {
return NormalizedAlternative.fromElements([...alt.elements, optional], node);
}
return NormalizedAlternative.fromElements([alt, optional], node);
}
return optional;
}
if (alt) {
return alt;
}
return NormalizedOther.fromNode(node);
}
constructor(node, options, max) {
this.type = "NormalizedOptional";
this.raw = node.raw;
this.max = max;
this.node = node;
this.options = options;
}
get element() {
var _a;
return ((_a = this.normalizedElement) !== null && _a !== void 0 ? _a : (this.normalizedElement = normalizeNode(this.node.element, this.options)));
}
decrementMax(dec = 1) {
if (this.max <= dec) {
return null;
}
if (this.max === Infinity) {
return this;
}
const opt = new NormalizedOptional(this.node, this.options, this.max - dec);
opt.normalizedElement = this.normalizedElement;
return opt;
}
}
function isCoveredNode(left, right, options) {
const leftNode = normalizeNode(left, options);
const rightNode = normalizeNode(right, options);
return isCoveredForNormalizedNode(leftNode, rightNode, options);
}
function isCoveredForNormalizedNode(left, right, options) {
if (right.type === "NormalizedDisjunctions") {
return right.alternatives.every((r) => isCoveredForNormalizedNode(left, r, options));
}
if (left.type === "NormalizedDisjunctions") {
return isCoveredAnyNode(left.alternatives, right, options);
}
if (left.type === "NormalizedAlternative") {
if (right.type === "NormalizedAlternative") {
return isCoveredAltNodes(left.elements, right.elements, options);
}
return isCoveredAltNodes(left.elements, [right], options);
}
else if (right.type === "NormalizedAlternative") {
return isCoveredAltNodes([left], right.elements, options);
}
if (left.type === "NormalizedOptional" ||
right.type === "NormalizedOptional") {
return isCoveredAltNodes([left], [right], options);
}
if (left.type === "NormalizedOther" || right.type === "NormalizedOther") {
if (left.type === "NormalizedOther" &&
right.type === "NormalizedOther") {
return (0, is_equals_1.isEqualNodes)(left.node, right.node, options.flags);
}
return false;
}
if (left.type === "NormalizedLookaroundAssertion" ||
right.type === "NormalizedLookaroundAssertion") {
if (left.type === "NormalizedLookaroundAssertion" &&
right.type === "NormalizedLookaroundAssertion") {
if (left.kind === right.kind && !left.negate && !right.negate) {
return right.alternatives.every((r) => isCoveredAnyNode(left.alternatives, r, options));
}
return (0, is_equals_1.isEqualNodes)(left.node, right.node, options.flags);
}
return false;
}
if (right.type === "NormalizedCharacter") {
return right.charSet.isSubsetOf(left.charSet);
}
return false;
}
const cacheNormalizeNode = new WeakMap();
function normalizeNode(node, options) {
let n = cacheNormalizeNode.get(node);
if (n) {
return n;
}
n = normalizeNodeWithoutCache(node, options);
cacheNormalizeNode.set(node, n);
return n;
}
function normalizeNodeWithoutCache(node, options) {
switch (node.type) {
case "CharacterSet":
case "CharacterClass":
case "Character":
case "CharacterClassRange":
case "ExpressionCharacterClass":
case "ClassIntersection":
case "ClassSubtraction":
case "ClassStringDisjunction":
case "StringAlternative": {
const set = (0, regexp_ast_analysis_1.toUnicodeSet)(node, options.flags);
if (set.accept.isEmpty) {
return NormalizedCharacter.fromChars(set.chars);
}
const alternatives = set.wordSets.map((wordSet) => {
return NormalizedAlternative.fromElements(wordSet.map(NormalizedCharacter.fromChars), node);
});
return NormalizedDisjunctions.fromAlternatives(alternatives, node);
}
case "Alternative":
return NormalizedAlternative.fromAlternative(node, options);
case "Quantifier":
return NormalizedOptional.fromQuantifier(node, options);
case "CapturingGroup":
case "Group":
case "Pattern":
return NormalizedDisjunctions.fromNode(node, options);
case "Assertion":
if (node.kind === "lookahead" || node.kind === "lookbehind") {
return NormalizedLookaroundAssertion.fromNode(node, options);
}
return NormalizedOther.fromNode(node);
case "RegExpLiteral":
return normalizeNode(node.pattern, options);
case "Backreference":
case "Flags":
case "ModifierFlags":
case "Modifiers":
return NormalizedOther.fromNode(node);
default:
return (0, util_1.assertNever)(node);
}
}
function isCoveredAnyNode(left, right, options) {
for (const e of left) {
if (isCoveredForNormalizedNode(e, right, options)) {
return true;
}
}
return false;
}
function isCoveredAltNodes(leftNodes, rightNodes, options) {
const left = options.canOmitRight ? omitEnds(leftNodes) : [...leftNodes];
const right = options.canOmitRight ? omitEnds(rightNodes) : [...rightNodes];
while (left.length && right.length) {
const le = left.shift();
const re = right.shift();
if (re.type === "NormalizedOptional") {
if (le.type === "NormalizedOptional") {
if (!isCoveredForNormalizedNode(le.element, re.element, options)) {
return false;
}
const decrementLe = le.decrementMax(re.max);
if (decrementLe) {
return isCoveredAltNodes([decrementLe, ...left], right, options);
}
const decrementRe = re.decrementMax(le.max);
if (decrementRe) {
return isCoveredAltNodes(left, [decrementRe, ...right], options);
}
}
else {
if (!isCoveredForNormalizedNode(le, re.element, options)) {
return false;
}
if (!isCoveredAltNodes([le, ...left], right, options)) {
return false;
}
const decrementRe = re.decrementMax();
if (decrementRe) {
return isCoveredAltNodes(left, [decrementRe, ...right], options);
}
}
}
else if (le.type === "NormalizedOptional") {
if (isCoveredAltNodes(left, [re, ...right], options)) {
return true;
}
if (!isCoveredForNormalizedNode(le.element, re, options)) {
return false;
}
const decrementLe = le.decrementMax();
if (decrementLe) {
if (isCoveredAltNodes([decrementLe, ...left], right, options)) {
return true;
}
}
}
else if (!isCoveredForNormalizedNode(le, re, options)) {
return false;
}
}
if (!options.canOmitRight) {
if (right.length) {
return false;
}
}
return !left.length;
}
function omitEnds(nodes) {
for (let index = nodes.length - 1; index >= 0; index--) {
const node = nodes[index];
if (node.type !== "NormalizedOptional") {
return nodes.slice(0, index + 1);
}
}
return [];
}

View file

@ -0,0 +1,4 @@
import type { Node } from "@eslint-community/regexpp/ast";
import type { ReadonlyFlags } from "regexp-ast-analysis";
import type { ShortCircuit } from "./common";
export declare function isEqualNodes<N extends Node>(a: N, b: N, flags: ReadonlyFlags, shortCircuit?: ShortCircuit): boolean;

View file

@ -0,0 +1,180 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEqualNodes = isEqualNodes;
const regexp_ast_analysis_1 = require("regexp-ast-analysis");
function isEqualChar(a, b, flags) {
if (a.type === "Character") {
if (b.type === "Character") {
if (a.value === b.value) {
return true;
}
}
else if (b.type === "CharacterSet") {
return false;
}
}
else if (a.type === "CharacterSet") {
if (b.type === "Character") {
return false;
}
else if (b.type === "CharacterSet") {
return a.raw === b.raw;
}
}
else if (a.type === "CharacterClassRange") {
if (b.type === "CharacterClassRange") {
return a.min.value === b.min.value && a.max.value === b.max.value;
}
}
if (a.raw === b.raw) {
return true;
}
return (0, regexp_ast_analysis_1.toUnicodeSet)(a, flags).equals((0, regexp_ast_analysis_1.toUnicodeSet)(b, flags));
}
const EQUALS_CHECKER = {
Alternative(a, b, flags, shortCircuit) {
return isEqualConcatenation(a.elements, b.elements, flags, shortCircuit);
},
Assertion(a, b, flags, shortCircuit) {
if (a.kind === "start" || a.kind === "end") {
return a.kind === b.kind;
}
if (a.kind === "word") {
return b.kind === "word" && a.negate === b.negate;
}
if (a.kind === "lookahead" || a.kind === "lookbehind") {
if (b.kind === a.kind && a.negate === b.negate) {
return isEqualSet(a.alternatives, b.alternatives, flags, shortCircuit);
}
return false;
}
return false;
},
Backreference(a, b) {
return a.ref === b.ref;
},
CapturingGroup(a, b, flags, shortCircuit) {
return (a.name === b.name &&
isEqualSet(a.alternatives, b.alternatives, flags, shortCircuit));
},
Character(a, b, flags) {
return isEqualChar(a, b, flags);
},
CharacterClass(a, b, flags) {
return isEqualChar(a, b, flags);
},
CharacterClassRange(a, b, flags) {
return isEqualChar(a, b, flags);
},
CharacterSet(a, b, flags) {
return isEqualChar(a, b, flags);
},
ClassIntersection(a, b, flags, shortCircuit) {
return isEqualSet([a.left, a.right], [b.left, b.right], flags, shortCircuit);
},
ClassStringDisjunction(a, b, flags, shortCircuit) {
return isEqualSet(a.alternatives, b.alternatives, flags, shortCircuit);
},
ClassSubtraction(a, b, flags, shortCircuit) {
return (isEqualNodes(a.left, b.left, flags, shortCircuit) &&
isEqualNodes(a.right, b.right, flags, shortCircuit));
},
ExpressionCharacterClass(a, b, flags) {
return (a.negate === b.negate &&
isEqualNodes(a.expression, b.expression, flags));
},
Flags(a, b) {
return (a.dotAll === b.dotAll &&
a.global === b.global &&
a.ignoreCase === b.ignoreCase &&
a.multiline === b.multiline &&
a.hasIndices === b.hasIndices &&
a.sticky === b.sticky &&
a.unicode === b.unicode &&
a.unicodeSets === b.unicodeSets);
},
Group(a, b, flags, shortCircuit) {
return isEqualSet(a.alternatives, b.alternatives, flags, shortCircuit);
},
ModifierFlags(a, b) {
return (a.dotAll === b.dotAll &&
a.ignoreCase === b.ignoreCase &&
a.multiline === b.multiline);
},
Modifiers(a, b, flags, shortCircuit) {
return (isEqualNodes(a.add, b.add, flags, shortCircuit) &&
((a.remove == null && b.remove == null) ||
(a.remove != null &&
b.remove != null &&
isEqualNodes(a.remove, b.remove, flags, shortCircuit))));
},
Pattern(a, b, flags, shortCircuit) {
return isEqualSet(a.alternatives, b.alternatives, flags, shortCircuit);
},
Quantifier(a, b, flags, shortCircuit) {
return (a.min === b.min &&
a.max === b.max &&
a.greedy === b.greedy &&
isEqualNodes(a.element, b.element, flags, shortCircuit));
},
RegExpLiteral(a, b, flags, shortCircuit) {
return (isEqualNodes(a.pattern, b.pattern, flags, shortCircuit) &&
isEqualNodes(a.flags, b.flags, flags, shortCircuit));
},
StringAlternative(a, b, flags, shortCircuit) {
return isEqualConcatenation(a.elements, b.elements, flags, shortCircuit);
},
};
function isToCharSetElement(node) {
return (node.type === "Character" ||
node.type === "CharacterClass" ||
node.type === "CharacterClassRange" ||
node.type === "CharacterSet");
}
function isEqualNodes(a, b, flags, shortCircuit) {
if (isToCharSetElement(a) && isToCharSetElement(b)) {
return isEqualChar(a, b, flags);
}
if (a.type !== b.type) {
return false;
}
if (shortCircuit) {
const kind = shortCircuit(a, b);
if (kind != null) {
return kind;
}
}
if (/[(*+?[\\{|]/u.test(a.raw) || /[(*+?[\\{|]/u.test(b.raw)) {
return EQUALS_CHECKER[a.type](a, b, flags, shortCircuit);
}
return a.raw === b.raw;
}
function isEqualConcatenation(a, b, flags, shortCircuit) {
if (a.length !== b.length) {
return false;
}
for (let index = 0; index < a.length; index++) {
const ae = a[index];
const be = b[index];
if (!isEqualNodes(ae, be, flags, shortCircuit)) {
return false;
}
}
return true;
}
function isEqualSet(a, b, flags, shortCircuit) {
if (a.length !== b.length) {
return false;
}
const beList = [...b];
for (const ae of a) {
const bIndex = beList.findIndex((be) => isEqualNodes(ae, be, flags, shortCircuit));
if (bIndex >= 0) {
beList.splice(bIndex, 1);
}
else {
return false;
}
}
return true;
}

View file

@ -0,0 +1,8 @@
import type { Quantifier } from "@eslint-community/regexpp/ast";
export declare function getQuantifierOffsets(qNode: Quantifier): [number, number];
export interface Quant {
min: number;
max: number;
greedy?: boolean;
}
export declare function quantToString(quant: Readonly<Quant>): string;

View file

@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getQuantifierOffsets = getQuantifierOffsets;
exports.quantToString = quantToString;
function getQuantifierOffsets(qNode) {
const startOffset = qNode.element.end - qNode.start;
const endOffset = qNode.raw.length - (qNode.greedy ? 0 : 1);
return [startOffset, endOffset];
}
function quantToString(quant) {
if (quant.max < quant.min ||
quant.min < 0 ||
!Number.isInteger(quant.min) ||
!(Number.isInteger(quant.max) || quant.max === Infinity)) {
throw new Error(`Invalid quantifier { min: ${quant.min}, max: ${quant.max} }`);
}
let value;
if (quant.min === 0 && quant.max === 1) {
value = "?";
}
else if (quant.min === 0 && quant.max === Infinity) {
value = "*";
}
else if (quant.min === 1 && quant.max === Infinity) {
value = "+";
}
else if (quant.min === quant.max) {
value = `{${quant.min}}`;
}
else if (quant.max === Infinity) {
value = `{${quant.min},}`;
}
else {
value = `{${quant.min},${quant.max}}`;
}
if (quant.greedy === false) {
return `${value}?`;
}
return value;
}

View file

@ -0,0 +1,12 @@
import type { Quantifier } from "@eslint-community/regexpp/ast";
import type { JS } from "refa";
import type { ReadonlyFlags } from "regexp-ast-analysis";
export type CanSimplify = {
readonly canSimplify: true;
readonly dependencies: Quantifier[];
};
export type CannotSimplify = {
readonly canSimplify: false;
};
export type SimplifyResult = CanSimplify | CannotSimplify;
export declare function canSimplifyQuantifier(quantifier: Quantifier, flags: ReadonlyFlags, parser: JS.Parser): SimplifyResult;

View file

@ -0,0 +1,245 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.canSimplifyQuantifier = canSimplifyQuantifier;
const refa_1 = require("refa");
const regexp_ast_analysis_1 = require("regexp-ast-analysis");
const util_1 = require("../util");
const containsAssertions = (0, util_1.cachedFn)((node) => {
return (0, regexp_ast_analysis_1.hasSomeDescendant)(node, (n) => n.type === "Assertion");
});
const cachedGetPossiblyConsumedChar = (0, util_1.cachedFn)((flags) => {
return (0, util_1.cachedFn)((element) => (0, regexp_ast_analysis_1.getConsumedChars)(element, flags));
});
const CANNOT_SIMPLIFY = { canSimplify: false };
function canSimplifyQuantifier(quantifier, flags, parser) {
if (quantifier.min === quantifier.max) {
return CANNOT_SIMPLIFY;
}
if ((0, regexp_ast_analysis_1.isZeroLength)(quantifier, flags)) {
return CANNOT_SIMPLIFY;
}
if (containsAssertions(quantifier)) {
return CANNOT_SIMPLIFY;
}
const direction = (0, regexp_ast_analysis_1.getMatchingDirection)(quantifier);
const preceding = getPrecedingQuantifiers(quantifier, direction, flags);
if (!preceding) {
return CANNOT_SIMPLIFY;
}
return canAbsorb(preceding, { direction, flags, parser, quantifier });
}
function canAbsorb(initialPreceding, options) {
const { direction, flags, parser, quantifier } = options;
const preceding = removeTargetQuantifier(initialPreceding, quantifier, direction, flags);
if (!preceding) {
return CANNOT_SIMPLIFY;
}
const dependencies = [...preceding];
const CAN_SIMPLIFY = {
canSimplify: true,
dependencies,
};
const fast = everyMaybe(preceding, (q) => canAbsorbElementFast(q, quantifier.element, flags));
if (typeof fast === "boolean") {
return fast ? CAN_SIMPLIFY : CANNOT_SIMPLIFY;
}
const formal = everyMaybe(fast, (q) => canAbsorbElementFormal(q, quantifier.element, parser));
if (typeof formal === "boolean") {
return formal ? CAN_SIMPLIFY : CANNOT_SIMPLIFY;
}
return formal.every((q) => {
const parts = splitQuantifierIntoTails(q, direction, flags);
if (!parts)
return false;
const result = canAbsorb(parts, options);
if (result.canSimplify)
dependencies.push(...result.dependencies);
return result.canSimplify;
})
? CAN_SIMPLIFY
: CANNOT_SIMPLIFY;
}
function everyMaybe(array, fn) {
const maybe = [];
for (const item of array) {
const result = fn(item);
if (result === false)
return false;
if (result === undefined)
maybe.push(item);
}
if (maybe.length === 0)
return true;
return maybe;
}
function canAbsorbElementFast(quantifier, element, flags) {
if (!quantifier.greedy) {
return false;
}
if (!isNonFinite(quantifier, flags)) {
return false;
}
const qChar = cachedGetPossiblyConsumedChar(flags)(quantifier.element);
const eChar = cachedGetPossiblyConsumedChar(flags)(element);
if (qChar.chars.isDisjointWith(eChar.chars)) {
return false;
}
if (eChar.exact && !eChar.chars.without(qChar.chars).isEmpty) {
return false;
}
if (containsAssertions(quantifier) || containsAssertions(element)) {
return undefined;
}
if (quantifier.element.type === "Character" ||
quantifier.element.type === "CharacterClass" ||
quantifier.element.type === "CharacterSet") {
if (quantifier.max !== Infinity) {
return false;
}
if (qChar.exact && qChar.chars.isSupersetOf(eChar.chars)) {
return true;
}
}
return undefined;
}
function isNonFinite(node, flags) {
return (0, regexp_ast_analysis_1.hasSomeDescendant)(node, (n) => n.type === "Quantifier" &&
n.max === Infinity &&
!(0, regexp_ast_analysis_1.isZeroLength)(n.element, flags), (n) => n.type !== "Assertion");
}
function toNfa(element, parser) {
const { expression, maxCharacter } = parser.parseElement(element, {
maxNodes: 1000,
assertions: "throw",
backreferences: "throw",
});
return refa_1.NFA.fromRegex(expression, { maxCharacter }, {}, new refa_1.NFA.LimitedNodeFactory(1000));
}
function canAbsorbElementFormal(quantifier, element, parser) {
if (containsAssertions(quantifier) || containsAssertions(element)) {
return undefined;
}
try {
const qNfa = toNfa(quantifier, parser);
const qDfa = refa_1.DFA.fromFA(qNfa, new refa_1.DFA.LimitedNodeFactory(1000));
const eNfa = toNfa(element, parser);
eNfa.quantify(0, 1);
qNfa.append(eNfa);
const qeDfa = refa_1.DFA.fromFA(qNfa, new refa_1.DFA.LimitedNodeFactory(1000));
qDfa.minimize();
qeDfa.minimize();
const equal = qDfa.structurallyEqual(qeDfa);
return equal;
}
catch (_a) {
}
return undefined;
}
function splitQuantifierIntoTails(quantifier, direction, flags) {
if ((0, regexp_ast_analysis_1.isPotentiallyZeroLength)(quantifier, flags)) {
return undefined;
}
return getTailQuantifiers(quantifier.element, direction, flags);
}
function removeTargetQuantifier(quantifiers, target, direction, flags) {
const result = [];
for (const q of quantifiers) {
if ((0, regexp_ast_analysis_1.hasSomeDescendant)(q, target)) {
const inner = splitQuantifierIntoTails(q, direction, flags);
if (inner === undefined) {
return undefined;
}
const mapped = removeTargetQuantifier(inner, target, direction, flags);
if (mapped === undefined) {
return undefined;
}
result.push(...mapped);
}
else {
result.push(q);
}
}
return result;
}
function unionQuantifiers(sets) {
const result = [];
for (const set of sets) {
if (set === undefined) {
return undefined;
}
result.push(...set);
}
if (result.length === 0)
return undefined;
return [...new Set(result)];
}
function getTailQuantifiers(element, direction, flags) {
switch (element.type) {
case "Assertion":
case "Backreference":
case "Character":
case "CharacterClass":
case "CharacterSet":
case "ExpressionCharacterClass":
return undefined;
case "Quantifier":
return [element];
case "Group":
case "CapturingGroup":
return unionQuantifiers(element.alternatives.map((a) => getTailQuantifiers(a, direction, flags)));
case "Alternative": {
const elements = direction === "ltr"
? (0, util_1.reversed)(element.elements)
: element.elements;
for (const e of elements) {
if ((0, regexp_ast_analysis_1.isEmpty)(e, flags))
continue;
if (e.type === "Quantifier") {
return [e];
}
return undefined;
}
const { parent } = element;
if (parent.type === "Pattern") {
return undefined;
}
if (parent.type === "Assertion") {
return undefined;
}
return getPrecedingQuantifiers(parent, direction, flags);
}
default:
return (0, util_1.assertNever)(element);
}
}
function getPrecedingQuantifiers(element, direction, flags) {
const parent = element.parent;
if (parent.type === "Quantifier") {
if (parent.max === 0) {
return undefined;
}
if (parent.max === 1) {
return getPrecedingQuantifiers(parent, direction, flags);
}
return unionQuantifiers([
getPrecedingQuantifiers(parent, direction, flags),
getTailQuantifiers(parent.element, direction, flags),
]);
}
if (parent.type !== "Alternative") {
return undefined;
}
const inc = direction === "ltr" ? -1 : +1;
const { elements } = parent;
const elementIndex = elements.indexOf(element);
for (let precedingIndex = elementIndex + inc; precedingIndex >= 0 && precedingIndex < elements.length; precedingIndex += inc) {
const preceding = parent.elements[precedingIndex];
if ((0, regexp_ast_analysis_1.isEmpty)(preceding, flags))
continue;
return getTailQuantifiers(preceding, direction, flags);
}
if (parent.parent.type === "Pattern") {
return undefined;
}
return getPrecedingQuantifiers(parent.parent, direction, flags);
}

View file

@ -0,0 +1,19 @@
type BaseReplacementElement<T> = BaseCharacterElement<T> | BaseDollarElement<T> | BaseReferenceElement<T>;
type BaseCharacterElement<T> = {
type: "CharacterElement";
value: string;
} & T;
type BaseDollarElement<T> = {
type: "DollarElement";
kind: "$" | "&" | "`" | "'";
} & T;
type BaseReferenceElement<T> = {
type: "ReferenceElement";
ref: number | string;
refText: string;
} & T;
export declare function parseReplacementsForString(text: string): BaseReplacementElement<object>[];
export declare function baseParseReplacements<T, E extends {
value: string;
}>(chars: E[], getData: (start: E, end: E) => T): BaseReplacementElement<T>[];
export {};

View file

@ -0,0 +1,95 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseReplacementsForString = parseReplacementsForString;
exports.baseParseReplacements = baseParseReplacements;
function parseReplacementsForString(text) {
return baseParseReplacements([...text].map((s) => ({ value: s })), () => ({}));
}
function baseParseReplacements(chars, getData) {
const elements = [];
let token;
let index = 0;
while ((token = chars[index++])) {
if (token.value === "$") {
const next = chars[index++];
if (next) {
if (next.value === "$" ||
next.value === "&" ||
next.value === "`" ||
next.value === "'") {
elements.push({
type: "DollarElement",
kind: next.value,
...getData(token, next),
});
continue;
}
if (parseNumberRef(token, next)) {
continue;
}
if (parseNamedRef(token, next)) {
continue;
}
index--;
}
}
elements.push({
type: "CharacterElement",
value: token.value,
...getData(token, token),
});
}
return elements;
function parseNumberRef(dollarToken, startToken) {
if (!/^\d$/u.test(startToken.value)) {
return false;
}
if (startToken.value === "0") {
const next = chars[index++];
if (next) {
if (/^[1-9]$/u.test(next.value)) {
const ref = Number(next.value);
elements.push({
type: "ReferenceElement",
ref,
refText: startToken.value + next.value,
...getData(dollarToken, next),
});
return true;
}
index--;
}
return false;
}
const ref = Number(startToken.value);
elements.push({
type: "ReferenceElement",
ref,
refText: startToken.value,
...getData(dollarToken, startToken),
});
return true;
}
function parseNamedRef(dollarToken, startToken) {
if (startToken.value !== "<") {
return false;
}
const startIndex = index;
let t;
while ((t = chars[index++])) {
if (t.value === ">") {
const refChars = chars.slice(startIndex, index - 1);
const ref = refChars.map((c) => c.value).join("");
elements.push({
type: "ReferenceElement",
ref,
refText: ref,
...getData(dollarToken, t),
});
return true;
}
}
index = startIndex;
return false;
}
}

View file

@ -0,0 +1,2 @@
export * from "./parser";
export * from "./tokens";

View file

@ -0,0 +1,18 @@
"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./parser"), exports);
__exportStar(require("./tokens"), exports);

View file

@ -0,0 +1,17 @@
import type { Token } from "./tokens";
export type StringLiteral = {
tokens: Token[];
value: string;
range: [number, number];
};
export type EcmaVersion = 3 | 5 | 6 | 2015 | 7 | 2016 | 8 | 2017 | 9 | 2018 | 10 | 2019 | 11 | 2020 | 12 | 2021;
export declare function parseStringLiteral(source: string, option?: {
start?: number;
end?: number;
ecmaVersion?: EcmaVersion;
}): StringLiteral;
export declare function parseStringTokens(source: string, option?: {
start?: number;
end?: number;
ecmaVersion?: EcmaVersion;
}): Generator<Token>;

View file

@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseStringLiteral = parseStringLiteral;
exports.parseStringTokens = parseStringTokens;
const tokenizer_1 = require("./tokenizer");
function parseStringLiteral(source, option) {
var _a, _b;
const startIndex = (_a = option === null || option === void 0 ? void 0 : option.start) !== null && _a !== void 0 ? _a : 0;
const cp = source.codePointAt(startIndex);
const ecmaVersion = (_b = option === null || option === void 0 ? void 0 : option.ecmaVersion) !== null && _b !== void 0 ? _b : Infinity;
const tokenizer = new tokenizer_1.Tokenizer(source, {
start: startIndex + 1,
end: option === null || option === void 0 ? void 0 : option.end,
ecmaVersion: ecmaVersion >= 6 && ecmaVersion < 2015
? ecmaVersion + 2009
: ecmaVersion,
});
const tokens = [...tokenizer.parseTokens(cp)];
return {
tokens,
get value() {
return tokens.map((t) => t.value).join("");
},
range: [startIndex, tokenizer.pos],
};
}
function* parseStringTokens(source, option) {
var _a, _b;
const startIndex = (_a = option === null || option === void 0 ? void 0 : option.start) !== null && _a !== void 0 ? _a : 0;
const ecmaVersion = (_b = option === null || option === void 0 ? void 0 : option.ecmaVersion) !== null && _b !== void 0 ? _b : Infinity;
const tokenizer = new tokenizer_1.Tokenizer(source, {
start: startIndex,
end: option === null || option === void 0 ? void 0 : option.end,
ecmaVersion: ecmaVersion >= 6 && ecmaVersion < 2015
? ecmaVersion + 2009
: ecmaVersion,
});
yield* tokenizer.parseTokens();
}

View file

@ -0,0 +1,16 @@
import type { Token } from "./tokens";
export declare class Tokenizer {
private readonly source;
pos: number;
private readonly end;
private readonly ecmaVersion;
constructor(source: string, options: {
start: number;
end?: number;
ecmaVersion: number;
});
parseTokens(quote?: number): Generator<Token>;
private readEscape;
private readUnicode;
private readHex;
}

View file

@ -0,0 +1,196 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Tokenizer = void 0;
const CP_BACK_SLASH = "\\".codePointAt(0);
const CP_BACKTICK = "`".codePointAt(0);
const CP_CR = "\r".codePointAt(0);
const CP_LF = "\n".codePointAt(0);
const CP_OPENING_BRACE = "{".codePointAt(0);
const CP_a = "a".codePointAt(0);
const CP_A = "A".codePointAt(0);
const CP_n = "n".codePointAt(0);
const CP_r = "r".codePointAt(0);
const CP_t = "t".codePointAt(0);
const CP_b = "b".codePointAt(0);
const CP_v = "v".codePointAt(0);
const CP_f = "f".codePointAt(0);
const CP_u = "u".codePointAt(0);
const CP_x = "x".codePointAt(0);
const CP_0 = "0".codePointAt(0);
const CP_7 = "7".codePointAt(0);
const CP_8 = "8".codePointAt(0);
const CP_9 = "9".codePointAt(0);
class Tokenizer {
constructor(source, options) {
var _a;
this.source = source;
this.pos = options.start;
this.end = (_a = options.end) !== null && _a !== void 0 ? _a : null;
this.ecmaVersion = options.ecmaVersion;
}
*parseTokens(quote) {
var _a;
const inTemplate = quote === CP_BACKTICK;
const endIndex = (_a = this.end) !== null && _a !== void 0 ? _a : this.source.length;
while (this.pos < endIndex) {
const start = this.pos;
const cp = this.source.codePointAt(start);
if (cp == null) {
throw new Error("Unterminated string constant");
}
this.pos = inc(start, cp);
if (cp === quote)
break;
if (cp === CP_BACK_SLASH) {
const { value, kind } = this.readEscape(inTemplate);
yield {
type: "EscapeToken",
kind,
value,
range: [start, this.pos],
};
}
else if (cp === CP_CR || cp === CP_LF) {
if (inTemplate) {
if (cp === CP_CR &&
this.source.codePointAt(this.pos) === CP_LF) {
this.pos++;
}
yield {
type: "CharacterToken",
value: "\n",
range: [start, this.pos],
};
}
else {
throw new Error("Unterminated string constant");
}
}
else {
if (this.ecmaVersion >= 2019 &&
(cp === 0x2028 || cp === 0x2029) &&
!inTemplate) {
throw new Error("Unterminated string constant");
}
yield {
type: "CharacterToken",
value: String.fromCodePoint(cp),
range: [start, this.pos],
};
}
}
}
readEscape(inTemplate) {
const cp = this.source.codePointAt(this.pos);
if (cp == null) {
throw new Error("Invalid or unexpected token");
}
this.pos = inc(this.pos, cp);
switch (cp) {
case CP_n:
return { value: "\n", kind: "special" };
case CP_r:
return { value: "\r", kind: "special" };
case CP_t:
return { value: "\t", kind: "special" };
case CP_b:
return { value: "\b", kind: "special" };
case CP_v:
return { value: "\v", kind: "special" };
case CP_f:
return { value: "\f", kind: "special" };
case CP_CR:
if (this.source.codePointAt(this.pos) === CP_LF) {
this.pos++;
}
case CP_LF:
return { value: "", kind: "eol" };
case CP_x:
return {
value: String.fromCodePoint(this.readHex(2)),
kind: "hex",
};
case CP_u:
return {
value: String.fromCodePoint(this.readUnicode()),
kind: "unicode",
};
default:
if (CP_0 <= cp && cp <= CP_7) {
let octalStr = /^[0-7]+/u.exec(this.source.slice(this.pos - 1, this.pos + 2))[0];
let octal = parseInt(octalStr, 8);
if (octal > 255) {
octalStr = octalStr.slice(0, -1);
octal = parseInt(octalStr, 8);
}
this.pos += octalStr.length - 1;
const nextCp = this.source.codePointAt(this.pos);
if ((octalStr !== "0" ||
nextCp === CP_8 ||
nextCp === CP_9) &&
inTemplate) {
throw new Error("Octal literal in template string");
}
return {
value: String.fromCodePoint(octal),
kind: "octal",
};
}
return {
value: String.fromCodePoint(cp),
kind: "char",
};
}
}
readUnicode() {
const cp = this.source.codePointAt(this.pos);
if (cp === CP_OPENING_BRACE) {
if (this.ecmaVersion < 2015) {
throw new Error(`Unexpected character '${String.fromCodePoint(cp)}'`);
}
this.pos++;
const endIndex = this.source.indexOf("}", this.pos);
if (endIndex < 0) {
throw new Error("Invalid Unicode escape sequence");
}
const code = this.readHex(endIndex - this.pos);
this.pos++;
if (code > 0x10ffff) {
throw new Error("Code point out of bounds");
}
return code;
}
return this.readHex(4);
}
readHex(length) {
let total = 0;
for (let i = 0; i < length; i++, this.pos++) {
const cp = this.source.codePointAt(this.pos);
if (cp == null) {
throw new Error(`Invalid hexadecimal escape sequence`);
}
let val;
if (CP_a <= cp) {
val = cp - CP_a + 10;
}
else if (CP_A <= cp) {
val = cp - CP_A + 10;
}
else if (CP_0 <= cp && cp <= CP_9) {
val = cp - CP_0;
}
else {
throw new Error(`Invalid hexadecimal escape sequence`);
}
if (val >= 16) {
throw new Error(`Invalid hexadecimal escape sequence`);
}
total = total * 16 + val;
}
return total;
}
}
exports.Tokenizer = Tokenizer;
function inc(pos, cp) {
return pos + (cp >= 0x10000 ? 2 : 1);
}

View file

@ -0,0 +1,13 @@
export interface BaseToken {
type: string;
value: string;
range: [number, number];
}
export type Token = CharacterToken | EscapeToken;
export interface CharacterToken extends BaseToken {
type: "CharacterToken";
}
export interface EscapeToken extends BaseToken {
type: "EscapeToken";
kind: "special" | "eol" | "unicode" | "hex" | "octal" | "char";
}

View file

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,24 @@
import type { Rule } from "eslint";
import type * as TS from "typescript";
type TypeScript = typeof import("typescript");
export declare function getTypeScriptTools(context: Rule.RuleContext): {
tsNodeMap: ReadonlyMap<unknown, TS.Node>;
checker: TS.TypeChecker | null;
usedTS: boolean;
hasFullTypeInformation: boolean;
};
export declare function getTypeScript(): TypeScript | undefined;
export declare function isArrayLikeObject(tsType: TS.Type): boolean;
export declare function isClassOrInterface(tsType: TS.Type): tsType is TS.InterfaceType;
export declare function isObject(tsType: TS.Type): tsType is TS.ObjectType;
export declare function isReferenceObject(tsType: TS.Type): tsType is TS.TypeReference;
export declare function isUnionOrIntersection(tsType: TS.Type): tsType is TS.UnionOrIntersectionType;
export declare function isTypeParameter(tsType: TS.Type): tsType is TS.UnionOrIntersectionType;
export declare function isAny(tsType: TS.Type): boolean;
export declare function isUnknown(tsType: TS.Type): boolean;
export declare function isStringLine(tsType: TS.Type): boolean;
export declare function isNumberLike(tsType: TS.Type): boolean;
export declare function isBooleanLike(tsType: TS.Type): boolean;
export declare function isBigIntLike(tsType: TS.Type): boolean;
export declare function isNull(tsType: TS.Type): boolean;
export {};

View file

@ -0,0 +1,114 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTypeScriptTools = getTypeScriptTools;
exports.getTypeScript = getTypeScript;
exports.isArrayLikeObject = isArrayLikeObject;
exports.isClassOrInterface = isClassOrInterface;
exports.isObject = isObject;
exports.isReferenceObject = isReferenceObject;
exports.isUnionOrIntersection = isUnionOrIntersection;
exports.isTypeParameter = isTypeParameter;
exports.isAny = isAny;
exports.isUnknown = isUnknown;
exports.isStringLine = isStringLine;
exports.isNumberLike = isNumberLike;
exports.isBooleanLike = isBooleanLike;
exports.isBigIntLike = isBigIntLike;
exports.isNull = isNull;
function getTypeScriptTools(context) {
const sourceCode = context.sourceCode;
const ts = getTypeScript();
const tsNodeMap = sourceCode.parserServices.esTreeNodeToTSNodeMap;
const usedTS = Boolean(ts && tsNodeMap);
const hasFullTypeInformation = usedTS && sourceCode.parserServices.hasFullTypeInformation !== false;
const checker = (hasFullTypeInformation &&
sourceCode.parserServices.program &&
sourceCode.parserServices.program.getTypeChecker()) ||
null;
return {
tsNodeMap: tsNodeMap || new Map(),
checker,
usedTS,
hasFullTypeInformation,
};
}
let cacheTypeScript;
function getTypeScript() {
try {
return (cacheTypeScript !== null && cacheTypeScript !== void 0 ? cacheTypeScript : (cacheTypeScript = require("typescript")));
}
catch (e) {
if (e.code === "MODULE_NOT_FOUND") {
return undefined;
}
if (typeof require === "undefined" ||
typeof require.define === "function") {
return undefined;
}
if (typeof e.message === "string" &&
e.message.includes("Dynamic require") &&
e.message.includes("is not supported")) {
return undefined;
}
throw e;
}
}
function isArrayLikeObject(tsType) {
const ts = getTypeScript();
return (isObject(tsType) &&
(tsType.objectFlags &
(ts.ObjectFlags.ArrayLiteral |
ts.ObjectFlags.EvolvingArray |
ts.ObjectFlags.Tuple)) !==
0);
}
function isClassOrInterface(tsType) {
const ts = getTypeScript();
return (isObject(tsType) &&
(tsType.objectFlags & ts.ObjectFlags.ClassOrInterface) !== 0);
}
function isObject(tsType) {
const ts = getTypeScript();
return (tsType.flags & ts.TypeFlags.Object) !== 0;
}
function isReferenceObject(tsType) {
const ts = getTypeScript();
return (isObject(tsType) &&
(tsType.objectFlags & ts.ObjectFlags.Reference) !== 0);
}
function isUnionOrIntersection(tsType) {
const ts = getTypeScript();
return (tsType.flags & ts.TypeFlags.UnionOrIntersection) !== 0;
}
function isTypeParameter(tsType) {
const ts = getTypeScript();
return (tsType.flags & ts.TypeFlags.TypeParameter) !== 0;
}
function isAny(tsType) {
const ts = getTypeScript();
return (tsType.flags & ts.TypeFlags.Any) !== 0;
}
function isUnknown(tsType) {
const ts = getTypeScript();
return (tsType.flags & ts.TypeFlags.Unknown) !== 0;
}
function isStringLine(tsType) {
const ts = getTypeScript();
return (tsType.flags & ts.TypeFlags.StringLike) !== 0;
}
function isNumberLike(tsType) {
const ts = getTypeScript();
return (tsType.flags & ts.TypeFlags.NumberLike) !== 0;
}
function isBooleanLike(tsType) {
const ts = getTypeScript();
return (tsType.flags & ts.TypeFlags.BooleanLike) !== 0;
}
function isBigIntLike(tsType) {
const ts = getTypeScript();
return (tsType.flags & ts.TypeFlags.BigIntLike) !== 0;
}
function isNull(tsType) {
const ts = getTypeScript();
return (tsType.flags & ts.TypeFlags.Null) !== 0;
}

View file

@ -0,0 +1 @@
export { type TypeTracker, createTypeTracker } from "./tracker";

View file

@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTypeTracker = void 0;
var tracker_1 = require("./tracker");
Object.defineProperty(exports, "createTypeTracker", { enumerable: true, get: function () { return tracker_1.createTypeTracker; } });

View file

@ -0,0 +1,36 @@
import type { Rule } from "eslint";
import type * as ES from "estree";
import type { RootResult } from "jsdoc-type-pratt-parser";
import * as commentParser from "comment-parser";
import type { Spec } from "comment-parser";
type ParsedComment = ReturnType<typeof commentParser.parse>[number];
export declare class JSDocParams {
private readonly params;
isEmpty(): boolean;
add(paths: string[], param: Spec): void;
get(paths: {
name: string | null;
index: number | null;
}[]): Spec | null;
}
export declare class JSDocParam extends JSDocParams {
readonly name: string | null;
readonly param: Spec;
constructor(name: string | null, param: Spec);
}
declare const TAGS: {
param: string[];
returns: string[];
type: string[];
};
export declare class JSDoc {
private readonly parsed;
private params;
constructor(parsed: ParsedComment);
getTag(name: keyof typeof TAGS): Spec | null;
parseParams(): JSDocParams;
private genTags;
}
export declare function getJSDoc(node: ES.Expression | ES.VariableDeclarator | ES.FunctionDeclaration | ES.PrivateIdentifier, context: Rule.RuleContext): JSDoc | null;
export declare function parseTypeText(text: string): RootResult | null;
export {};

View file

@ -0,0 +1,186 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.JSDoc = exports.JSDocParam = exports.JSDocParams = void 0;
exports.getJSDoc = getJSDoc;
exports.parseTypeText = parseTypeText;
const jsdocTypeParser = __importStar(require("jsdoc-type-pratt-parser"));
const eslint_utils_1 = require("@eslint-community/eslint-utils");
const commentParser = __importStar(require("comment-parser"));
class JSDocParams {
constructor() {
this.params = [];
}
isEmpty() {
return this.params.length === 0;
}
add(paths, param) {
const name = paths.shift();
if (paths.length > 0) {
for (const rootParam of this.params) {
if (rootParam.name === name) {
rootParam.add(paths, param);
return;
}
}
}
this.params.push(new JSDocParam(name || null, param));
}
get(paths) {
const { name, index } = paths.shift();
if (name) {
for (const param of this.params) {
if (param.name === name) {
return paths.length ? param.get(paths) : param.param;
}
}
}
if (index != null) {
const param = this.params[index];
if (param) {
return paths.length ? param.get(paths) : param.param;
}
}
return null;
}
}
exports.JSDocParams = JSDocParams;
class JSDocParam extends JSDocParams {
constructor(name, param) {
super();
this.name = name;
this.param = param;
}
}
exports.JSDocParam = JSDocParam;
const TAGS = {
param: ["param", "arg", "argument"],
returns: ["returns", "return"],
type: ["type"],
};
class JSDoc {
constructor(parsed) {
this.params = null;
this.parsed = parsed;
}
getTag(name) {
for (const tag of this.genTags(name)) {
return tag;
}
return null;
}
parseParams() {
if (this.params) {
return this.params;
}
const params = (this.params = new JSDocParams());
for (const param of this.genTags("param")) {
const paths = (param.name || "").split(/\./u);
params.add(paths, param);
}
return params;
}
*genTags(name) {
const names = TAGS[name];
for (const tag of this.parsed.tags) {
if (names.includes(tag.tag)) {
yield tag;
}
}
}
}
exports.JSDoc = JSDoc;
function getJSDoc(node, context) {
const sourceCode = context.sourceCode;
const jsdoc = findJSDocComment(node, sourceCode);
if (jsdoc) {
try {
const parsed = commentParser.parse(`/*${jsdoc.value}*/`)[0];
return new JSDoc(parsed);
}
catch (_a) {
}
}
return null;
}
function findJSDocComment(node, sourceCode) {
let target = node;
let tokenBefore = null;
while (target) {
tokenBefore = sourceCode.getTokenBefore(target, {
includeComments: true,
});
if (!tokenBefore) {
return null;
}
if (tokenBefore.type === "Keyword" &&
target.type === "VariableDeclarator") {
if (tokenBefore.value === "const" ||
tokenBefore.value === "let" ||
tokenBefore.value === "var") {
target = tokenBefore;
continue;
}
}
if (tokenBefore.type === "Punctuator") {
if (tokenBefore.value === "(") {
target = tokenBefore;
continue;
}
}
if ((0, eslint_utils_1.isCommentToken)(tokenBefore)) {
if (tokenBefore.type === "Line") {
target = tokenBefore;
continue;
}
}
break;
}
if (tokenBefore &&
tokenBefore.type === "Block" &&
tokenBefore.value.startsWith("*")) {
return tokenBefore;
}
return null;
}
function parseTypeText(text) {
try {
const result = jsdocTypeParser.tryParse(text);
return result;
}
catch (_a) {
return null;
}
}

View file

@ -0,0 +1,9 @@
import type { Rule } from "eslint";
import type * as ES from "estree";
export type TypeTracker = {
isString: (node: ES.Expression) => boolean;
maybeString: (node: ES.Expression) => boolean;
isRegExp: (node: ES.Expression) => boolean;
getTypes: (node: ES.Expression) => string[];
};
export declare function createTypeTracker(context: Rule.RuleContext): TypeTracker;

View file

@ -0,0 +1,663 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTypeTracker = createTypeTracker;
const ast_utils_1 = require("../ast-utils");
const ts_util_1 = require("../ts-util");
const util_1 = require("../util");
const jsdoc_1 = require("./jsdoc");
const type_data_1 = require("./type-data");
const iterable_1 = require("./type-data/iterable");
const utils_1 = require("./utils");
const ts = (0, ts_util_1.getTypeScript)();
const cacheTypeTracker = new WeakMap();
function createTypeTracker(context) {
const programNode = context.sourceCode.ast;
const cache = cacheTypeTracker.get(programNode);
if (cache) {
return cache;
}
const { tsNodeMap, checker, usedTS } = (0, ts_util_1.getTypeScriptTools)(context);
const cacheTypeInfo = new WeakMap();
const tracker = {
isString,
maybeString,
isRegExp,
getTypes,
};
cacheTypeTracker.set(programNode, tracker);
return tracker;
function isString(node) {
return (0, type_data_1.hasType)(getType(node), "String");
}
function maybeString(node) {
if (isString(node)) {
return true;
}
if (usedTS) {
return false;
}
return getType(node) == null;
}
function isRegExp(node) {
return (0, type_data_1.hasType)(getType(node), "RegExp");
}
function getTypes(node) {
const result = getType(node);
if (result == null) {
return [];
}
if (typeof result === "string") {
return [result];
}
return result.typeNames();
}
function getType(node) {
var _a;
if (cacheTypeInfo.has(node)) {
return (_a = cacheTypeInfo.get(node)) !== null && _a !== void 0 ? _a : null;
}
cacheTypeInfo.set(node, null);
try {
const type = getTypeWithoutCache(node);
cacheTypeInfo.set(node, type);
return type;
}
catch (_b) {
return null;
}
}
function getTypeWithoutCache(node) {
var _a, _b, _c, _d, _e, _f;
if (node.type === "Literal") {
if (typeof node.value === "string") {
return type_data_1.STRING;
}
if (typeof node.value === "boolean") {
return type_data_1.BOOLEAN;
}
if (typeof node.value === "number") {
return type_data_1.NUMBER;
}
if ("regex" in node && node.regex) {
return type_data_1.REGEXP;
}
if ("bigint" in node && node.bigint) {
return type_data_1.BIGINT;
}
if (node.value == null) {
return "null";
}
}
else if (node.type === "TemplateLiteral") {
return type_data_1.STRING;
}
if (usedTS) {
return getTypeByTs(node);
}
const jsdoc = (0, jsdoc_1.getJSDoc)(node, context);
if (jsdoc) {
if ((0, utils_1.isParenthesized)(context, node)) {
const type = typeTextToTypeInfo((_a = jsdoc.getTag("type")) === null || _a === void 0 ? void 0 : _a.type);
if (type) {
return type;
}
}
}
if (node.type === "ArrowFunctionExpression" ||
node.type === "FunctionExpression") {
if (jsdoc) {
const type = typeTextToTypeInfo((_b = jsdoc.getTag("returns")) === null || _b === void 0 ? void 0 : _b.type);
if (type) {
return new type_data_1.TypeFunction(() => type);
}
}
}
if (node.type === "FunctionExpression") {
return type_data_1.UNKNOWN_FUNCTION;
}
if (node.type === "ArrowFunctionExpression") {
if (node.body.type !== "BlockStatement") {
const body = node.body;
return new type_data_1.TypeFunction(() => getType(body));
}
return type_data_1.UNKNOWN_FUNCTION;
}
if (node.type === "ArrayExpression") {
return new type_data_1.TypeArray(function* () {
for (const element of node.elements) {
if (!element) {
yield null;
}
else if (element.type !== "SpreadElement") {
yield getType(element);
}
else {
const argType = getType(element.argument);
if ((0, type_data_1.isTypeClass)(argType)) {
yield argType.iterateType();
}
else {
yield null;
}
}
}
}, node.elements.every((e) => e && e.type !== "SpreadElement"));
}
else if (node.type === "ObjectExpression") {
return new type_data_1.TypeObject(function* () {
for (let index = node.properties.length - 1; index >= 0; index--) {
const property = node.properties[index];
if (property.type !== "SpreadElement") {
if (property.value.type === "ObjectPattern" ||
property.value.type === "ArrayPattern" ||
property.value.type === "RestElement" ||
property.value.type === "AssignmentPattern")
continue;
const name = (0, utils_1.getPropertyName)(context, property);
if (name != null) {
const value = property.value;
yield [name, () => getType(value)];
}
}
else {
const spreadType = getType(property.argument);
if ((0, type_data_1.isTypeClass)(spreadType) &&
spreadType.type === "Object") {
yield* spreadType.allProperties();
}
}
}
});
}
else if (node.type === "BinaryExpression") {
const type = type_data_1.BI_OPERATOR_TYPES[node.operator];
if (type) {
return type(() => [getType(node.left), getType(node.right)]);
}
}
else if (node.type === "UnaryExpression") {
const type = type_data_1.UN_OPERATOR_TYPES[node.operator];
if (type) {
return type(() => getType(node.argument));
}
}
else if (node.type === "AssignmentExpression") {
return getType(node.right);
}
else if (node.type === "SequenceExpression") {
return getType(node.expressions[node.expressions.length - 1]);
}
else if (node.type === "ChainExpression") {
return getType(node.expression);
}
else if (node.type === "ClassExpression") {
return null;
}
else if (node.type === "Identifier") {
const variable = (0, utils_1.findVariable)(context, node);
if (variable) {
if (variable.defs.length === 1) {
const def = variable.defs[0];
if (def.type === "Variable") {
const idJsdoc = (0, jsdoc_1.getJSDoc)(def.node, context);
if (idJsdoc) {
const type = typeTextToTypeInfo((_c = idJsdoc.getTag("type")) === null || _c === void 0 ? void 0 : _c.type);
if (type) {
return type;
}
const returnType = typeTextToTypeInfo((_d = idJsdoc.getTag("returns")) === null || _d === void 0 ? void 0 : _d.type);
if (returnType) {
return new type_data_1.TypeFunction(() => returnType);
}
}
if (def.parent.kind === "const") {
if (def.node.init) {
const type = getType(def.node.init);
if (type) {
return type;
}
}
else {
const parent = (0, ast_utils_1.getParent)(def.parent);
if (parent) {
if ((parent === null || parent === void 0 ? void 0 : parent.type) === "ForOfStatement") {
const rightType = getType(parent.right);
if ((0, type_data_1.isTypeClass)(rightType)) {
const type = rightType.iterateType();
if (type) {
return type;
}
}
}
else if ((parent === null || parent === void 0 ? void 0 : parent.type) === "ForInStatement") {
return type_data_1.STRING;
}
}
}
}
}
else if (def.type === "Parameter") {
const fnJsdoc = (0, jsdoc_1.getJSDoc)(def.node, context);
if (fnJsdoc) {
const jsdocParams = fnJsdoc.parseParams();
if (!jsdocParams.isEmpty()) {
const type = typeTextToTypeInfo((_e = jsdocParams.get(getParamPath(def.name.name, def.name, context))) === null || _e === void 0 ? void 0 : _e.type);
if (type) {
return type;
}
}
}
const parent = (0, ast_utils_1.getParent)(def.name);
if (parent) {
if (parent.type === "RestElement") {
const pp = (0, ast_utils_1.getParent)(parent);
if (pp) {
if (pp.type === "ArrayPattern") {
return type_data_1.UNKNOWN_ARRAY;
}
if (pp.type === "ObjectPattern") {
return type_data_1.UNKNOWN_OBJECT;
}
if (pp.type === "FunctionExpression" ||
pp.type === "FunctionDeclaration" ||
pp.type === "ArrowFunctionExpression") {
return type_data_1.UNKNOWN_ARRAY;
}
}
}
else if (parent.type === "AssignmentPattern") {
return getType(parent.right);
}
}
}
else if (def.type === "FunctionName") {
const fnJsdoc = (0, jsdoc_1.getJSDoc)(def.node, context);
if (fnJsdoc) {
const type = typeTextToTypeInfo((_f = fnJsdoc.getTag("returns")) === null || _f === void 0 ? void 0 : _f.type);
if (type) {
return new type_data_1.TypeFunction(() => type);
}
}
return type_data_1.UNKNOWN_FUNCTION;
}
}
else if (variable.defs.length === 0) {
const type = type_data_1.GLOBAL.propertyType(node.name);
if (type) {
return type;
}
}
}
}
else if (node.type === "NewExpression") {
if (node.callee.type !== "Super") {
const type = getType(node.callee);
if ((0, type_data_1.isTypeClass)(type)) {
const argTypes = [];
for (const arg of node.arguments) {
argTypes.push(arg.type === "SpreadElement"
? null
: () => {
return getType(arg);
});
}
return type.returnType(null, argTypes, {
isConstructor: true,
});
}
}
}
else if (node.type === "CallExpression" ||
node.type === "TaggedTemplateExpression") {
const argTypes = [];
if (node.type === "CallExpression") {
for (const arg of node.arguments) {
argTypes.push(arg.type === "SpreadElement"
? null
: () => {
return getType(arg);
});
}
}
const callee = node.type === "CallExpression" ? node.callee : node.tag;
if (callee.type === "MemberExpression") {
const mem = callee;
if (mem.object.type !== "Super") {
let propertyName = null;
if (!mem.computed) {
if (mem.property.type === "Identifier") {
propertyName = mem.property.name;
}
}
else {
const propertyType = getType(mem.property);
if ((0, type_data_1.hasType)(propertyType, "Number")) {
propertyName = "0";
}
}
if (propertyName != null) {
if (propertyName === "toString" ||
propertyName === "toLocaleString") {
return type_data_1.STRING;
}
const objectType = getType(mem.object);
if ((0, type_data_1.isTypeClass)(objectType)) {
const type = objectType.propertyType(propertyName);
if ((0, type_data_1.isTypeClass)(type)) {
return type.returnType(() => objectType, argTypes);
}
}
}
}
}
else if (callee.type !== "Super") {
const type = getType(callee);
if ((0, type_data_1.isTypeClass)(type)) {
return type.returnType(null, argTypes);
}
}
}
else if (node.type === "MemberExpression") {
if (node.object.type !== "Super") {
let propertyName = null;
if (!node.computed) {
if (node.property.type === "Identifier") {
propertyName = node.property.name;
}
}
else {
const propertyType = getType(node.property);
if ((0, type_data_1.hasType)(propertyType, "Number")) {
propertyName = "0";
}
}
if (propertyName != null) {
const objectType = getType(node.object);
if ((0, type_data_1.isTypeClass)(objectType)) {
const type = objectType.propertyType(propertyName);
if (type) {
return type;
}
}
}
}
}
return usedTS ? getTypeByTs(node) : null;
}
function getTypeByTs(node) {
const tsNode = tsNodeMap.get(node);
const tsType = (tsNode && (checker === null || checker === void 0 ? void 0 : checker.getTypeAtLocation(tsNode))) || null;
return tsType && getTypeFromTsType(tsType);
}
function getTypeFromTsType(tsType) {
var _a, _b;
if ((0, ts_util_1.isStringLine)(tsType)) {
return type_data_1.STRING;
}
if ((0, ts_util_1.isNumberLike)(tsType)) {
return type_data_1.NUMBER;
}
if ((0, ts_util_1.isBooleanLike)(tsType)) {
return type_data_1.BOOLEAN;
}
if ((0, ts_util_1.isBigIntLike)(tsType)) {
return type_data_1.BIGINT;
}
if ((0, ts_util_1.isAny)(tsType) || (0, ts_util_1.isUnknown)(tsType)) {
return null;
}
if ((0, ts_util_1.isArrayLikeObject)(tsType)) {
return type_data_1.UNKNOWN_ARRAY;
}
if ((0, ts_util_1.isReferenceObject)(tsType) && tsType.target !== tsType) {
return getTypeFromTsType(tsType.target);
}
if ((0, ts_util_1.isTypeParameter)(tsType)) {
const constraintType = getConstraintType(tsType);
if (constraintType) {
return getTypeFromTsType(constraintType);
}
return null;
}
if ((0, ts_util_1.isUnionOrIntersection)(tsType)) {
return type_data_1.TypeUnionOrIntersection.buildType(function* () {
for (const t of tsType.types) {
const tn = getTypeFromTsType(t);
if (tn) {
yield tn;
}
}
});
}
if ((0, ts_util_1.isClassOrInterface)(tsType)) {
const name = tsType.symbol.escapedName;
const typeName = (_b = (_a = /^Readonly(?<typeName>.*)/u.exec(name)) === null || _a === void 0 ? void 0 : _a.groups.typeName) !== null && _b !== void 0 ? _b : name;
return typeName === "Array" ? type_data_1.UNKNOWN_ARRAY : typeName;
}
if ((0, ts_util_1.isObject)(tsType)) {
return type_data_1.UNKNOWN_OBJECT;
}
return checker ? checker.typeToString(tsType) : null;
}
function getConstraintType(tsType) {
const symbol = tsType.symbol;
const declarations = symbol && symbol.declarations;
const declaration = declarations && declarations[0];
if (declaration &&
ts.isTypeParameterDeclaration(declaration) &&
declaration.constraint != null) {
return checker === null || checker === void 0 ? void 0 : checker.getTypeFromTypeNode(declaration.constraint);
}
return undefined;
}
}
function typeTextToTypeInfo(typeText) {
if (typeText == null) {
return null;
}
return jsDocTypeNodeToTypeInfo((0, jsdoc_1.parseTypeText)(typeText));
}
function jsDocTypeNodeToTypeInfo(node) {
if (node == null) {
return null;
}
if (node.type === "JsdocTypeName") {
return typeNameToTypeInfo(node.value);
}
if (node.type === "JsdocTypeStringValue") {
return type_data_1.STRING;
}
if (node.type === "JsdocTypeNumber") {
return type_data_1.NUMBER;
}
if (node.type === "JsdocTypeAsserts") {
return type_data_1.BOOLEAN;
}
if (node.type === "JsdocTypeOptional" ||
node.type === "JsdocTypeNullable" ||
node.type === "JsdocTypeNotNullable" ||
node.type === "JsdocTypeParenthesis") {
return jsDocTypeNodeToTypeInfo(node.element);
}
if (node.type === "JsdocTypeVariadic") {
return new type_data_1.TypeArray(function* () {
if (node.element) {
yield jsDocTypeNodeToTypeInfo(node.element);
}
else {
yield null;
}
});
}
if (node.type === "JsdocTypeUnion" ||
node.type === "JsdocTypeIntersection") {
return type_data_1.TypeUnionOrIntersection.buildType(function* () {
for (const e of node.elements) {
yield jsDocTypeNodeToTypeInfo(e);
}
});
}
if (node.type === "JsdocTypeGeneric") {
const subject = jsDocTypeNodeToTypeInfo(node.left);
if ((0, type_data_1.hasType)(subject, "Array")) {
return new type_data_1.TypeArray(function* () {
yield jsDocTypeNodeToTypeInfo(node.elements[0]);
});
}
if ((0, type_data_1.hasType)(subject, "Map")) {
return new type_data_1.TypeMap(() => jsDocTypeNodeToTypeInfo(node.elements[0]), () => jsDocTypeNodeToTypeInfo(node.elements[1]));
}
if ((0, type_data_1.hasType)(subject, "Set")) {
return new type_data_1.TypeSet(() => jsDocTypeNodeToTypeInfo(node.elements[0]));
}
if (subject === iterable_1.UNKNOWN_ITERABLE) {
return new iterable_1.TypeIterable(() => jsDocTypeNodeToTypeInfo(node.elements[0]));
}
return subject;
}
if (node.type === "JsdocTypeObject") {
return new type_data_1.TypeObject(function* () {
for (const element of node.elements) {
if (element.type === "JsdocTypeObjectField") {
if (typeof element.key !== "string") {
continue;
}
yield [
element.key,
() => element.right
? jsDocTypeNodeToTypeInfo(element.right)
: null,
];
}
else if (element.type === "JsdocTypeJsdocObjectField") {
if (element.left.type === "JsdocTypeNullable" &&
element.left.element.type === "JsdocTypeName") {
yield [
element.left.element.value,
() => element.right
? jsDocTypeNodeToTypeInfo(element.right)
: null,
];
}
}
}
});
}
if (node.type === "JsdocTypeTuple") {
if (node.elements[0].type === "JsdocTypeKeyValue") {
const elements = node.elements;
return new type_data_1.TypeArray(function* () {
for (const element of elements) {
if (element.right) {
yield jsDocTypeNodeToTypeInfo(element.right);
}
}
});
}
const elements = node.elements;
return new type_data_1.TypeArray(function* () {
for (const element of elements) {
yield jsDocTypeNodeToTypeInfo(element);
}
});
}
if (node.type === "JsdocTypeFunction") {
if (node.returnType) {
const returnType = node.returnType;
return new type_data_1.TypeFunction(() => jsDocTypeNodeToTypeInfo(returnType));
}
return type_data_1.UNKNOWN_FUNCTION;
}
if (node.type === "JsdocTypeTypeof") {
return new type_data_1.TypeFunction(() => jsDocTypeNodeToTypeInfo(node.element));
}
if (node.type === "JsdocTypeAny" ||
node.type === "JsdocTypeUnknown" ||
node.type === "JsdocTypeNull" ||
node.type === "JsdocTypeUndefined") {
return null;
}
if (node.type === "JsdocTypeImport" ||
node.type === "JsdocTypeKeyof" ||
node.type === "JsdocTypeNamePath" ||
node.type === "JsdocTypePredicate" ||
node.type === "JsdocTypeSpecialNamePath" ||
node.type === "JsdocTypeSymbol") {
return null;
}
throw (0, util_1.assertNever)(node);
}
function typeNameToTypeInfo(name) {
if (name === "String" || name === "string") {
return type_data_1.STRING;
}
if (name === "Number" || name === "number") {
return type_data_1.NUMBER;
}
if (name === "Boolean" || name === "boolean") {
return type_data_1.BOOLEAN;
}
if (name === "BigInt" || name === "bigint") {
return type_data_1.BIGINT;
}
if (name === "RegExp") {
return type_data_1.REGEXP;
}
if (name === "Array" || name === "array") {
return type_data_1.UNKNOWN_ARRAY;
}
if (name === "Function" || name === "function") {
return type_data_1.UNKNOWN_FUNCTION;
}
if (name === "Object" || name === "object") {
return type_data_1.UNKNOWN_OBJECT;
}
if (name === "Record") {
return type_data_1.UNKNOWN_OBJECT;
}
if (name === "Map") {
return type_data_1.UNKNOWN_MAP;
}
if (name === "Set") {
return type_data_1.UNKNOWN_SET;
}
if (name === "Generator" ||
name === "Iterable" ||
name === "IterableIterator") {
return iterable_1.UNKNOWN_ITERABLE;
}
return null;
}
function getParamPath(name, node, context) {
const parent = (0, ast_utils_1.getParent)(node);
if (!parent) {
return [{ name, index: null }];
}
if (parent.type === "FunctionDeclaration" ||
parent.type === "ArrowFunctionExpression" ||
parent.type === "FunctionExpression") {
return [{ name, index: parent.params.indexOf(node) }];
}
if (parent.type === "AssignmentPattern") {
return getParamPath(name, parent, context);
}
if (parent.type === "ArrayPattern") {
const path = {
name,
index: parent.elements.indexOf(node),
};
return getParamPath(null, parent, context).concat([path]);
}
if (parent.type === "Property") {
const object = (0, ast_utils_1.getParent)(parent);
const path = {
name: (0, utils_1.getPropertyName)(context, parent),
index: object.properties.indexOf(parent),
};
return getParamPath(null, object, context).concat([path]);
}
if (parent.type === "RestElement") {
return getParamPath(name, parent, context);
}
return [{ name, index: null }];
}

View file

@ -0,0 +1,19 @@
import { TypeGlobalFunction } from "./function";
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
export declare class TypeArray implements ITypeClass {
type: "Array";
private readonly collection;
private readonly maybeTuple;
constructor(generator?: () => IterableIterator<TypeInfo | null>, maybeTuple?: boolean);
has(type: NamedType | OtherTypeName): boolean;
paramType(index: number): TypeInfo | null;
at(index: number): TypeInfo | null;
propertyType(name: string): TypeInfo | null;
iterateType(): TypeInfo | null;
returnType(): null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}
export declare const UNKNOWN_ARRAY: TypeArray;
export declare const STRING_ARRAY: TypeArray;
export declare function buildArrayConstructor(): TypeGlobalFunction;

View file

@ -0,0 +1,186 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.STRING_ARRAY = exports.UNKNOWN_ARRAY = exports.TypeArray = void 0;
exports.buildArrayConstructor = buildArrayConstructor;
const util_1 = require("../../util");
const common_1 = require("./common");
const function_1 = require("./function");
const iterable_1 = require("./iterable");
const number_1 = require("./number");
const object_1 = require("./object");
const string_1 = require("./string");
const union_or_intersection_1 = require("./union-or-intersection");
class TypeArray {
constructor(generator, maybeTuple) {
this.type = "Array";
this.collection = new common_1.TypeCollection(generator);
this.maybeTuple = maybeTuple !== null && maybeTuple !== void 0 ? maybeTuple : false;
}
has(type) {
return type === "Array";
}
paramType(index) {
if (index === 0) {
return union_or_intersection_1.TypeUnionOrIntersection.buildType(() => this.collection.all());
}
return null;
}
at(index) {
if (!this.maybeTuple) {
return null;
}
let i = 0;
for (const t of this.collection.tuple()) {
if (i === index) {
return t;
}
i++;
}
return null;
}
propertyType(name) {
if (name === "0") {
return this.paramType(0);
}
return getPrototypes()[name] || null;
}
iterateType() {
return this.paramType(0);
}
returnType() {
return null;
}
typeNames() {
const param0 = (0, common_1.getTypeName)(this.iterateType());
return [`Array${param0 ? `<${param0}>` : ""}`];
}
equals(o) {
if (o.type !== "Array") {
return false;
}
return (0, common_1.isEquals)(this.iterateType(), o.iterateType());
}
}
exports.TypeArray = TypeArray;
exports.UNKNOWN_ARRAY = new TypeArray();
exports.STRING_ARRAY = new TypeArray(() => [string_1.STRING][Symbol.iterator]());
function buildArrayConstructor() {
const ARRAY_TYPES = (0, common_1.createObject)({
isArray: function_1.RETURN_BOOLEAN,
from: function_1.RETURN_UNKNOWN_ARRAY,
of: function_1.RETURN_UNKNOWN_ARRAY,
prototype: null,
[Symbol.species]: null,
});
return new function_1.TypeGlobalFunction(() => exports.UNKNOWN_ARRAY, ARRAY_TYPES);
}
const getPrototypes = (0, util_1.lazy)(() => {
const RETURN_ARRAY_ELEMENT = new function_1.TypeFunction(function returnArrayElement(selfType) {
const type = selfType === null || selfType === void 0 ? void 0 : selfType();
if (!(0, common_1.isTypeClass)(type)) {
return null;
}
return type.paramType(0);
});
const RETURN_SELF = new function_1.TypeFunction(function returnSelf(selfType) {
var _a;
return (_a = selfType === null || selfType === void 0 ? void 0 : selfType()) !== null && _a !== void 0 ? _a : null;
});
const RETURN_CONCAT = new function_1.TypeFunction(function returnConcat(selfType, argTypes) {
return new TypeArray(function* () {
for (const getType of [selfType, ...argTypes]) {
const s = getType === null || getType === void 0 ? void 0 : getType();
if ((0, common_1.isTypeClass)(s)) {
yield s.iterateType();
}
else {
yield null;
}
}
});
});
const RETURN_ENTRIES = new function_1.TypeFunction(function (selfType) {
return new iterable_1.TypeIterable(() => {
return new TypeArray(function* () {
yield number_1.NUMBER;
const type = selfType === null || selfType === void 0 ? void 0 : selfType();
if ((0, common_1.isTypeClass)(type)) {
yield type.iterateType();
}
});
});
});
const RETURN_KEYS = new function_1.TypeFunction(function () {
return new iterable_1.TypeIterable(() => {
return number_1.NUMBER;
});
});
const RETURN_VALUES = new function_1.TypeFunction(function (selfType) {
return new iterable_1.TypeIterable(() => {
const type = selfType === null || selfType === void 0 ? void 0 : selfType();
if ((0, common_1.isTypeClass)(type)) {
return type.iterateType();
}
return null;
});
});
const RETURN_MAP = new function_1.TypeFunction(function (selfType, [argType]) {
return new TypeArray(function* () {
const type = argType === null || argType === void 0 ? void 0 : argType();
if ((0, common_1.isTypeClass)(type)) {
yield type.returnType(selfType, [
() => {
const s = selfType === null || selfType === void 0 ? void 0 : selfType();
return (0, common_1.isTypeClass)(s) ? s.iterateType() : null;
},
() => number_1.NUMBER,
]);
}
});
});
return (0, common_1.createObject)({
...(0, object_1.getObjectPrototypes)(),
toString: function_1.RETURN_STRING,
toLocaleString: function_1.RETURN_STRING,
pop: RETURN_ARRAY_ELEMENT,
push: function_1.RETURN_NUMBER,
concat: RETURN_CONCAT,
join: function_1.RETURN_STRING,
reverse: RETURN_SELF,
shift: RETURN_ARRAY_ELEMENT,
slice: RETURN_SELF,
sort: RETURN_SELF,
splice: RETURN_SELF,
unshift: function_1.RETURN_NUMBER,
indexOf: function_1.RETURN_NUMBER,
lastIndexOf: function_1.RETURN_NUMBER,
every: function_1.RETURN_BOOLEAN,
some: function_1.RETURN_BOOLEAN,
forEach: function_1.RETURN_VOID,
map: RETURN_MAP,
filter: RETURN_SELF,
reduce: null,
reduceRight: null,
find: RETURN_ARRAY_ELEMENT,
findIndex: function_1.RETURN_NUMBER,
fill: function_1.RETURN_UNKNOWN_ARRAY,
copyWithin: RETURN_SELF,
entries: RETURN_ENTRIES,
keys: RETURN_KEYS,
values: RETURN_VALUES,
includes: function_1.RETURN_BOOLEAN,
flatMap: function_1.RETURN_UNKNOWN_ARRAY,
flat: function_1.RETURN_UNKNOWN_ARRAY,
at: RETURN_ARRAY_ELEMENT,
findLast: RETURN_ARRAY_ELEMENT,
findLastIndex: function_1.RETURN_NUMBER,
toReversed: RETURN_SELF,
toSorted: RETURN_SELF,
toSpliced: RETURN_SELF,
with: RETURN_SELF,
length: number_1.NUMBER,
0: null,
[Symbol.iterator]: null,
[Symbol.unscopables]: null,
});
});

View file

@ -0,0 +1,14 @@
import { TypeGlobalFunction } from "./function";
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
export declare class TypeBigInt implements ITypeClass {
type: "BigInt";
has(type: NamedType | OtherTypeName): boolean;
paramType(): null;
propertyType(name: string): TypeInfo | null;
iterateType(): null;
returnType(): null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}
export declare const BIGINT: TypeBigInt;
export declare function buildBigIntConstructor(): TypeGlobalFunction;

View file

@ -0,0 +1,52 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BIGINT = exports.TypeBigInt = void 0;
exports.buildBigIntConstructor = buildBigIntConstructor;
const util_1 = require("../../util");
const common_1 = require("./common");
const function_1 = require("./function");
const object_1 = require("./object");
const string_1 = require("./string");
class TypeBigInt {
constructor() {
this.type = "BigInt";
}
has(type) {
return type === "BigInt";
}
paramType() {
return null;
}
propertyType(name) {
return getPrototypes()[name] || null;
}
iterateType() {
return null;
}
returnType() {
return null;
}
typeNames() {
return ["BigInt"];
}
equals(o) {
return o.type === "BigInt";
}
}
exports.TypeBigInt = TypeBigInt;
exports.BIGINT = new TypeBigInt();
function buildBigIntConstructor() {
const BIGINT_TYPES = (0, common_1.createObject)({
asIntN: function_1.RETURN_BIGINT,
asUintN: function_1.RETURN_BIGINT,
prototype: null,
});
return new function_1.TypeGlobalFunction(() => exports.BIGINT, BIGINT_TYPES);
}
const getPrototypes = (0, util_1.lazy)(() => (0, common_1.createObject)({
...(0, object_1.getObjectPrototypes)(),
toString: function_1.RETURN_STRING,
toLocaleString: function_1.RETURN_STRING,
valueOf: function_1.RETURN_BIGINT,
[Symbol.toStringTag]: string_1.STRING,
}));

View file

@ -0,0 +1,14 @@
import { TypeGlobalFunction } from "./function";
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
export declare class TypeBoolean implements ITypeClass {
type: "Boolean";
has(type: NamedType | OtherTypeName): boolean;
paramType(): null;
propertyType(name: string): TypeInfo | null;
iterateType(): null;
returnType(): null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}
export declare const BOOLEAN: TypeBoolean;
export declare function buildBooleanConstructor(): TypeGlobalFunction;

View file

@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BOOLEAN = exports.TypeBoolean = void 0;
exports.buildBooleanConstructor = buildBooleanConstructor;
const util_1 = require("../../util");
const common_1 = require("./common");
const function_1 = require("./function");
const object_1 = require("./object");
class TypeBoolean {
constructor() {
this.type = "Boolean";
}
has(type) {
return type === "Boolean";
}
paramType() {
return null;
}
propertyType(name) {
return getPrototypes()[name] || null;
}
iterateType() {
return null;
}
returnType() {
return null;
}
typeNames() {
return ["Boolean"];
}
equals(o) {
return o.type === "Boolean";
}
}
exports.TypeBoolean = TypeBoolean;
exports.BOOLEAN = new TypeBoolean();
function buildBooleanConstructor() {
const BOOLEAN_TYPES = (0, common_1.createObject)({
prototype: null,
});
return new function_1.TypeGlobalFunction(() => exports.BOOLEAN, BOOLEAN_TYPES);
}
const getPrototypes = (0, util_1.lazy)(() => (0, common_1.createObject)({
...(0, object_1.getObjectPrototypes)(),
valueOf: function_1.RETURN_BOOLEAN,
}));

View file

@ -0,0 +1,16 @@
import type { NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
export declare function isTypeClass(type: TypeInfo | null | undefined): type is TypeClass;
export declare function isEquals(t1: TypeInfo | null | undefined, t2: TypeInfo | null | undefined): boolean;
export declare function hasType(result: TypeInfo | null, type: NamedType | OtherTypeName): boolean;
export declare function createObject<T>(t: T): T;
export declare class TypeCollection {
readonly generator: () => IterableIterator<TypeInfo>;
private unknownIndex;
constructor(generator?: () => IterableIterator<TypeInfo | null>);
has(type: NamedType | OtherTypeName): boolean;
isOneType(): boolean;
tuple(): IterableIterator<TypeInfo>;
all(): IterableIterator<TypeInfo>;
strings(): IterableIterator<string>;
}
export declare function getTypeName(type: TypeInfo | null): string | null;

View file

@ -0,0 +1,136 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeCollection = void 0;
exports.isTypeClass = isTypeClass;
exports.isEquals = isEquals;
exports.hasType = hasType;
exports.createObject = createObject;
exports.getTypeName = getTypeName;
function isTypeClass(type) {
if (!type) {
return false;
}
const t = typeof type;
if (t === "string") {
return false;
}
return true;
}
function isEquals(t1, t2) {
if (t1 === t2) {
return true;
}
if (isTypeClass(t1) && isTypeClass(t2)) {
return t1.equals(t2);
}
return false;
}
function hasType(result, type) {
if (result == null) {
return false;
}
if (typeof result === "string") {
return result === type;
}
return result.has(type);
}
function createObject(t) {
return Object.assign(Object.create(null), t);
}
class TypeCollection {
constructor(generator) {
this.unknownIndex = null;
const that = this;
this.generator = generator
? function* () {
var _a;
let index = 0;
for (const t of generator()) {
if (t != null) {
yield t;
}
else {
(_a = that.unknownIndex) !== null && _a !== void 0 ? _a : (that.unknownIndex = index);
}
index++;
}
}
: () => [][Symbol.iterator]();
}
has(type) {
for (const t of this.generator()) {
if (typeof t === "string") {
if (t === type)
return true;
}
else {
if (t.has(type))
return true;
}
}
return false;
}
isOneType() {
let first = null;
for (const t of this.all()) {
if (first == null) {
first = t;
}
else {
if (!isEquals(first, t)) {
return false;
}
}
}
return true;
}
*tuple() {
let index = 0;
for (const t of this.generator()) {
if (this.unknownIndex != null && index < this.unknownIndex) {
return;
}
yield t;
index++;
}
}
*all() {
const set = new Set();
for (const t of this.generator()) {
if (!set.has(t)) {
set.add(t);
yield t;
}
}
}
*strings() {
const set = new Set();
for (const t of this.all()) {
if (typeof t === "string") {
const str = t;
if (!set.has(str)) {
set.add(t);
yield str;
}
}
else {
for (const str of t.typeNames()) {
if (!set.has(str)) {
set.add(t);
yield str;
}
}
}
}
}
}
exports.TypeCollection = TypeCollection;
function getTypeName(type) {
if (type == null) {
return null;
}
if (typeof type === "string") {
return type;
}
return type.typeNames().join("|");
}

View file

@ -0,0 +1,37 @@
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
type FunctionArg = (thisType: (() => TypeInfo | null) | null, argTypes: ((() => TypeInfo | null) | null)[], meta?: {
isConstructor?: boolean;
}) => TypeInfo | null;
export declare class TypeFunction implements ITypeClass {
type: "Function";
private readonly fn;
constructor(fn: FunctionArg);
has(type: NamedType | OtherTypeName): boolean;
returnType(thisType: (() => TypeInfo | null) | null, argTypes: ((() => TypeInfo | null) | null)[], meta?: {
isConstructor?: boolean;
}): TypeInfo | null;
paramType(): null;
propertyType(name: string): TypeInfo | null;
iterateType(): null;
typeNames(): string[];
equals(_o: TypeClass): boolean;
}
export declare class TypeGlobalFunction extends TypeFunction {
private readonly props;
constructor(fn: FunctionArg, props: {
[key: string]: TypeInfo | null;
});
propertyType(name: string): TypeInfo | null;
}
export declare const UNKNOWN_FUNCTION: TypeFunction;
export declare function buildFunctionConstructor(): TypeGlobalFunction;
export declare const RETURN_VOID: TypeFunction;
export declare const RETURN_STRING: TypeFunction;
export declare const RETURN_NUMBER: TypeFunction;
export declare const RETURN_BOOLEAN: TypeFunction;
export declare const RETURN_UNKNOWN_ARRAY: TypeFunction;
export declare const RETURN_STRING_ARRAY: TypeFunction;
export declare const RETURN_UNKNOWN_OBJECT: TypeFunction;
export declare const RETURN_REGEXP: TypeFunction;
export declare const RETURN_BIGINT: TypeFunction;
export {};

View file

@ -0,0 +1,106 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RETURN_BIGINT = exports.RETURN_REGEXP = exports.RETURN_UNKNOWN_OBJECT = exports.RETURN_STRING_ARRAY = exports.RETURN_UNKNOWN_ARRAY = exports.RETURN_BOOLEAN = exports.RETURN_NUMBER = exports.RETURN_STRING = exports.RETURN_VOID = exports.UNKNOWN_FUNCTION = exports.TypeGlobalFunction = exports.TypeFunction = void 0;
exports.buildFunctionConstructor = buildFunctionConstructor;
const util_1 = require("../../util");
const array_1 = require("./array");
const bigint_1 = require("./bigint");
const boolean_1 = require("./boolean");
const common_1 = require("./common");
const number_1 = require("./number");
const object_1 = require("./object");
const regexp_1 = require("./regexp");
const string_1 = require("./string");
class TypeFunction {
constructor(fn) {
this.type = "Function";
this.fn = fn;
}
has(type) {
return type === "Function";
}
returnType(thisType, argTypes, meta) {
return this.fn(thisType, argTypes, meta);
}
paramType() {
return null;
}
propertyType(name) {
return getPrototypes()[name] || null;
}
iterateType() {
return null;
}
typeNames() {
return ["Function"];
}
equals(_o) {
return false;
}
}
exports.TypeFunction = TypeFunction;
class TypeGlobalFunction extends TypeFunction {
constructor(fn, props) {
super(fn);
this.props = props;
}
propertyType(name) {
return this.props[name] || super.propertyType(name);
}
}
exports.TypeGlobalFunction = TypeGlobalFunction;
exports.UNKNOWN_FUNCTION = new TypeFunction(function returnUnknown() {
return null;
});
function buildFunctionConstructor() {
const FUNCTION_TYPES = (0, common_1.createObject)({
prototype: null,
});
return new TypeGlobalFunction(function returnFunction() {
return exports.UNKNOWN_FUNCTION;
}, FUNCTION_TYPES);
}
exports.RETURN_VOID = new TypeFunction(function retVoid() {
return "undefined";
});
exports.RETURN_STRING = new TypeFunction(function returnString() {
return string_1.STRING;
});
exports.RETURN_NUMBER = new TypeFunction(function returnNumber() {
return number_1.NUMBER;
});
exports.RETURN_BOOLEAN = new TypeFunction(function returnBoolean() {
return boolean_1.BOOLEAN;
});
exports.RETURN_UNKNOWN_ARRAY = new TypeFunction(function returnUnknownArray() {
return array_1.UNKNOWN_ARRAY;
});
exports.RETURN_STRING_ARRAY = new TypeFunction(function returnStringArray() {
return array_1.STRING_ARRAY;
});
exports.RETURN_UNKNOWN_OBJECT = new TypeFunction(function returnObject() {
return object_1.UNKNOWN_OBJECT;
});
exports.RETURN_REGEXP = new TypeFunction(function returnRegExp() {
return regexp_1.REGEXP;
});
exports.RETURN_BIGINT = new TypeFunction(function returnBigInt() {
return bigint_1.BIGINT;
});
const RETURN_SELF = new TypeFunction(function returnSelf(selfType) {
var _a;
return (_a = selfType === null || selfType === void 0 ? void 0 : selfType()) !== null && _a !== void 0 ? _a : null;
});
const getPrototypes = (0, util_1.lazy)(() => (0, common_1.createObject)({
...(0, object_1.getObjectPrototypes)(),
toString: exports.RETURN_STRING,
bind: RETURN_SELF,
length: number_1.NUMBER,
name: string_1.STRING,
apply: exports.UNKNOWN_FUNCTION,
call: exports.UNKNOWN_FUNCTION,
arguments: null,
caller: exports.UNKNOWN_FUNCTION,
prototype: null,
[Symbol.hasInstance]: null,
}));

View file

@ -0,0 +1,12 @@
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
export declare class TypeGlobal implements ITypeClass {
type: "Global";
has(_type: NamedType | OtherTypeName): boolean;
paramType(): null;
propertyType(name: string): TypeInfo | null;
iterateType(): null;
returnType(): null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}
export declare const GLOBAL: TypeGlobal;

View file

@ -0,0 +1,72 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GLOBAL = exports.TypeGlobal = void 0;
const util_1 = require("../../util");
const array_1 = require("./array");
const bigint_1 = require("./bigint");
const boolean_1 = require("./boolean");
const common_1 = require("./common");
const function_1 = require("./function");
const map_1 = require("./map");
const number_1 = require("./number");
const object_1 = require("./object");
const regexp_1 = require("./regexp");
const set_1 = require("./set");
const string_1 = require("./string");
class TypeGlobal {
constructor() {
this.type = "Global";
}
has(_type) {
return false;
}
paramType() {
return null;
}
propertyType(name) {
return getProperties()[name] || null;
}
iterateType() {
return null;
}
returnType() {
return null;
}
typeNames() {
return ["Global"];
}
equals(o) {
return o.type === "Global";
}
}
exports.TypeGlobal = TypeGlobal;
exports.GLOBAL = new TypeGlobal();
const getProperties = (0, util_1.lazy)(() => (0, common_1.createObject)({
String: (0, string_1.buildStringConstructor)(),
Number: (0, number_1.buildNumberConstructor)(),
Boolean: (0, boolean_1.buildBooleanConstructor)(),
RegExp: (0, regexp_1.buildRegExpConstructor)(),
BigInt: (0, bigint_1.buildBigIntConstructor)(),
Array: (0, array_1.buildArrayConstructor)(),
Object: (0, object_1.buildObjectConstructor)(),
Function: (0, function_1.buildFunctionConstructor)(),
Map: (0, map_1.buildMapConstructor)(),
Set: (0, set_1.buildSetConstructor)(),
isFinite: function_1.RETURN_BOOLEAN,
isNaN: function_1.RETURN_BOOLEAN,
parseFloat: function_1.RETURN_NUMBER,
parseInt: function_1.RETURN_NUMBER,
decodeURI: function_1.RETURN_STRING,
decodeURIComponent: function_1.RETURN_STRING,
encodeURI: function_1.RETURN_STRING,
encodeURIComponent: function_1.RETURN_STRING,
escape: function_1.RETURN_STRING,
unescape: function_1.RETURN_STRING,
globalThis: exports.GLOBAL,
window: exports.GLOBAL,
self: exports.GLOBAL,
global: exports.GLOBAL,
undefined: "undefined",
Infinity: number_1.NUMBER,
NaN: number_1.NUMBER,
}));

View file

@ -0,0 +1,48 @@
import type * as ES from "estree";
import { TypeArray, UNKNOWN_ARRAY } from "./array";
import type { TypeBigInt } from "./bigint";
import { BIGINT } from "./bigint";
import type { TypeBoolean } from "./boolean";
import { BOOLEAN } from "./boolean";
import { isTypeClass, hasType } from "./common";
import { UNKNOWN_FUNCTION, TypeFunction } from "./function";
import type { TypeGlobal } from "./global";
import { GLOBAL } from "./global";
import type { TypeIterable } from "./iterable";
import { TypeMap, UNKNOWN_MAP } from "./map";
import type { TypeNumber } from "./number";
import { NUMBER } from "./number";
import { TypeObject, UNKNOWN_OBJECT } from "./object";
import type { TypeRegExp } from "./regexp";
import { REGEXP } from "./regexp";
import { TypeSet, UNKNOWN_SET } from "./set";
import type { TypeString } from "./string";
import { STRING } from "./string";
import { TypeUnionOrIntersection } from "./union-or-intersection";
export { hasType, TypeArray, TypeObject, UNKNOWN_ARRAY, UNKNOWN_OBJECT, UNKNOWN_FUNCTION, STRING, NUMBER, BOOLEAN, REGEXP, BIGINT, TypeUnionOrIntersection, isTypeClass, TypeSet, TypeMap, UNKNOWN_MAP, UNKNOWN_SET, TypeFunction, GLOBAL, };
export declare const GLOBAL_NUMBER: unique symbol;
export declare const GLOBAL_REGEXP: unique symbol;
export declare const GLOBAL_FUNCTION: unique symbol;
export declare const GLOBAL_OBJECT: unique symbol;
export type NamedType = "null" | "undefined";
export type OtherTypeName = "Function" | "Array" | "Object" | "String" | "Number" | "Boolean" | "RegExp" | "BigInt" | "Map" | "Set";
export type TypeInfo = NamedType | TypeClass;
export type TypeClass = TypeUnionOrIntersection | TypeArray | TypeObject | TypeString | TypeNumber | TypeBoolean | TypeRegExp | TypeBigInt | TypeMap | TypeSet | TypeFunction | TypeIterable | TypeGlobal;
export interface ITypeClass {
type: "TypeUnionOrIntersection" | "Array" | "Object" | "String" | "Number" | "Boolean" | "RegExp" | "BigInt" | "Map" | "Set" | "Function" | "Iterable" | "Global";
has(type: NamedType | OtherTypeName): boolean;
paramType(index: number): TypeInfo | null;
iterateType(): TypeInfo | null;
propertyType(name: string): TypeInfo | null;
returnType(thisType: (() => TypeInfo | null) | null, argTypes: ((() => TypeInfo | null) | null)[], meta?: {
isConstructor?: boolean;
}): TypeInfo | null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}
export declare const BI_OPERATOR_TYPES: {
[key in ES.BinaryExpression["operator"]]: (getTypes: () => [TypeInfo | null, TypeInfo | null]) => TypeInfo | null;
};
export declare const UN_OPERATOR_TYPES: {
[key in ES.UnaryExpression["operator"]]: (getType: () => TypeInfo | null) => TypeInfo | null;
};

View file

@ -0,0 +1,136 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UN_OPERATOR_TYPES = exports.BI_OPERATOR_TYPES = exports.GLOBAL_OBJECT = exports.GLOBAL_FUNCTION = exports.GLOBAL_REGEXP = exports.GLOBAL_NUMBER = exports.GLOBAL = exports.TypeFunction = exports.UNKNOWN_SET = exports.UNKNOWN_MAP = exports.TypeMap = exports.TypeSet = exports.isTypeClass = exports.TypeUnionOrIntersection = exports.BIGINT = exports.REGEXP = exports.BOOLEAN = exports.NUMBER = exports.STRING = exports.UNKNOWN_FUNCTION = exports.UNKNOWN_OBJECT = exports.UNKNOWN_ARRAY = exports.TypeObject = exports.TypeArray = exports.hasType = void 0;
const array_1 = require("./array");
Object.defineProperty(exports, "TypeArray", { enumerable: true, get: function () { return array_1.TypeArray; } });
Object.defineProperty(exports, "UNKNOWN_ARRAY", { enumerable: true, get: function () { return array_1.UNKNOWN_ARRAY; } });
const bigint_1 = require("./bigint");
Object.defineProperty(exports, "BIGINT", { enumerable: true, get: function () { return bigint_1.BIGINT; } });
const boolean_1 = require("./boolean");
Object.defineProperty(exports, "BOOLEAN", { enumerable: true, get: function () { return boolean_1.BOOLEAN; } });
const common_1 = require("./common");
Object.defineProperty(exports, "isTypeClass", { enumerable: true, get: function () { return common_1.isTypeClass; } });
Object.defineProperty(exports, "hasType", { enumerable: true, get: function () { return common_1.hasType; } });
const function_1 = require("./function");
Object.defineProperty(exports, "UNKNOWN_FUNCTION", { enumerable: true, get: function () { return function_1.UNKNOWN_FUNCTION; } });
Object.defineProperty(exports, "TypeFunction", { enumerable: true, get: function () { return function_1.TypeFunction; } });
const global_1 = require("./global");
Object.defineProperty(exports, "GLOBAL", { enumerable: true, get: function () { return global_1.GLOBAL; } });
const map_1 = require("./map");
Object.defineProperty(exports, "TypeMap", { enumerable: true, get: function () { return map_1.TypeMap; } });
Object.defineProperty(exports, "UNKNOWN_MAP", { enumerable: true, get: function () { return map_1.UNKNOWN_MAP; } });
const number_1 = require("./number");
Object.defineProperty(exports, "NUMBER", { enumerable: true, get: function () { return number_1.NUMBER; } });
const object_1 = require("./object");
Object.defineProperty(exports, "TypeObject", { enumerable: true, get: function () { return object_1.TypeObject; } });
Object.defineProperty(exports, "UNKNOWN_OBJECT", { enumerable: true, get: function () { return object_1.UNKNOWN_OBJECT; } });
const regexp_1 = require("./regexp");
Object.defineProperty(exports, "REGEXP", { enumerable: true, get: function () { return regexp_1.REGEXP; } });
const set_1 = require("./set");
Object.defineProperty(exports, "TypeSet", { enumerable: true, get: function () { return set_1.TypeSet; } });
Object.defineProperty(exports, "UNKNOWN_SET", { enumerable: true, get: function () { return set_1.UNKNOWN_SET; } });
const string_1 = require("./string");
Object.defineProperty(exports, "STRING", { enumerable: true, get: function () { return string_1.STRING; } });
const union_or_intersection_1 = require("./union-or-intersection");
Object.defineProperty(exports, "TypeUnionOrIntersection", { enumerable: true, get: function () { return union_or_intersection_1.TypeUnionOrIntersection; } });
exports.GLOBAL_NUMBER = Symbol("Number");
exports.GLOBAL_REGEXP = Symbol("RegExp");
exports.GLOBAL_FUNCTION = Symbol("Function");
exports.GLOBAL_OBJECT = Symbol("Object");
function binaryNumOp(getTypes) {
const [t1, t2] = getTypes();
return union_or_intersection_1.TypeUnionOrIntersection.buildType(function* () {
let unknown = true;
if ((0, common_1.hasType)(t1, "Number") || (0, common_1.hasType)(t2, "Number")) {
unknown = false;
yield number_1.NUMBER;
}
if ((0, common_1.hasType)(t1, "BigInt") && (0, common_1.hasType)(t2, "BigInt")) {
unknown = false;
yield bigint_1.BIGINT;
}
if (unknown) {
yield number_1.NUMBER;
yield bigint_1.BIGINT;
}
});
}
function resultBool() {
return boolean_1.BOOLEAN;
}
function binaryBitwise() {
return number_1.NUMBER;
}
exports.BI_OPERATOR_TYPES = (0, common_1.createObject)({
"==": resultBool,
"!=": resultBool,
"===": resultBool,
"!==": resultBool,
"<": resultBool,
"<=": resultBool,
">": resultBool,
">=": resultBool,
in: resultBool,
instanceof: resultBool,
"-": binaryNumOp,
"*": binaryNumOp,
"/": binaryNumOp,
"%": binaryNumOp,
"^": binaryNumOp,
"**": binaryNumOp,
"&": binaryNumOp,
"|": binaryNumOp,
"<<": binaryBitwise,
">>": binaryBitwise,
">>>": binaryBitwise,
"+": (getTypes) => {
const [t1, t2] = getTypes();
return union_or_intersection_1.TypeUnionOrIntersection.buildType(function* () {
let unknown = true;
if ((0, common_1.hasType)(t1, "String") || (0, common_1.hasType)(t2, "String")) {
unknown = false;
yield string_1.STRING;
}
if ((0, common_1.hasType)(t1, "Number") && (0, common_1.hasType)(t2, "Number")) {
unknown = false;
yield number_1.NUMBER;
}
if ((0, common_1.hasType)(t1, "BigInt") && (0, common_1.hasType)(t2, "BigInt")) {
unknown = false;
yield bigint_1.BIGINT;
}
if (unknown) {
yield string_1.STRING;
yield number_1.NUMBER;
yield bigint_1.BIGINT;
}
});
},
});
function unaryNumOp(getType) {
const t = getType();
return union_or_intersection_1.TypeUnionOrIntersection.buildType(function* () {
let unknown = true;
if ((0, common_1.hasType)(t, "Number")) {
unknown = false;
yield number_1.NUMBER;
}
if ((0, common_1.hasType)(t, "BigInt")) {
unknown = false;
yield bigint_1.BIGINT;
}
if (unknown) {
yield number_1.NUMBER;
yield bigint_1.BIGINT;
}
});
}
exports.UN_OPERATOR_TYPES = (0, common_1.createObject)({
"!": resultBool,
delete: resultBool,
"+": unaryNumOp,
"-": unaryNumOp,
"~": unaryNumOp,
void: () => "undefined",
typeof: () => string_1.STRING,
});

View file

@ -0,0 +1,14 @@
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
export declare class TypeIterable implements ITypeClass {
type: "Iterable";
private readonly param0;
constructor(param0: () => TypeInfo | null);
has(_type: NamedType | OtherTypeName): boolean;
paramType(index: number): TypeInfo | null;
propertyType(name: string): TypeInfo | null;
iterateType(): TypeInfo | null;
returnType(): null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}
export declare const UNKNOWN_ITERABLE: TypeIterable;

View file

@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UNKNOWN_ITERABLE = exports.TypeIterable = void 0;
const util_1 = require("../../util");
const common_1 = require("./common");
const object_1 = require("./object");
const getPrototypes = (0, util_1.lazy)(() => {
return (0, common_1.createObject)({
...(0, object_1.getObjectPrototypes)(),
[Symbol.iterator]: null,
});
});
class TypeIterable {
constructor(param0) {
this.type = "Iterable";
this.param0 = param0;
}
has(_type) {
return false;
}
paramType(index) {
if (index === 0) {
return this.param0();
}
return null;
}
propertyType(name) {
return getPrototypes()[name] || null;
}
iterateType() {
return this.paramType(0);
}
returnType() {
return null;
}
typeNames() {
const param0 = (0, common_1.getTypeName)(this.iterateType());
return [`Iterable${param0 != null ? `<${param0}>` : ""}`];
}
equals(o) {
if (o.type !== "Iterable") {
return false;
}
return (0, common_1.isEquals)(this.iterateType(), o.iterateType());
}
}
exports.TypeIterable = TypeIterable;
exports.UNKNOWN_ITERABLE = new TypeIterable(() => null);

View file

@ -0,0 +1,17 @@
import { TypeFunction } from "./function";
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
export declare class TypeMap implements ITypeClass {
type: "Map";
private readonly param0;
private readonly param1;
constructor(param0: () => TypeInfo | null, param1: () => TypeInfo | null);
has(type: NamedType | OtherTypeName): boolean;
paramType(index: number): TypeInfo | null;
propertyType(name: string): TypeInfo | null;
iterateType(): TypeInfo | null;
returnType(): null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}
export declare const UNKNOWN_MAP: TypeMap;
export declare function buildMapConstructor(): TypeFunction;

View file

@ -0,0 +1,142 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UNKNOWN_MAP = exports.TypeMap = void 0;
exports.buildMapConstructor = buildMapConstructor;
const util_1 = require("../../util");
const array_1 = require("./array");
const common_1 = require("./common");
const function_1 = require("./function");
const iterable_1 = require("./iterable");
const number_1 = require("./number");
const object_1 = require("./object");
const string_1 = require("./string");
const getPrototypes = (0, util_1.lazy)(() => {
const RETURN_MAP_VALUE = new function_1.TypeFunction(function returnMapValue(selfType) {
const type = selfType === null || selfType === void 0 ? void 0 : selfType();
if (!(0, common_1.isTypeClass)(type)) {
return null;
}
return type.paramType(1);
});
const RETURN_SELF = new function_1.TypeFunction(function returnSelf(selfType) {
var _a;
return (_a = selfType === null || selfType === void 0 ? void 0 : selfType()) !== null && _a !== void 0 ? _a : null;
});
const RETURN_ENTRIES = new function_1.TypeFunction(function (selfType) {
return new iterable_1.TypeIterable(() => {
return new array_1.TypeArray(function* () {
const type = selfType === null || selfType === void 0 ? void 0 : selfType();
if ((0, common_1.isTypeClass)(type)) {
yield type.paramType(0);
yield type.paramType(1);
}
else {
yield null;
yield null;
}
}, true);
});
});
const RETURN_KEYS = new function_1.TypeFunction(function (selfType) {
return new iterable_1.TypeIterable(() => {
const type = selfType === null || selfType === void 0 ? void 0 : selfType();
if ((0, common_1.isTypeClass)(type)) {
return type.paramType(0);
}
return null;
});
});
const RETURN_VALUES = new function_1.TypeFunction(function (selfType) {
return new iterable_1.TypeIterable(() => {
const type = selfType === null || selfType === void 0 ? void 0 : selfType();
if ((0, common_1.isTypeClass)(type)) {
return type.paramType(1);
}
return null;
});
});
return (0, common_1.createObject)({
...(0, object_1.getObjectPrototypes)(),
clear: function_1.RETURN_VOID,
delete: function_1.RETURN_BOOLEAN,
forEach: function_1.RETURN_VOID,
get: RETURN_MAP_VALUE,
has: function_1.RETURN_BOOLEAN,
set: RETURN_SELF,
size: number_1.NUMBER,
entries: RETURN_ENTRIES,
keys: RETURN_KEYS,
values: RETURN_VALUES,
[Symbol.iterator]: null,
[Symbol.toStringTag]: string_1.STRING,
});
});
class TypeMap {
constructor(param0, param1) {
this.type = "Map";
this.param0 = param0;
this.param1 = param1;
}
has(type) {
return type === "Map";
}
paramType(index) {
if (index === 0) {
return this.param0();
}
if (index === 1) {
return this.param1();
}
return null;
}
propertyType(name) {
return getPrototypes()[name] || null;
}
iterateType() {
const map = this;
return new array_1.TypeArray(function* () {
yield map.paramType(0);
yield map.paramType(1);
}, true);
}
returnType() {
return null;
}
typeNames() {
const param0 = (0, common_1.getTypeName)(this.paramType(0));
const param1 = (0, common_1.getTypeName)(this.paramType(1));
return [
`Map${param0 != null && param1 != null ? `<${param0},${param1}>` : ""}`,
];
}
equals(o) {
if (o.type !== "Map") {
return false;
}
return ((0, common_1.isEquals)(this.paramType(0), o.paramType(0)) &&
(0, common_1.isEquals)(this.paramType(1), o.paramType(1)));
}
}
exports.TypeMap = TypeMap;
exports.UNKNOWN_MAP = new TypeMap(() => null, () => null);
function buildMapConstructor() {
const MAP_TYPES = (0, common_1.createObject)({
prototype: null,
[Symbol.species]: null,
});
return new function_1.TypeGlobalFunction(mapConstructor, MAP_TYPES);
}
function mapConstructor(_thisType, argTypes, meta) {
var _a;
if (!(meta === null || meta === void 0 ? void 0 : meta.isConstructor)) {
return null;
}
const arg = (_a = argTypes[0]) === null || _a === void 0 ? void 0 : _a.call(argTypes);
if ((0, common_1.isTypeClass)(arg) && arg.type === "Array") {
const iterateType = arg.iterateType();
if ((0, common_1.isTypeClass)(iterateType) && iterateType.type === "Array") {
return new TypeMap(() => iterateType.at(0), () => iterateType.at(1));
}
}
return exports.UNKNOWN_MAP;
}

View file

@ -0,0 +1,14 @@
import { TypeGlobalFunction } from "./function";
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
export declare class TypeNumber implements ITypeClass {
type: "Number";
has(type: NamedType | OtherTypeName): boolean;
paramType(): null;
propertyType(name: string): TypeInfo | null;
iterateType(): null;
returnType(): null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}
export declare const NUMBER: TypeNumber;
export declare function buildNumberConstructor(): TypeGlobalFunction;

View file

@ -0,0 +1,65 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NUMBER = exports.TypeNumber = void 0;
exports.buildNumberConstructor = buildNumberConstructor;
const util_1 = require("../../util");
const common_1 = require("./common");
const function_1 = require("./function");
const object_1 = require("./object");
class TypeNumber {
constructor() {
this.type = "Number";
}
has(type) {
return type === "Number";
}
paramType() {
return null;
}
propertyType(name) {
return getPrototypes()[name] || null;
}
iterateType() {
return null;
}
returnType() {
return null;
}
typeNames() {
return ["Number"];
}
equals(o) {
return o.type === "Number";
}
}
exports.TypeNumber = TypeNumber;
exports.NUMBER = new TypeNumber();
function buildNumberConstructor() {
const NUMBER_TYPES = (0, common_1.createObject)({
MAX_VALUE: exports.NUMBER,
MIN_VALUE: exports.NUMBER,
NaN: exports.NUMBER,
NEGATIVE_INFINITY: exports.NUMBER,
POSITIVE_INFINITY: exports.NUMBER,
EPSILON: exports.NUMBER,
isFinite: function_1.RETURN_BOOLEAN,
isInteger: function_1.RETURN_BOOLEAN,
isNaN: function_1.RETURN_BOOLEAN,
isSafeInteger: function_1.RETURN_BOOLEAN,
MAX_SAFE_INTEGER: exports.NUMBER,
MIN_SAFE_INTEGER: exports.NUMBER,
parseFloat: function_1.RETURN_NUMBER,
parseInt: function_1.RETURN_NUMBER,
prototype: null,
});
return new function_1.TypeGlobalFunction(() => exports.NUMBER, NUMBER_TYPES);
}
const getPrototypes = (0, util_1.lazy)(() => (0, common_1.createObject)({
...(0, object_1.getObjectPrototypes)(),
toString: function_1.RETURN_STRING,
toFixed: function_1.RETURN_STRING,
toExponential: function_1.RETURN_STRING,
toPrecision: function_1.RETURN_STRING,
valueOf: function_1.RETURN_NUMBER,
toLocaleString: function_1.RETURN_STRING,
}));

View file

@ -0,0 +1,25 @@
import { TypeGlobalFunction } from "./function";
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
type ObjectKeys = "constructor" | "toString" | "toLocaleString" | "valueOf" | "hasOwnProperty" | "isPrototypeOf" | "propertyIsEnumerable";
export declare const getObjectPrototypes: () => {
[key in ObjectKeys]: TypeInfo | null;
};
export declare class TypeObject implements ITypeClass {
type: "Object";
private readonly propertiesGenerator;
constructor(propertiesGenerator?: () => IterableIterator<[
string,
() => TypeInfo | null
]>);
allProperties(): IterableIterator<[string, () => TypeInfo | null]>;
has(type: NamedType | OtherTypeName): boolean;
paramType(): null;
propertyType(name: string): TypeInfo | null;
iterateType(): null;
returnType(): null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}
export declare const UNKNOWN_OBJECT: TypeObject;
export declare function buildObjectConstructor(): TypeGlobalFunction;
export {};

View file

@ -0,0 +1,139 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UNKNOWN_OBJECT = exports.TypeObject = exports.getObjectPrototypes = void 0;
exports.buildObjectConstructor = buildObjectConstructor;
const util_1 = require("../../util");
const common_1 = require("./common");
const function_1 = require("./function");
exports.getObjectPrototypes = (0, util_1.lazy)(() => (0, common_1.createObject)({
constructor: function_1.UNKNOWN_FUNCTION,
toString: function_1.RETURN_STRING,
toLocaleString: function_1.RETURN_STRING,
valueOf: function_1.RETURN_UNKNOWN_OBJECT,
hasOwnProperty: function_1.RETURN_BOOLEAN,
isPrototypeOf: function_1.RETURN_BOOLEAN,
propertyIsEnumerable: function_1.RETURN_BOOLEAN,
}));
class TypeObject {
constructor(propertiesGenerator) {
this.type = "Object";
this.propertiesGenerator =
propertiesGenerator !== null && propertiesGenerator !== void 0 ? propertiesGenerator : (() => {
return [][Symbol.iterator]();
});
}
*allProperties() {
const set = new Set();
for (const t of this.propertiesGenerator()) {
if (set.has(t[0])) {
continue;
}
set.add(t[0]);
yield t;
}
}
has(type) {
return type === "Object";
}
paramType() {
return null;
}
propertyType(name) {
for (const [key, getValue] of this.allProperties()) {
if (key === name) {
return getValue();
}
}
return (0, exports.getObjectPrototypes)()[name] || null;
}
iterateType() {
return null;
}
returnType() {
return null;
}
typeNames() {
return ["Object"];
}
equals(o) {
if (o.type !== "Object") {
return false;
}
const itr2 = o.allProperties();
const props2 = new Map();
for (const [key1, get1] of this.allProperties()) {
const get2 = props2.get(key1);
if (get2) {
if (!(0, common_1.isEquals)(get1(), get2())) {
return false;
}
}
else {
let e2 = itr2.next();
while (!e2.done) {
const [key2, get] = e2.value;
props2.set(key2, get);
if (key1 === key2) {
if (!(0, common_1.isEquals)(get1(), get())) {
return false;
}
break;
}
e2 = itr2.next();
}
if (e2.done) {
return false;
}
}
}
const e2 = itr2.next();
if (!e2.done) {
return false;
}
return true;
}
}
exports.TypeObject = TypeObject;
exports.UNKNOWN_OBJECT = new TypeObject();
function buildObjectConstructor() {
const RETURN_ARG = new function_1.TypeFunction(function returnArg(_selfType, argTypes) {
var _a, _b;
return (_b = (_a = argTypes[0]) === null || _a === void 0 ? void 0 : _a.call(argTypes)) !== null && _b !== void 0 ? _b : null;
});
const RETURN_ASSIGN = new function_1.TypeFunction(function returnAssign(selfType, argTypes) {
return new TypeObject(function* () {
for (const getType of [selfType, ...argTypes].reverse()) {
const s = getType === null || getType === void 0 ? void 0 : getType();
if ((0, common_1.isTypeClass)(s) && s.type === "Object") {
yield* s.allProperties();
}
}
});
});
const OBJECT_TYPES = (0, common_1.createObject)({
getPrototypeOf: null,
getOwnPropertyDescriptor: null,
getOwnPropertyNames: function_1.RETURN_STRING_ARRAY,
create: null,
defineProperty: null,
defineProperties: null,
seal: RETURN_ARG,
freeze: RETURN_ARG,
preventExtensions: null,
isSealed: function_1.RETURN_BOOLEAN,
isFrozen: function_1.RETURN_BOOLEAN,
isExtensible: function_1.RETURN_BOOLEAN,
keys: function_1.RETURN_STRING_ARRAY,
assign: RETURN_ASSIGN,
getOwnPropertySymbols: function_1.RETURN_UNKNOWN_ARRAY,
is: function_1.RETURN_BOOLEAN,
setPrototypeOf: null,
values: function_1.RETURN_UNKNOWN_ARRAY,
entries: function_1.RETURN_UNKNOWN_ARRAY,
getOwnPropertyDescriptors: null,
fromEntries: null,
hasOwn: function_1.RETURN_BOOLEAN,
prototype: null,
});
return new function_1.TypeGlobalFunction((_thisType, [argType]) => { var _a; return (_a = argType === null || argType === void 0 ? void 0 : argType()) !== null && _a !== void 0 ? _a : exports.UNKNOWN_OBJECT; }, OBJECT_TYPES);
}

View file

@ -0,0 +1,14 @@
import { TypeGlobalFunction } from "./function";
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
export declare class TypeRegExp implements ITypeClass {
type: "RegExp";
has(type: NamedType | OtherTypeName): boolean;
paramType(): null;
propertyType(name: string): TypeInfo | null;
iterateType(): null;
returnType(): null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}
export declare const REGEXP: TypeRegExp;
export declare function buildRegExpConstructor(): TypeGlobalFunction;

View file

@ -0,0 +1,86 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.REGEXP = exports.TypeRegExp = void 0;
exports.buildRegExpConstructor = buildRegExpConstructor;
const util_1 = require("../../util");
const boolean_1 = require("./boolean");
const common_1 = require("./common");
const function_1 = require("./function");
const number_1 = require("./number");
const object_1 = require("./object");
const string_1 = require("./string");
class TypeRegExp {
constructor() {
this.type = "RegExp";
}
has(type) {
return type === "RegExp";
}
paramType() {
return null;
}
propertyType(name) {
return getPrototypes()[name] || null;
}
iterateType() {
return null;
}
returnType() {
return null;
}
typeNames() {
return ["RegExp"];
}
equals(o) {
return o.type === "RegExp";
}
}
exports.TypeRegExp = TypeRegExp;
exports.REGEXP = new TypeRegExp();
function buildRegExpConstructor() {
const REGEXP_TYPES = (0, common_1.createObject)({
$1: string_1.STRING,
$2: string_1.STRING,
$3: string_1.STRING,
$4: string_1.STRING,
$5: string_1.STRING,
$6: string_1.STRING,
$7: string_1.STRING,
$8: string_1.STRING,
$9: string_1.STRING,
$_: string_1.STRING,
"$&": string_1.STRING,
"$+": string_1.STRING,
"$`": string_1.STRING,
"$'": string_1.STRING,
input: string_1.STRING,
lastParen: string_1.STRING,
leftContext: string_1.STRING,
rightContext: string_1.STRING,
lastMatch: number_1.NUMBER,
prototype: null,
[Symbol.species]: null,
});
return new function_1.TypeGlobalFunction(() => exports.REGEXP, REGEXP_TYPES);
}
const getPrototypes = (0, util_1.lazy)(() => (0, common_1.createObject)({
...(0, object_1.getObjectPrototypes)(),
exec: function_1.RETURN_STRING_ARRAY,
test: function_1.RETURN_BOOLEAN,
source: string_1.STRING,
global: boolean_1.BOOLEAN,
ignoreCase: boolean_1.BOOLEAN,
multiline: boolean_1.BOOLEAN,
lastIndex: number_1.NUMBER,
compile: function_1.RETURN_REGEXP,
flags: string_1.STRING,
sticky: boolean_1.BOOLEAN,
unicode: boolean_1.BOOLEAN,
dotAll: boolean_1.BOOLEAN,
hasIndices: boolean_1.BOOLEAN,
[Symbol.match]: null,
[Symbol.replace]: null,
[Symbol.search]: null,
[Symbol.split]: null,
[Symbol.matchAll]: null,
}));

View file

@ -0,0 +1,16 @@
import { TypeFunction } from "./function";
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
export declare class TypeSet implements ITypeClass {
type: "Set";
private readonly param0;
constructor(param0: () => TypeInfo | null);
has(type: NamedType | OtherTypeName): boolean;
paramType(index: number): TypeInfo | null;
propertyType(name: string): TypeInfo | null;
iterateType(): TypeInfo | null;
returnType(): null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}
export declare const UNKNOWN_SET: TypeSet;
export declare function buildSetConstructor(): TypeFunction;

View file

@ -0,0 +1,120 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UNKNOWN_SET = exports.TypeSet = void 0;
exports.buildSetConstructor = buildSetConstructor;
const util_1 = require("../../util");
const array_1 = require("./array");
const common_1 = require("./common");
const function_1 = require("./function");
const iterable_1 = require("./iterable");
const number_1 = require("./number");
const object_1 = require("./object");
const string_1 = require("./string");
const _1 = require(".");
const getPrototypes = (0, util_1.lazy)(() => {
const RETURN_SELF = new function_1.TypeFunction(function returnSelf(selfType) {
var _a;
return (_a = selfType === null || selfType === void 0 ? void 0 : selfType()) !== null && _a !== void 0 ? _a : null;
});
const RETURN_ENTRIES = new function_1.TypeFunction(function (selfType) {
return new iterable_1.TypeIterable(() => {
return new array_1.TypeArray(function* () {
const type = selfType === null || selfType === void 0 ? void 0 : selfType();
if ((0, _1.isTypeClass)(type)) {
yield type.iterateType();
yield type.iterateType();
}
else {
yield null;
yield null;
}
}, true);
});
});
const RETURN_KEYS = new function_1.TypeFunction(function (selfType) {
return new iterable_1.TypeIterable(() => {
const type = selfType === null || selfType === void 0 ? void 0 : selfType();
if ((0, _1.isTypeClass)(type)) {
return type.iterateType();
}
return null;
});
});
const RETURN_VALUES = new function_1.TypeFunction(function (selfType) {
return new iterable_1.TypeIterable(() => {
const type = selfType === null || selfType === void 0 ? void 0 : selfType();
if ((0, _1.isTypeClass)(type)) {
return type.iterateType();
}
return null;
});
});
return (0, common_1.createObject)({
...(0, object_1.getObjectPrototypes)(),
clear: function_1.RETURN_VOID,
delete: function_1.RETURN_BOOLEAN,
forEach: function_1.RETURN_VOID,
has: function_1.RETURN_BOOLEAN,
add: RETURN_SELF,
size: number_1.NUMBER,
entries: RETURN_ENTRIES,
keys: RETURN_KEYS,
values: RETURN_VALUES,
[Symbol.iterator]: null,
[Symbol.toStringTag]: string_1.STRING,
});
});
class TypeSet {
constructor(param0) {
this.type = "Set";
this.param0 = param0;
}
has(type) {
return type === "Set";
}
paramType(index) {
if (index === 0) {
return this.param0();
}
return null;
}
propertyType(name) {
return getPrototypes()[name] || null;
}
iterateType() {
return this.paramType(0);
}
returnType() {
return null;
}
typeNames() {
const param0 = (0, common_1.getTypeName)(this.iterateType());
return [`Set${param0 != null ? `<${param0}>` : ""}`];
}
equals(o) {
if (o.type !== "Set") {
return false;
}
return (0, common_1.isEquals)(this.iterateType(), o.iterateType());
}
}
exports.TypeSet = TypeSet;
exports.UNKNOWN_SET = new TypeSet(() => null);
function buildSetConstructor() {
const SET_TYPES = (0, common_1.createObject)({
prototype: null,
[Symbol.species]: null,
});
return new function_1.TypeGlobalFunction(setConstructor, SET_TYPES);
}
function setConstructor(_thisType, argTypes, meta) {
var _a;
if (!(meta === null || meta === void 0 ? void 0 : meta.isConstructor)) {
return null;
}
const arg = (_a = argTypes[0]) === null || _a === void 0 ? void 0 : _a.call(argTypes);
if ((0, _1.isTypeClass)(arg)) {
return new TypeSet(() => arg.iterateType());
}
return exports.UNKNOWN_SET;
}

View file

@ -0,0 +1,14 @@
import { TypeGlobalFunction } from "./function";
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
export declare class TypeString implements ITypeClass {
type: "String";
has(type: NamedType | OtherTypeName): boolean;
paramType(): null;
propertyType(name: string): TypeInfo | null;
iterateType(): TypeInfo;
returnType(): null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}
export declare const STRING: TypeString;
export declare function buildStringConstructor(): TypeGlobalFunction;

View file

@ -0,0 +1,103 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.STRING = exports.TypeString = void 0;
exports.buildStringConstructor = buildStringConstructor;
const util_1 = require("../../util");
const common_1 = require("./common");
const function_1 = require("./function");
const number_1 = require("./number");
const object_1 = require("./object");
class TypeString {
constructor() {
this.type = "String";
}
has(type) {
return type === "String";
}
paramType() {
return null;
}
propertyType(name) {
if (name === "0") {
return this;
}
return getPrototypes()[name] || null;
}
iterateType() {
return this;
}
returnType() {
return null;
}
typeNames() {
return ["String"];
}
equals(o) {
return o.type === "String";
}
}
exports.TypeString = TypeString;
exports.STRING = new TypeString();
function buildStringConstructor() {
const STRING_TYPES = (0, common_1.createObject)({
fromCharCode: function_1.RETURN_STRING,
fromCodePoint: function_1.RETURN_STRING,
raw: function_1.RETURN_STRING,
prototype: null,
});
return new function_1.TypeGlobalFunction(() => exports.STRING, STRING_TYPES);
}
const getPrototypes = (0, util_1.lazy)(() => (0, common_1.createObject)({
...(0, object_1.getObjectPrototypes)(),
toString: function_1.RETURN_STRING,
charAt: function_1.RETURN_STRING,
charCodeAt: function_1.RETURN_NUMBER,
concat: function_1.RETURN_STRING,
indexOf: function_1.RETURN_NUMBER,
lastIndexOf: function_1.RETURN_NUMBER,
localeCompare: function_1.RETURN_NUMBER,
match: function_1.RETURN_STRING_ARRAY,
replace: function_1.RETURN_STRING,
search: function_1.RETURN_NUMBER,
slice: function_1.RETURN_STRING,
split: function_1.RETURN_STRING_ARRAY,
substring: function_1.RETURN_STRING,
toLowerCase: function_1.RETURN_STRING,
toLocaleLowerCase: function_1.RETURN_STRING,
toUpperCase: function_1.RETURN_STRING,
toLocaleUpperCase: function_1.RETURN_STRING,
trim: function_1.RETURN_STRING,
substr: function_1.RETURN_STRING,
valueOf: function_1.RETURN_STRING,
codePointAt: function_1.RETURN_NUMBER,
includes: function_1.RETURN_BOOLEAN,
endsWith: function_1.RETURN_BOOLEAN,
normalize: function_1.RETURN_STRING,
repeat: function_1.RETURN_STRING,
startsWith: function_1.RETURN_BOOLEAN,
anchor: function_1.RETURN_STRING,
big: function_1.RETURN_STRING,
blink: function_1.RETURN_STRING,
bold: function_1.RETURN_STRING,
fixed: function_1.RETURN_STRING,
fontcolor: function_1.RETURN_STRING,
fontsize: function_1.RETURN_STRING,
italics: function_1.RETURN_STRING,
link: function_1.RETURN_STRING,
small: function_1.RETURN_STRING,
strike: function_1.RETURN_STRING,
sub: function_1.RETURN_STRING,
sup: function_1.RETURN_STRING,
padStart: function_1.RETURN_STRING,
padEnd: function_1.RETURN_STRING,
trimLeft: function_1.RETURN_STRING,
trimRight: function_1.RETURN_STRING,
trimStart: function_1.RETURN_STRING,
trimEnd: function_1.RETURN_STRING,
matchAll: null,
replaceAll: function_1.RETURN_STRING,
at: function_1.RETURN_STRING,
length: number_1.NUMBER,
0: exports.STRING,
[Symbol.iterator]: null,
}));

View file

@ -0,0 +1,14 @@
import type { ITypeClass, NamedType, OtherTypeName, TypeClass, TypeInfo } from ".";
export declare class TypeUnionOrIntersection implements ITypeClass {
type: "TypeUnionOrIntersection";
private readonly collection;
static buildType(generator?: () => IterableIterator<TypeInfo | null>): TypeInfo | null;
private constructor();
has(type: NamedType | OtherTypeName): boolean;
paramType(): null;
propertyType(name: string): TypeInfo | null;
iterateType(): TypeInfo | null;
returnType(thisType: (() => TypeInfo | null) | null, argTypes: ((() => TypeInfo | null) | null)[]): TypeInfo | null;
typeNames(): string[];
equals(o: TypeClass): boolean;
}

View file

@ -0,0 +1,87 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeUnionOrIntersection = void 0;
const common_1 = require("./common");
const _1 = require(".");
class TypeUnionOrIntersection {
static buildType(generator) {
const collection = new common_1.TypeCollection(generator);
if (collection.isOneType()) {
for (const t of collection.all()) {
return t;
}
return null;
}
return new TypeUnionOrIntersection(() => collection.all());
}
constructor(generator) {
this.type = "TypeUnionOrIntersection";
this.collection = new common_1.TypeCollection(generator);
}
has(type) {
return this.collection.has(type);
}
paramType() {
return null;
}
propertyType(name) {
const baseCollection = this.collection;
return TypeUnionOrIntersection.buildType(function* () {
for (const type of baseCollection.all()) {
const propType = (0, _1.isTypeClass)(type)
? type.propertyType(name)
: null;
if (propType) {
yield propType;
}
}
});
}
iterateType() {
const baseCollection = this.collection;
return TypeUnionOrIntersection.buildType(function* () {
for (const type of baseCollection.all()) {
if ((0, _1.isTypeClass)(type)) {
const itrType = type.iterateType();
if (itrType) {
yield itrType;
}
}
}
});
}
returnType(thisType, argTypes) {
const baseCollection = this.collection;
return TypeUnionOrIntersection.buildType(function* () {
for (const type of baseCollection.all()) {
if ((0, _1.isTypeClass)(type)) {
const itrType = type.returnType(thisType, argTypes);
if (itrType) {
yield itrType;
}
}
}
});
}
typeNames() {
return [...this.collection.strings()].sort();
}
equals(o) {
if (o.type !== "TypeUnionOrIntersection") {
return false;
}
const itr1 = this.collection.all();
const itr2 = o.collection.all();
let e1 = itr1.next();
let e2 = itr2.next();
while (!e1.done && !e2.done) {
if (!(0, common_1.isEquals)(e1.value, e2.value)) {
return false;
}
e1 = itr1.next();
e2 = itr2.next();
}
return e1.done === e2.done;
}
}
exports.TypeUnionOrIntersection = TypeUnionOrIntersection;

View file

@ -0,0 +1,5 @@
import type { Rule, Scope } from "eslint";
import type * as ES from "estree";
export declare function findVariable(context: Rule.RuleContext, node: ES.Identifier): Scope.Variable | null;
export declare function getPropertyName(context: Rule.RuleContext, node: ES.Property | ES.MemberExpression | ES.MethodDefinition): string | null;
export declare function isParenthesized(context: Rule.RuleContext, node: ES.Node): boolean;

View file

@ -0,0 +1,49 @@
"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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.findVariable = findVariable;
exports.getPropertyName = getPropertyName;
exports.isParenthesized = isParenthesized;
const astUtils = __importStar(require("../ast-utils"));
const eslintUtils = __importStar(require("@eslint-community/eslint-utils"));
function findVariable(context, node) {
return astUtils.findVariable(context, node);
}
function getPropertyName(context, node) {
return eslintUtils.getPropertyName(node, astUtils.getScope(context, node));
}
function isParenthesized(context, node) {
return eslintUtils.isParenthesized(node, context.sourceCode);
}

View file

@ -0,0 +1,14 @@
export declare class AliasMap {
private readonly toShortMap;
private readonly toLongMap;
constructor({ shortToLong, otherToLong, }: {
shortToLong: Record<string, string>;
otherToLong: Record<string, string>;
});
toShort(key: string): string;
toLong(key: string): string;
}
export declare const UNICODE_CATEGORY_ALIAS: AliasMap;
export declare const UNICODE_BINARY_PROPERTY_ALIAS: AliasMap;
export declare const UNICODE_GENERAL_CATEGORY_ALIAS: AliasMap;
export declare const UNICODE_SCRIPT_ALIAS: AliasMap;

View file

@ -0,0 +1,328 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UNICODE_SCRIPT_ALIAS = exports.UNICODE_GENERAL_CATEGORY_ALIAS = exports.UNICODE_BINARY_PROPERTY_ALIAS = exports.UNICODE_CATEGORY_ALIAS = exports.AliasMap = void 0;
class AliasMap {
constructor({ shortToLong, otherToLong, }) {
const toShortMap = new Map();
const toLongMap = new Map();
for (const [short, long] of Object.entries(shortToLong)) {
toShortMap.set(long, short);
toLongMap.set(short, long);
}
for (const [other, long] of Object.entries(otherToLong)) {
toLongMap.set(other, long);
const short = toShortMap.get(long);
if (!short) {
throw new Error(`No short key for ${long} with other key ${other}`);
}
toShortMap.set(other, short);
}
this.toShortMap = toShortMap;
this.toLongMap = toLongMap;
}
toShort(key) {
return this.toShortMap.get(key) || key;
}
toLong(key) {
return this.toLongMap.get(key) || key;
}
}
exports.AliasMap = AliasMap;
exports.UNICODE_CATEGORY_ALIAS = new AliasMap({
shortToLong: {
gc: "General_Category",
sc: "Script",
scx: "Script_Extensions",
},
otherToLong: {},
});
exports.UNICODE_BINARY_PROPERTY_ALIAS = new AliasMap({
shortToLong: {
AHex: "ASCII_Hex_Digit",
Alpha: "Alphabetic",
Bidi_C: "Bidi_Control",
Bidi_M: "Bidi_Mirrored",
CE: "Composition_Exclusion",
CI: "Case_Ignorable",
Comp_Ex: "Full_Composition_Exclusion",
CWCF: "Changes_When_Casefolded",
CWCM: "Changes_When_Casemapped",
CWKCF: "Changes_When_NFKC_Casefolded",
CWL: "Changes_When_Lowercased",
CWT: "Changes_When_Titlecased",
CWU: "Changes_When_Uppercased",
Dep: "Deprecated",
DI: "Default_Ignorable_Code_Point",
Dia: "Diacritic",
EBase: "Emoji_Modifier_Base",
EComp: "Emoji_Component",
EMod: "Emoji_Modifier",
EPres: "Emoji_Presentation",
Ext: "Extender",
ExtPict: "Extended_Pictographic",
Gr_Base: "Grapheme_Base",
Gr_Ext: "Grapheme_Extend",
Gr_Link: "Grapheme_Link",
Hex: "Hex_Digit",
IDC: "ID_Continue",
Ideo: "Ideographic",
IDS: "ID_Start",
IDSB: "IDS_Binary_Operator",
IDST: "IDS_Trinary_Operator",
IDSU: "IDS_Unary_Operator",
Join_C: "Join_Control",
LOE: "Logical_Order_Exception",
Lower: "Lowercase",
MCM: "Modifier_Combining_Mark",
NChar: "Noncharacter_Code_Point",
OAlpha: "Other_Alphabetic",
ODI: "Other_Default_Ignorable_Code_Point",
OGr_Ext: "Other_Grapheme_Extend",
OIDC: "Other_ID_Continue",
OIDS: "Other_ID_Start",
OLower: "Other_Lowercase",
OMath: "Other_Math",
OUpper: "Other_Uppercase",
Pat_Syn: "Pattern_Syntax",
Pat_WS: "Pattern_White_Space",
PCM: "Prepended_Concatenation_Mark",
QMark: "Quotation_Mark",
RI: "Regional_Indicator",
SD: "Soft_Dotted",
STerm: "Sentence_Terminal",
Term: "Terminal_Punctuation",
UIdeo: "Unified_Ideograph",
Upper: "Uppercase",
VS: "Variation_Selector",
WSpace: "White_Space",
XIDC: "XID_Continue",
XIDS: "XID_Start",
XO_NFC: "Expands_On_NFC",
XO_NFD: "Expands_On_NFD",
XO_NFKC: "Expands_On_NFKC",
XO_NFKD: "Expands_On_NFKD",
},
otherToLong: {
space: "White_Space",
},
});
exports.UNICODE_GENERAL_CATEGORY_ALIAS = new AliasMap({
shortToLong: {
C: "Other",
Cc: "Control",
Cf: "Format",
Cn: "Unassigned",
Co: "Private_Use",
Cs: "Surrogate",
L: "Letter",
LC: "Cased_Letter",
Ll: "Lowercase_Letter",
Lm: "Modifier_Letter",
Lo: "Other_Letter",
Lt: "Titlecase_Letter",
Lu: "Uppercase_Letter",
M: "Mark",
Mc: "Spacing_Mark",
Me: "Enclosing_Mark",
Mn: "Nonspacing_Mark",
N: "Number",
Nd: "Decimal_Number",
Nl: "Letter_Number",
No: "Other_Number",
P: "Punctuation",
Pc: "Connector_Punctuation",
Pd: "Dash_Punctuation",
Pe: "Close_Punctuation",
Pf: "Final_Punctuation",
Pi: "Initial_Punctuation",
Po: "Other_Punctuation",
Ps: "Open_Punctuation",
S: "Symbol",
Sc: "Currency_Symbol",
Sk: "Modifier_Symbol",
Sm: "Math_Symbol",
So: "Other_Symbol",
Z: "Separator",
Zl: "Line_Separator",
Zp: "Paragraph_Separator",
Zs: "Space_Separator",
},
otherToLong: {
cntrl: "Control",
Combining_Mark: "Mark",
digit: "Decimal_Number",
punct: "Punctuation",
},
});
exports.UNICODE_SCRIPT_ALIAS = new AliasMap({
shortToLong: {
Adlm: "Adlam",
Aghb: "Caucasian_Albanian",
Arab: "Arabic",
Armi: "Imperial_Aramaic",
Armn: "Armenian",
Avst: "Avestan",
Bali: "Balinese",
Bamu: "Bamum",
Bass: "Bassa_Vah",
Batk: "Batak",
Beng: "Bengali",
Bhks: "Bhaiksuki",
Bopo: "Bopomofo",
Brah: "Brahmi",
Brai: "Braille",
Bugi: "Buginese",
Buhd: "Buhid",
Cakm: "Chakma",
Cans: "Canadian_Aboriginal",
Cari: "Carian",
Cher: "Cherokee",
Chrs: "Chorasmian",
Copt: "Coptic",
Cpmn: "Cypro_Minoan",
Cprt: "Cypriot",
Cyrl: "Cyrillic",
Deva: "Devanagari",
Diak: "Dives_Akuru",
Dogr: "Dogra",
Dsrt: "Deseret",
Dupl: "Duployan",
Egyp: "Egyptian_Hieroglyphs",
Elba: "Elbasan",
Elym: "Elymaic",
Ethi: "Ethiopic",
Gara: "Garay",
Geor: "Georgian",
Glag: "Glagolitic",
Gong: "Gunjala_Gondi",
Gonm: "Masaram_Gondi",
Goth: "Gothic",
Gran: "Grantha",
Grek: "Greek",
Gujr: "Gujarati",
Gukh: "Gurung_Khema",
Guru: "Gurmukhi",
Hang: "Hangul",
Hani: "Han",
Hano: "Hanunoo",
Hatr: "Hatran",
Hebr: "Hebrew",
Hira: "Hiragana",
Hluw: "Anatolian_Hieroglyphs",
Hmng: "Pahawh_Hmong",
Hmnp: "Nyiakeng_Puachue_Hmong",
Hrkt: "Katakana_Or_Hiragana",
Hung: "Old_Hungarian",
Ital: "Old_Italic",
Java: "Javanese",
Kali: "Kayah_Li",
Kana: "Katakana",
Khar: "Kharoshthi",
Khmr: "Khmer",
Khoj: "Khojki",
Kits: "Khitan_Small_Script",
Knda: "Kannada",
Krai: "Kirat_Rai",
Kthi: "Kaithi",
Lana: "Tai_Tham",
Laoo: "Lao",
Latn: "Latin",
Lepc: "Lepcha",
Limb: "Limbu",
Lina: "Linear_A",
Linb: "Linear_B",
Lyci: "Lycian",
Lydi: "Lydian",
Mahj: "Mahajani",
Maka: "Makasar",
Mand: "Mandaic",
Mani: "Manichaean",
Marc: "Marchen",
Medf: "Medefaidrin",
Mend: "Mende_Kikakui",
Merc: "Meroitic_Cursive",
Mero: "Meroitic_Hieroglyphs",
Mlym: "Malayalam",
Mong: "Mongolian",
Mroo: "Mro",
Mtei: "Meetei_Mayek",
Mult: "Multani",
Mymr: "Myanmar",
Nagm: "Nag_Mundari",
Nand: "Nandinagari",
Narb: "Old_North_Arabian",
Nbat: "Nabataean",
Nkoo: "Nko",
Nshu: "Nushu",
Ogam: "Ogham",
Olck: "Ol_Chiki",
Onao: "Ol_Onal",
Orkh: "Old_Turkic",
Orya: "Oriya",
Osge: "Osage",
Osma: "Osmanya",
Ougr: "Old_Uyghur",
Palm: "Palmyrene",
Pauc: "Pau_Cin_Hau",
Perm: "Old_Permic",
Phag: "Phags_Pa",
Phli: "Inscriptional_Pahlavi",
Phlp: "Psalter_Pahlavi",
Phnx: "Phoenician",
Plrd: "Miao",
Prti: "Inscriptional_Parthian",
Rjng: "Rejang",
Rohg: "Hanifi_Rohingya",
Runr: "Runic",
Samr: "Samaritan",
Sarb: "Old_South_Arabian",
Saur: "Saurashtra",
Sgnw: "SignWriting",
Shaw: "Shavian",
Shrd: "Sharada",
Sidd: "Siddham",
Sind: "Khudawadi",
Sinh: "Sinhala",
Sogd: "Sogdian",
Sogo: "Old_Sogdian",
Sora: "Sora_Sompeng",
Soyo: "Soyombo",
Sund: "Sundanese",
Sunu: "Sunuwar",
Sylo: "Syloti_Nagri",
Syrc: "Syriac",
Tagb: "Tagbanwa",
Takr: "Takri",
Tale: "Tai_Le",
Talu: "New_Tai_Lue",
Taml: "Tamil",
Tang: "Tangut",
Tavt: "Tai_Viet",
Telu: "Telugu",
Tfng: "Tifinagh",
Tglg: "Tagalog",
Thaa: "Thaana",
Tibt: "Tibetan",
Tirh: "Tirhuta",
Tnsa: "Tangsa",
Todr: "Todhri",
Tutg: "Tulu_Tigalari",
Ugar: "Ugaritic",
Vaii: "Vai",
Vith: "Vithkuqi",
Wara: "Warang_Citi",
Wcho: "Wancho",
Xpeo: "Old_Persian",
Xsux: "Cuneiform",
Yezi: "Yezidi",
Yiii: "Yi",
Zanb: "Zanabazar_Square",
Zinh: "Inherited",
Zyyy: "Common",
Zzzz: "Unknown",
},
otherToLong: {
Qaac: "Coptic",
Qaai: "Inherited",
},
});

Some files were not shown because too many files have changed in this diff Show more