"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
const error_1 = require("../error");
/**
* Espree Adapter
* @extends Adapter
*/
class EspreeAdapter {
// get node source
getSource(node, options) {
const source = this.fileContent(node).slice(node.start, node.end);
if (options && options.fixIndent) {
const column = this.getIndent(node);
return source
.split("\n")
.map((line, index) => {
if (index === 0 || line === "") {
return line;
}
else {
const index = line.search(/\S|$/);
return index < column ? line.slice(index) : line.slice(column);
}
})
.join("\n");
}
else {
return source;
}
}
/**
* Get the source code of current file.
* @returns {string} source code of current file.
*/
fileContent(node) {
return fs_1.default.readFileSync(node.loc.source, "utf-8");
}
/**
* Get the source range of child node.
* @param {Node} node - The node.
* @param {string} childName - The name to find child node.
* @returns {Object} The range of the child node, e.g. { start: 0, end: 10 }
* @throws {NotSupportedError} if we can't get the range.
* @example
* const node = espree.parse("function foobar(foo, bar) {}")
* childNodeRange(node, "id") // { start: "function ".length, end: "function foobar".length }
*
* // node array
* const node = espree.parse("function foobar(foo, bar) {}")
* childNodeRange(node, "params") // { start: "function foobar".length, end: "function foobar(foo, bar)".length }
*
* // index for node array
* const node = espree.parse("function foobar(foo, bar) {}")
* childNodeRange(node, "params.1") // { start: "function foobar(foo, ".length, end: "function foobar(foo, bar".length }
*
* // {name}Property for node who has properties
* const node = espree.parse('const foobar = { foo: "foo", bar: "bar" }')
* childNodeRange(node, "declarations.0.init.fooProperty") // { start: "const foobar = { ".length, end: "const foobar = { foo".length }
*
* // {name}Value for node who has properties
* const node = espree.parse('const foobar = { foo: "foo", bar: "bar" }')
* childNodeRange(node, 'declarations.0.init.fooValue')) // { start: "const foobar = { foo: ".length, end: "const foobar = { foo: "foo".length }
*
* // {name}Attribute for node who has attributes
* const node = espree.parse('<Field name="email" autoComplete="email" />')
* childNodeRange(node, "expression.openingElement.autoCompleteAttribute") // { start: '<Field name="email" '.length, end: '<Field name="email" autoComplete="email"'.length }
*
* // async for MethodDefinition node
* const node = espree.parse("async foobar() {}")
* childNodeRange(node, "async") // { start: 0, end: "async".length }
*
* // dot for MemberExpression node
* const node = espree.parse("foo.bar")
* childNodeRange(node, "dot") // { start: "foo".length, end: "foo.".length }
*
* // class for ClassDeclaration node
* const node = espree.parse("class FooBar {}")
* childNodeRange(node, "class") // { start: 0, end: "class".length }
*
* // semicolon for Property node
* const node = espree.parse("{ foo: bar }");
* childNodeRange(node, "semicolon") // { start: "{ foo", end: "{ foo:".length }
*/
childNodeRange(node, childName) {
if (node.type === "MethodDefinition" && childName === "async") {
return { start: node.start, end: node.key.start };
}
else if (node.type === "MemberExpression" && childName === "dot") {
return {
start: node.property.start - 1,
end: node.property.start,
};
}
else if (["MemberExpression", "CallExpression"].includes(node.type) &&
childName === "arguments") {
if (node.arguments && node.arguments.length > 0) {
return {
start: node.arguments[0].start - 1,
end: node.arguments[node.arguments.length - 1].end + 1,
};
}
else {
return { start: node.end - 2, end: node.end };
}
}
else if (node.type === "ClassDeclaration" && childName === "class") {
return { start: node.start, end: node.start + 5 };
}
else if (["FunctionDeclaration", "FunctionExpression"].includes(node.type) && childName === "params") {
if (node.params && node.params.length > 0) {
return {
start: node.params[0].start - 1,
end: node.params[node.params.length - 1].end + 1,
};
}
else {
return { start: node.end - 2, end: node.end };
}
}
else if (node.type === "ImportDeclaration" &&
childName === "specifiers") {
return {
start: node.start + this.getSource(node).indexOf("{"),
end: node.start + this.getSource(node).indexOf("}") + 1,
};
}
else if (node.type === "Property" && childName === "semicolon") {
return {
start: node.key.end,
end: node.key.end + 1,
};
}
else if (node.hasOwnProperty("properties") && childName.endsWith("Property")) {
const property = node["properties"].find(property => this.getSource(property.key) == childName.slice(0, -"Property".length));
return { start: property.start, end: property.end };
}
else if (node.hasOwnProperty("attributes") && childName.endsWith("Attribute")) {
const attribute = node["attributes"].find(attribute => this.getSource(attribute.name) == childName.slice(0, -"Attribute".length));
return { start: attribute.start, end: attribute.end };
}
else if (node.hasOwnProperty("properties") && childName.endsWith("Value")) {
const property = node["properties"].find(property => this.getSource(property.key) == childName.slice(0, -"Value".length));
return { start: property["value"].start, end: property["value"].end };
}
else {
const [directChildName, ...nestedChildName] = childName.split(".");
if (node[directChildName]) {
const childNode = node[directChildName];
if (Array.isArray(childNode)) {
const [childDirectChildName, ...childNestedChildName] = nestedChildName;
if (childNestedChildName.length > 0) {
return this.childNodeRange(childNode[childDirectChildName], childNestedChildName.join("."));
}
if (typeof childNode[childDirectChildName] === "function") {
const childChildNode = childNode[childDirectChildName].bind(childNode);
return { start: childChildNode().start, end: childChildNode().end };
}
else if (!Number.isNaN(childDirectChildName)) {
const childChildNode = childNode.at(Number.parseInt(childDirectChildName));
if (childChildNode) {
return { start: childChildNode.start, end: childChildNode.end };
}
else {
// arguments.0 for func()
return { start: node.end - 1, end: node.end - 1 };
}
}
else {
throw new error_1.NotSupportedError(`${childName} is not supported for ${this.getSource(node)}`);
}
}
if (nestedChildName.length > 0) {
return this.childNodeRange(childNode, nestedChildName.join("."));
}
if (childNode) {
return { start: childNode.start, end: childNode.end };
}
}
}
throw new error_1.NotSupportedError(`${childName} is not supported for ${this.getSource(node)}`);
}
/**
* Get the value of child node.
* @param {Node} node - The node to evaluate.
* @param {string} childName - The name to find child node.
* @returns {any} The value of child node, it can be a node, an array, a string or a number.
* @example
* const node = espree.parse("foobar(foo, bar)")
* childNodeValue(node, "expression.arguments.0") // node["expression"]["arguments"][0]
*
* // node array
* const node = espree.parse("foobar(foo, bar)")
* childNodeValue(node, "expression.arguments") // node["expression"]["arguments"]
*
* // {name}Property for node who has properties
* const node = espree.parse('const foobar = { foo: "foo", bar: "bar" }')
* childNodeValue(node, "declarations.0.init.fooProperty") // node["declarations"][0]["init"]["properties"][0]
*
* // {name}Value for node who has properties
* const node = espree.parse('const foobar = { foo: "foo", bar: "bar" }')
* childNodeValue(node, 'declarations.0.init.fooValue')) // node["declarations"][0]["init"]["properties"][0]["value"]
*
* // {name}Attribute for node who has attributes
* const node = espree.parse('<Field name="email" autoComplete="email" />')
* childNodeValue(node, "expression.openingElement.autoCompleteAttribute") // node["expression"]["openingElement"]["attributes"][1]
*/
childNodeValue(node, childName) {
return this.actualValue(node, childName.split("."));
}
getStart(node, childName) {
if (childName) {
node = this.childNodeValue(node, childName);
}
return node.start;
}
getEnd(node, childName) {
if (childName) {
node = this.childNodeValue(node, childName);
}
return node.end;
}
getStartLoc(node, childName) {
if (childName) {
node = this.childNodeValue(node, childName);
}
const { line, column } = node.loc.start;
return { line, column };
}
getEndLoc(node, childName) {
if (childName) {
node = this.childNodeValue(node, childName);
}
const { line, column } = node.loc.end;
return { line, column };
}
getIndent(node) {
return this.fileContent(node)
.split("\n")[this.getStartLoc(node).line - 1].search(/\S|$/);
}
actualValue(node, multiKeys) {
let childNode = node;
multiKeys.forEach((key) => {
if (!childNode)
return;
if (childNode.hasOwnProperty(key)) {
childNode = childNode[key];
}
else if (Array.isArray(childNode) && /-?\d+/.test(key)) {
childNode = childNode.at(Number.parseInt(key));
}
else if (childNode.hasOwnProperty("properties") && key.endsWith("Property")) {
childNode = childNode.properties.find(property => this.getSource(property.key) == key.slice(0, -"Property".length));
}
else if (childNode.hasOwnProperty("attributes") && key.endsWith("Attribute")) {
childNode = childNode.attributes.find(attribute => this.getSource(attribute.name) == key.slice(0, -"Attribute".length));
}
else if (childNode.hasOwnProperty("properties") && key.endsWith("Value")) {
const property = childNode.properties.find(property => this.getSource(property.key) == key.slice(0, -"Value".length));
childNode = property["value"];
}
else if (typeof childNode[key] === "function") {
childNode = childNode[key].call(childNode);
}
else {
throw `${key} is not supported for ${this.getSource(childNode)}`;
}
});
return childNode;
}
}
exports.default = EspreeAdapter;