function removePureRenderMixin()

in transforms/pure-render-mixin.js [11:177]


function removePureRenderMixin(file, api, options) {
  const j = api.jscodeshift;

  require('./utils/array-polyfills');
  const ReactUtils = require('./utils/ReactUtils')(j);

  const printOptions = options.printOptions || {
    quote: 'single',
    trailingComma: true
  };
  const root = j(file.source);

  const PURE_RENDER_MIXIN = options['mixin-name'] || 'PureRenderMixin';
  const SHOULD_COMPONENT_UPDATE = 'shouldComponentUpdate';
  const NEXT_PROPS = 'nextProps';
  const NEXT_STATE = 'nextState';

  // ---------------------------------------------------------------------------
  // shouldComponentUpdate
  const createShouldComponentUpdateFunction = () =>
    j.functionExpression(
      null,
      [j.identifier(NEXT_PROPS), j.identifier(NEXT_STATE)],
      j.blockStatement([
        j.returnStatement(
          j.callExpression(
            j.memberExpression(
              j.identifier('React'),
              j.memberExpression(
                j.identifier('addons'),
                j.identifier('shallowCompare'),
                false
              ),
              false
            ),
            [
              j.thisExpression(),
              j.identifier(NEXT_PROPS),
              j.identifier(NEXT_STATE)
            ]
          )
        )
      ])
    );

  const createShouldComponentUpdateProperty = () =>
    j.property(
      'init',
      j.identifier(SHOULD_COMPONENT_UPDATE),
      createShouldComponentUpdateFunction()
    );

  const hasShouldComponentUpdate = classPath =>
    ReactUtils.getReactCreateClassSpec(classPath).properties.every(
      property => property.key.name !== SHOULD_COMPONENT_UPDATE
    );

  // ---------------------------------------------------------------------------
  // Mixin related code
  const isPureRenderMixin = node =>
    node.type === 'Identifier' && node.name === PURE_RENDER_MIXIN;

  const hasPureRenderMixin = classPath => {
    const spec = ReactUtils.getReactCreateClassSpec(classPath);
    const mixin = spec && spec.properties.find(ReactUtils.isMixinProperty);
    return mixin && mixin.value.elements.some(isPureRenderMixin);
  };

  const removeMixin = elements =>
    j.property(
      'init',
      j.identifier('mixins'),
      j.arrayExpression(elements.filter(element => !isPureRenderMixin(element)))
    );

  // ---------------------------------------------------------------------------
  // Boom!
  const insertShouldComponentUpdate = properties => {
    const length = properties.length;
    const lastProp = properties[length - 1];
    // I wouldn't dare insert at the bottom if the last function is render
    if (lastProp.key.type === 'Identifier' && lastProp.key.name === 'render') {
      properties.splice(
        length - 1,
        1,
        createShouldComponentUpdateProperty(),
        lastProp
      );
    } else {
      properties.push(createShouldComponentUpdateProperty());
    }
    return properties;
  };

  const cleanupReactComponent = classPath => {
    const spec = ReactUtils.getReactCreateClassSpec(classPath);
    const properties = spec.properties
      .map(property => {
        if (ReactUtils.isMixinProperty(property)) {
          const elements = property.value.elements;
          return elements.length !== 1 ? removeMixin(elements) : null;
        }
        return property;
      })
      .filter(property => !!property);

    ReactUtils.findReactCreateClassCallExpression(classPath).replaceWith(
      ReactUtils.createCreateReactClassCallExpression(
        insertShouldComponentUpdate(properties)
      )
    );
  };

  // Remove it if only two or fewer are left:
  // var PureRenderMixin = React.addons.PureRenderMixin;
  const hasPureRenderIdentifiers = path =>
    path
      .find(j.Identifier, {
        name: PURE_RENDER_MIXIN
      })
      .size() > 2;

  const deletePureRenderMixin = path => {
    if (hasPureRenderIdentifiers(path)) {
      return;
    }

    const declaration = path
      .findVariableDeclarators(PURE_RENDER_MIXIN)
      .closest(j.VariableDeclaration);

    if (declaration.size > 1) {
      declaration.forEach(p =>
        j(p).replaceWith(
          j.variableDeclaration(
            'var',
            p.value.declarations.filter(isPureRenderMixin)
          )
        )
      );
    } else {
      // Let's assume the variable declaration happens at the top level
      const program = declaration.closest(j.Program).get();
      const body = program.value.body;
      const index = body.indexOf(declaration.get().value);
      if (index !== -1) {
        body.splice(index, 1);
      }
    }
  };

  if (options['explicit-require'] === false || ReactUtils.hasReact(root)) {
    const didTransform =
      ReactUtils.findReactCreateClass(root)
        .filter(hasPureRenderMixin)
        .filter(hasShouldComponentUpdate)
        .forEach(cleanupReactComponent)
        .size() > 0;

    if (didTransform) {
      deletePureRenderMixin(root);
      return root.toSource(printOptions);
    }
  }

  return null;
}