'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var core = require('@babel/core');
var helperPluginUtils = require('@babel/helper-plugin-utils');

function isNameOrLength(key) {
  if (core.types.isIdentifier(key)) {
    return key.name === "name" || key.name === "length";
  }
  if (core.types.isStringLiteral(key)) {
    return key.value === "name" || key.value === "length";
  }
  return false;
}
function isStaticFieldWithValue(node) {
  return (core.types.isClassProperty(node) || core.types.isClassPrivateProperty(node)) && node.static && !!node.value;
}
const hasReferenceVisitor = {
  ReferencedIdentifier(path, state) {
    if (path.node.name === state.name) {
      state.ref();
      path.stop();
    }
  },
  Scope(path, {
    name
  }) {
    if (path.scope.hasOwnBinding(name)) {
      path.skip();
    }
  }
};
function isReferenceOrThis(node, name) {
  return core.types.isThisExpression(node) || name && core.types.isIdentifier(node, {
    name
  });
}
const hasReferenceOrThisVisitor = {
  "ThisExpression|ReferencedIdentifier"(path, state) {
    if (isReferenceOrThis(path.node, state.name)) {
      state.ref();
      path.stop();
    }
  },
  FunctionParent(path, state) {
    if (path.isArrowFunctionExpression()) return;
    if (state.name && !path.scope.hasOwnBinding(state.name)) {
      path.traverse(hasReferenceVisitor, state);
    }
    path.skip();
    if (path.isMethod()) {
      if (path.requeueComputedKeyAndDecorators) {
        path.requeueComputedKeyAndDecorators();
      } else {
        require("@babel/traverse").NodePath.prototype.requeueComputedKeyAndDecorators.call(path);
      }
    }
  }
};
function getPotentiallyBuggyFieldsIndexes(path) {
  var _path$node$id;
  const buggyPublicStaticFieldsIndexes = [];
  let classReferenced = false;
  const className = (_path$node$id = path.node.id) == null ? void 0 : _path$node$id.name;
  const hasReferenceState = {
    name: className,
    ref: () => classReferenced = true
  };
  if (className) {
    for (const el of path.get("body.body")) {
      if (el.node.computed) {
        el.get("key").traverse(hasReferenceVisitor, hasReferenceState);
        if (classReferenced) break;
      }
    }
  }
  let nextPotentiallyBuggy = false;
  const {
    body
  } = path.node.body;
  for (let i = 0; i < body.length; i++) {
    const node = body[i];
    if (!nextPotentiallyBuggy) {
      if (core.types.isStaticBlock(node)) {
        classReferenced = true;
        nextPotentiallyBuggy = true;
      } else if (isStaticFieldWithValue(node)) {
        if (!classReferenced) {
          if (isReferenceOrThis(node.value, className)) {
            classReferenced = true;
          } else {
            path.get(`body.body.${i}.value`).traverse(hasReferenceOrThisVisitor, hasReferenceState);
          }
        }
        if (classReferenced) {
          nextPotentiallyBuggy = !path.scope.isPure(node.value);
        }
      }
    }
    if (core.types.isClassProperty(node, {
      static: true
    }) && (nextPotentiallyBuggy || node.computed || isNameOrLength(node.key))) {
      buggyPublicStaticFieldsIndexes.push(i);
    }
  }
  return buggyPublicStaticFieldsIndexes;
}
function getNameOrLengthStaticFieldsIndexes(path) {
  const indexes = [];
  const {
    body
  } = path.node.body;
  for (let i = 0; i < body.length; i++) {
    const node = body[i];
    if (core.types.isClassProperty(node, {
      static: true,
      computed: false
    }) && isNameOrLength(node.key)) {
      indexes.push(i);
    }
  }
  return indexes;
}
function toRanges(nums) {
  const ranges = [];
  if (nums.length === 0) return ranges;
  let start = nums[0];
  let end = start + 1;
  for (let i = 1; i < nums.length; i++) {
    if (nums[i] <= nums[i - 1]) {
      throw new Error("Internal Babel error: nums must be in ascending order");
    }
    if (nums[i] === end) {
      end++;
    } else {
      ranges.push([start, end]);
      start = nums[i];
      end = start + 1;
    }
  }
  ranges.push([start, end]);
  return ranges;
}

function buildFieldsReplacement(fields, scope, file) {
  return core.types.staticBlock(fields.map(field => {
    const key = field.computed || !core.types.isIdentifier(field.key) ? field.key : core.types.stringLiteral(field.key.name);
    return core.types.expressionStatement(core.types.callExpression(file.addHelper("defineProperty"), [core.types.thisExpression(), key, field.value || scope.buildUndefinedNode()]));
  }));
}
var index = helperPluginUtils.declare(api => {
  api.assertVersion("^7.0.0-0 || >8.0.0-alpha <8.0.0-beta");
  const setPublicClassFields = api.assumption("setPublicClassFields");
  return {
    name: "bugfix-v8-static-class-fields-redefine-readonly",
    visitor: {
      Class(path) {
        const ranges = toRanges(setPublicClassFields ? getNameOrLengthStaticFieldsIndexes(path) : getPotentiallyBuggyFieldsIndexes(path));
        for (let i = ranges.length - 1; i >= 0; i--) {
          const [start, end] = ranges[i];
          const startPath = path.get("body.body")[start];
          startPath.replaceWith(buildFieldsReplacement(path.node.body.body.slice(start, end), path.scope, this.file));
          for (let j = end - 1; j > start; j--) {
            path.get("body.body")[j].remove();
          }
        }
      }
    }
  };
});

exports.default = index;
//# sourceMappingURL=index.js.map