This change modifies the build scripts to use --legacy-peer-deps flag when installing npm dependencies to resolve potential peer dependency conflicts during the build process. The change is applied to both bash (build.sh) and batch (build.bat) build scripts.
332 lines
11 KiB
JavaScript
332 lines
11 KiB
JavaScript
/**
|
|
* @fileoverview A rule to disallow using `this`/`super` before `super()`.
|
|
* @author Toru Nagashima
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Requirements
|
|
//------------------------------------------------------------------------------
|
|
|
|
const astUtils = require("./utils/ast-utils");
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Helpers
|
|
//------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Checks whether or not a given node is a constructor.
|
|
* @param {ASTNode} node A node to check. This node type is one of
|
|
* `Program`, `FunctionDeclaration`, `FunctionExpression`, and
|
|
* `ArrowFunctionExpression`.
|
|
* @returns {boolean} `true` if the node is a constructor.
|
|
*/
|
|
function isConstructorFunction(node) {
|
|
return (
|
|
node.type === "FunctionExpression" &&
|
|
node.parent.type === "MethodDefinition" &&
|
|
node.parent.kind === "constructor"
|
|
);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Rule Definition
|
|
//------------------------------------------------------------------------------
|
|
|
|
/** @type {import('../shared/types').Rule} */
|
|
module.exports = {
|
|
meta: {
|
|
type: "problem",
|
|
|
|
docs: {
|
|
description: "Disallow `this`/`super` before calling `super()` in constructors",
|
|
recommended: true,
|
|
url: "https://eslint.org/docs/latest/rules/no-this-before-super"
|
|
},
|
|
|
|
schema: [],
|
|
|
|
messages: {
|
|
noBeforeSuper: "'{{kind}}' is not allowed before 'super()'."
|
|
}
|
|
},
|
|
|
|
create(context) {
|
|
|
|
/*
|
|
* Information for each constructor.
|
|
* - upper: Information of the upper constructor.
|
|
* - hasExtends: A flag which shows whether the owner class has a valid
|
|
* `extends` part.
|
|
* - scope: The scope of the owner class.
|
|
* - codePath: The code path of this constructor.
|
|
*/
|
|
let funcInfo = null;
|
|
|
|
/*
|
|
* Information for each code path segment.
|
|
* Each key is the id of a code path segment.
|
|
* Each value is an object:
|
|
* - superCalled: The flag which shows `super()` called in all code paths.
|
|
* - invalidNodes: The array of invalid ThisExpression and Super nodes.
|
|
*/
|
|
let segInfoMap = Object.create(null);
|
|
|
|
/**
|
|
* Gets whether or not `super()` is called in a given code path segment.
|
|
* @param {CodePathSegment} segment A code path segment to get.
|
|
* @returns {boolean} `true` if `super()` is called.
|
|
*/
|
|
function isCalled(segment) {
|
|
return !segment.reachable || segInfoMap[segment.id].superCalled;
|
|
}
|
|
|
|
/**
|
|
* Checks whether or not this is in a constructor.
|
|
* @returns {boolean} `true` if this is in a constructor.
|
|
*/
|
|
function isInConstructorOfDerivedClass() {
|
|
return Boolean(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends);
|
|
}
|
|
|
|
/**
|
|
* Determines if every segment in a set has been called.
|
|
* @param {Set<CodePathSegment>} segments The segments to search.
|
|
* @returns {boolean} True if every segment has been called; false otherwise.
|
|
*/
|
|
function isEverySegmentCalled(segments) {
|
|
for (const segment of segments) {
|
|
if (!isCalled(segment)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks whether or not this is before `super()` is called.
|
|
* @returns {boolean} `true` if this is before `super()` is called.
|
|
*/
|
|
function isBeforeCallOfSuper() {
|
|
return (
|
|
isInConstructorOfDerivedClass() &&
|
|
!isEverySegmentCalled(funcInfo.currentSegments)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Sets a given node as invalid.
|
|
* @param {ASTNode} node A node to set as invalid. This is one of
|
|
* a ThisExpression and a Super.
|
|
* @returns {void}
|
|
*/
|
|
function setInvalid(node) {
|
|
const segments = funcInfo.currentSegments;
|
|
|
|
for (const segment of segments) {
|
|
if (segment.reachable) {
|
|
segInfoMap[segment.id].invalidNodes.push(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the current segment as `super` was called.
|
|
* @returns {void}
|
|
*/
|
|
function setSuperCalled() {
|
|
const segments = funcInfo.currentSegments;
|
|
|
|
for (const segment of segments) {
|
|
if (segment.reachable) {
|
|
segInfoMap[segment.id].superCalled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
|
|
/**
|
|
* Adds information of a constructor into the stack.
|
|
* @param {CodePath} codePath A code path which was started.
|
|
* @param {ASTNode} node The current node.
|
|
* @returns {void}
|
|
*/
|
|
onCodePathStart(codePath, node) {
|
|
if (isConstructorFunction(node)) {
|
|
|
|
// Class > ClassBody > MethodDefinition > FunctionExpression
|
|
const classNode = node.parent.parent.parent;
|
|
|
|
funcInfo = {
|
|
upper: funcInfo,
|
|
isConstructor: true,
|
|
hasExtends: Boolean(
|
|
classNode.superClass &&
|
|
!astUtils.isNullOrUndefined(classNode.superClass)
|
|
),
|
|
codePath,
|
|
currentSegments: new Set()
|
|
};
|
|
} else {
|
|
funcInfo = {
|
|
upper: funcInfo,
|
|
isConstructor: false,
|
|
hasExtends: false,
|
|
codePath,
|
|
currentSegments: new Set()
|
|
};
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Removes the top of stack item.
|
|
*
|
|
* And this traverses all segments of this code path then reports every
|
|
* invalid node.
|
|
* @param {CodePath} codePath A code path which was ended.
|
|
* @returns {void}
|
|
*/
|
|
onCodePathEnd(codePath) {
|
|
const isDerivedClass = funcInfo.hasExtends;
|
|
|
|
funcInfo = funcInfo.upper;
|
|
if (!isDerivedClass) {
|
|
return;
|
|
}
|
|
|
|
codePath.traverseSegments((segment, controller) => {
|
|
const info = segInfoMap[segment.id];
|
|
|
|
for (let i = 0; i < info.invalidNodes.length; ++i) {
|
|
const invalidNode = info.invalidNodes[i];
|
|
|
|
context.report({
|
|
messageId: "noBeforeSuper",
|
|
node: invalidNode,
|
|
data: {
|
|
kind: invalidNode.type === "Super" ? "super" : "this"
|
|
}
|
|
});
|
|
}
|
|
|
|
if (info.superCalled) {
|
|
controller.skip();
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Initialize information of a given code path segment.
|
|
* @param {CodePathSegment} segment A code path segment to initialize.
|
|
* @returns {void}
|
|
*/
|
|
onCodePathSegmentStart(segment) {
|
|
funcInfo.currentSegments.add(segment);
|
|
|
|
if (!isInConstructorOfDerivedClass()) {
|
|
return;
|
|
}
|
|
|
|
// Initialize info.
|
|
segInfoMap[segment.id] = {
|
|
superCalled: (
|
|
segment.prevSegments.length > 0 &&
|
|
segment.prevSegments.every(isCalled)
|
|
),
|
|
invalidNodes: []
|
|
};
|
|
},
|
|
|
|
onUnreachableCodePathSegmentStart(segment) {
|
|
funcInfo.currentSegments.add(segment);
|
|
},
|
|
|
|
onUnreachableCodePathSegmentEnd(segment) {
|
|
funcInfo.currentSegments.delete(segment);
|
|
},
|
|
|
|
onCodePathSegmentEnd(segment) {
|
|
funcInfo.currentSegments.delete(segment);
|
|
},
|
|
|
|
/**
|
|
* Update information of the code path segment when a code path was
|
|
* looped.
|
|
* @param {CodePathSegment} fromSegment The code path segment of the
|
|
* end of a loop.
|
|
* @param {CodePathSegment} toSegment A code path segment of the head
|
|
* of a loop.
|
|
* @returns {void}
|
|
*/
|
|
onCodePathSegmentLoop(fromSegment, toSegment) {
|
|
if (!isInConstructorOfDerivedClass()) {
|
|
return;
|
|
}
|
|
|
|
// Update information inside of the loop.
|
|
funcInfo.codePath.traverseSegments(
|
|
{ first: toSegment, last: fromSegment },
|
|
(segment, controller) => {
|
|
const info = segInfoMap[segment.id];
|
|
|
|
if (info.superCalled) {
|
|
info.invalidNodes = [];
|
|
controller.skip();
|
|
} else if (
|
|
segment.prevSegments.length > 0 &&
|
|
segment.prevSegments.every(isCalled)
|
|
) {
|
|
info.superCalled = true;
|
|
info.invalidNodes = [];
|
|
}
|
|
}
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Reports if this is before `super()`.
|
|
* @param {ASTNode} node A target node.
|
|
* @returns {void}
|
|
*/
|
|
ThisExpression(node) {
|
|
if (isBeforeCallOfSuper()) {
|
|
setInvalid(node);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Reports if this is before `super()`.
|
|
* @param {ASTNode} node A target node.
|
|
* @returns {void}
|
|
*/
|
|
Super(node) {
|
|
if (!astUtils.isCallee(node) && isBeforeCallOfSuper()) {
|
|
setInvalid(node);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Marks `super()` called.
|
|
* @param {ASTNode} node A target node.
|
|
* @returns {void}
|
|
*/
|
|
"CallExpression:exit"(node) {
|
|
if (node.callee.type === "Super" && isBeforeCallOfSuper()) {
|
|
setSuperCalled();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Resets state.
|
|
* @returns {void}
|
|
*/
|
|
"Program:exit"() {
|
|
segInfoMap = Object.create(null);
|
|
}
|
|
};
|
|
}
|
|
};
|