123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- // @flow
- "use strict";
- function getVariableDefinition(name /*: string */, scope /*: Scope */) {
- let currentScope = scope;
- do {
- const variable = currentScope.set.get(name);
- if (variable && variable.defs[0]) {
- return { scope: currentScope, definition: variable.defs[0] };
- }
- } while ((currentScope = currentScope.upper));
- }
- /*::
- type ReferenceOriginImport = { kind: "import", source: string, name: string };
- type ReferenceOriginParam = {
- kind: "export param",
- exportName: string,
- index: number,
- };
- type ReferenceOrigin =
- | ReferenceOriginImport
- | ReferenceOriginParam
- | { kind: "import *", source: string }
- | {
- kind: "property",
- base: ReferenceOriginImport | ReferenceOriginParam,
- path: string,
- };
- */
- // Given a node and a context, returns a description of where its value comes
- // from.
- // It resolves imports, parameters of exported functions and property accesses.
- // See the ReferenceOrigin type for more informations.
- function getReferenceOrigin(
- node /*: Node */,
- scope /*: Scope */
- ) /*: ?ReferenceOrigin */ {
- if (node.type === "Identifier") {
- const variable = getVariableDefinition(node.name, scope);
- if (!variable) return null;
- const definition = variable.definition;
- const defNode = definition.node;
- if (definition.type === "ImportBinding") {
- if (defNode.type === "ImportSpecifier") {
- return {
- kind: "import",
- source: definition.parent.source.value,
- name: defNode.imported.name,
- };
- }
- if (defNode.type === "ImportNamespaceSpecifier") {
- return {
- kind: "import *",
- source: definition.parent.source.value,
- };
- }
- }
- if (definition.type === "Variable" && defNode.init) {
- const origin = getReferenceOrigin(defNode.init, variable.scope);
- return origin && patternToProperty(definition.name, origin);
- }
- if (definition.type === "Parameter") {
- const parent = defNode.parent;
- let exportName /*: string */;
- if (parent.type === "ExportDefaultDeclaration") {
- exportName = "default";
- } else if (parent.type === "ExportNamedDeclaration") {
- exportName = defNode.id.name;
- } else if (
- parent.type === "AssignmentExpression" &&
- parent.left.type === "MemberExpression" &&
- parent.left.object.type === "Identifier" &&
- parent.left.object.name === "module" &&
- parent.left.property.type === "Identifier" &&
- parent.left.property.name === "exports"
- ) {
- exportName = "module.exports";
- } else {
- return null;
- }
- return patternToProperty(definition.name, {
- kind: "export param",
- exportName,
- index: definition.index,
- });
- }
- }
- if (node.type === "MemberExpression" && !node.computed) {
- const origin = getReferenceOrigin(node.object, scope);
- return origin && addProperty(origin, node.property.name);
- }
- return null;
- }
- function patternToProperty(
- id /*: Node */,
- base /*: ReferenceOrigin */
- ) /*: ?ReferenceOrigin */ {
- const path = getPatternPath(id);
- return path && path.reduce(addProperty, base);
- }
- // Adds a property to a given origin. If it was a namespace import it becomes
- // a named import, so that `import * as x from "foo"; x.bar` and
- // `import { bar } from "foo"` have the same origin.
- function addProperty(
- origin /*: ReferenceOrigin */,
- name /*: string */
- ) /* ReferenceOrigin */ {
- if (origin.kind === "import *") {
- return {
- kind: "import",
- source: origin.source,
- name,
- };
- }
- if (origin.kind === "property") {
- return {
- kind: "property",
- base: origin.base,
- path: origin.path + "." + name,
- };
- }
- return {
- kind: "property",
- base: origin,
- path: name,
- };
- }
- // if "node" is c of { a: { b: c } }, the result is ["a","b"]
- function getPatternPath(node /*: Node */) /*: ?string[] */ {
- let current = node;
- const path = [];
- // Unshift keys to path while going up
- do {
- const property = current.parent;
- if (
- property.type === "ArrayPattern" ||
- property.type === "AssignmentPattern" ||
- property.computed
- ) {
- // These nodes are not supported.
- return null;
- }
- if (property.type === "Property") {
- path.unshift(property.key.name);
- } else {
- // The destructuring pattern is finished
- break;
- }
- } while ((current = current.parent.parent));
- return path;
- }
- function reportError(context /*: Context */, node /*: Node */) {
- const isMemberExpression = node.type === "MemberExpression";
- const id = isMemberExpression ? node.property : node;
- context.report({
- node: id,
- message: `t.${id.name}() is deprecated. Use t.cloneNode() instead.`,
- fix(fixer) {
- if (isMemberExpression) {
- return fixer.replaceText(id, "cloneNode");
- }
- },
- });
- }
- module.exports = {
- meta: {
- schema: [],
- fixable: "code",
- },
- create(context /*: Context */) {
- return {
- CallExpression(node /*: Node */) {
- const origin = getReferenceOrigin(node.callee, context.getScope());
- if (!origin) return;
- if (
- origin.kind === "import" &&
- (origin.name === "clone" || origin.name === "cloneDeep") &&
- origin.source === "@babel/types"
- ) {
- // imported from @babel/types
- return reportError(context, node.callee);
- }
- if (
- origin.kind === "property" &&
- (origin.path === "clone" || origin.path === "cloneDeep") &&
- origin.base.kind === "import" &&
- origin.base.name === "types" &&
- origin.base.source === "@babel/core"
- ) {
- // imported from @babel/core
- return reportError(context, node.callee);
- }
- if (
- origin.kind === "property" &&
- (origin.path === "types.clone" ||
- origin.path === "types.cloneDeep") &&
- origin.base.kind === "export param" &&
- (origin.base.exportName === "default" ||
- origin.base.exportName === "module.exports") &&
- origin.base.index === 0
- ) {
- // export default function ({ types: t }) {}
- // module.exports = function ({ types: t }) {}
- return reportError(context, node.callee);
- }
- },
- };
- },
- };
- /*:: // ESLint types
- type Node = { type: string, [string]: any };
- type Definition = {
- type: "ImportedBinding",
- name: Node,
- node: Node,
- parent: Node,
- };
- type Variable = {
- defs: Definition[],
- };
- type Scope = {
- set: Map<string, Variable>,
- upper: ?Scope,
- };
- type Context = {
- report(options: {
- node: Node,
- message: string,
- fix?: (fixer: Fixer) => ?Fixer,
- }): void,
- getScope(): Scope,
- };
- type Fixer = {
- replaceText(node: Node, replacement: string): Fixer,
- };
- */
|