pankod/refine

View on GitHub
packages/codemod/src/transformations/v4/fix-v4-deprecations.ts

Summary

Maintainability
F
4 days
Test Coverage
import {
  JSCodeshift,
  Collection,
  ObjectExpression,
  Property,
  Identifier,
  ObjectProperty,
  BooleanLiteral,
  JSXAttribute,
  JSXExpressionContainer,
  ArrayExpression,
} from "jscodeshift";

export const parser = "tsx";

const configToSpreadConfig = (j: JSCodeshift, source: Collection) => {
  const useListHooks = source.find(j.CallExpression, {
    callee: {
      name: "useList",
    },
  });

  useListHooks.replaceWith((p) => {
    const configProperty = (
      p.node.arguments[0] as unknown as ObjectExpression
    ).properties.find((p: Property) => (p.key as Identifier).name === "config");

    if (!configProperty) {
      return p.node;
    }

    const propertiesWithoutConfig = (
      p.node.arguments[0] as unknown as ObjectExpression
    ).properties.filter(
      (p: Property) => (p.key as Identifier).name !== "config",
    );

    const configProperties = (
      (configProperty as ObjectProperty).value as ObjectExpression
    ).properties;

    p.node.arguments = [
      j.objectExpression([...propertiesWithoutConfig, ...configProperties]),
    ];

    return p.node;
  });
};

const sortToSorters = (j: JSCodeshift, source: Collection) => {
  const willCheckHooks = [
    "useCheckboxGroup",
    "useRadioGroup",
    "useSelect",
    "useAutocomplete",
    "useList",
  ];

  willCheckHooks.forEach((hookName) => {
    const useListHooks = source.find(j.CallExpression, {
      callee: {
        name: hookName,
      },
    });

    useListHooks.replaceWith((p) => {
      if (p.node.arguments.length === 0) {
        return p.node;
      }

      const sortProperty = (
        p.node.arguments[0] as unknown as ObjectExpression
      ).properties.find((p: Property) => (p.key as Identifier).name === "sort");

      if (!sortProperty) {
        return p.node;
      }

      const propertiesWithoutSort = (
        p.node.arguments[0] as unknown as ObjectExpression
      ).properties.filter(
        (p: Property) => (p.key as Identifier).name !== "sort",
      );

      p.node.arguments = [
        j.objectExpression([
          ...propertiesWithoutSort,
          j.objectProperty(
            j.identifier("sorters"),
            (sortProperty as any).value,
          ),
        ]),
      ];

      return p.node;
    });
  });
};

const sorterToSorters = (j: JSCodeshift, source: Collection) => {
  const willCheckHooks = ["useExport"];

  willCheckHooks.forEach((hookName) => {
    const useListHooks = source.find(j.CallExpression, {
      callee: {
        name: hookName,
      },
    });

    useListHooks.replaceWith((p) => {
      if (p.node.arguments.length === 0) {
        return p.node;
      }

      const sortProperty = (
        p.node.arguments[0] as unknown as ObjectExpression
      ).properties.find(
        (p: Property) => (p.key as Identifier).name === "sorter",
      );

      if (!sortProperty) {
        return p.node;
      }

      const propertiesWithoutSort = (
        p.node.arguments[0] as unknown as ObjectExpression
      ).properties.filter(
        (p: Property) => (p.key as Identifier).name !== "sorter",
      );

      p.node.arguments = [
        j.objectExpression([
          ...propertiesWithoutSort,
          j.objectProperty(
            j.identifier("sorters"),
            (sortProperty as any).value,
          ),
        ]),
      ];

      return p.node;
    });
  });
};

const resourceNametoResource = (j: JSCodeshift, source: Collection) => {
  const willCheckHooks = ["useExport", "useImport"];

  willCheckHooks.forEach((hookName) => {
    const useListHooks = source.find(j.CallExpression, {
      callee: {
        name: hookName,
      },
    });

    useListHooks.replaceWith((p) => {
      if (p.node.arguments.length === 0) {
        return p.node;
      }

      const resourceNameProperty = (
        p.node.arguments[0] as unknown as ObjectExpression
      ).properties.find(
        (p: Property) => (p.key as Identifier).name === "resourceName",
      );

      if (!resourceNameProperty) {
        return p.node;
      }

      const propertiesWithoutResourceName = (
        p.node.arguments[0] as unknown as ObjectExpression
      ).properties.filter(
        (p: Property) => (p.key as Identifier).name !== "resourceName",
      );

      p.node.arguments = [
        j.objectExpression([
          ...propertiesWithoutResourceName,
          j.objectProperty(
            j.identifier("resource"),
            (resourceNameProperty as any).value,
          ),
        ]),
      ];

      return p.node;
    });
  });
};

const deprecatedUseTablePaginationProps = [
  "initialCurrent",
  "initialPageSize",
  "hasPagination",
];

const deprecatedUseTableFiltersProps = [
  "initialFilter",
  "permanentFilter",
  "defaultSetFilterBehavior",
];

const deprecatedUseTableSortersProps = ["initialSorter", "permanentSorter"];

const fixDeprecatedReactTableProps = (j: JSCodeshift, source: Collection) => {
  const refineReactTableImports = source.find(j.ImportDeclaration, {
    source: {
      value: "@pankod/refine-react-table",
    },
    specifiers: [
      {
        imported: {
          name: "useTable",
        },
      },
    ],
  });

  if (refineReactTableImports.length === 0) {
    return;
  }

  const useTableHooks = source.find(j.CallExpression, {
    callee: {
      name: "useTable",
    },
  });

  useTableHooks.replaceWith((p) => {
    if (p.node.arguments.length === 0) {
      return p.node;
    }

    const hasRefineCoreProps = (
      p.node.arguments[0] as ObjectExpression
    ).properties.find(
      (p: Property) => (p.key as Identifier).name === "refineCoreProps",
    );

    if (!hasRefineCoreProps) {
      return p.node;
    }

    const otherProperties = (
      p.node.arguments[0] as ObjectExpression
    ).properties.filter(
      (p: Property) => (p.key as Identifier).name !== "refineCoreProps",
    );

    const paginationProperties = deprecatedUseTablePaginationProps.map(
      (prop) => {
        const property = (
          (hasRefineCoreProps as ObjectProperty).value as ObjectExpression
        ).properties.find((p: Property) => (p.key as Identifier).name === prop);

        if (!property) {
          return;
        }

        if (prop === "hasPagination") {
          return j.property(
            "init",
            j.identifier("mode"),
            j.literal(
              ((property as ObjectProperty).value as BooleanLiteral).value
                ? "server"
                : "off",
            ),
          );
        }

        if (prop === "initialCurrent") {
          return j.property(
            "init",
            j.identifier("current"),
            (property as ObjectProperty).value,
          );
        }

        if (prop === "initialPageSize") {
          return j.property(
            "init",
            j.identifier("pageSize"),
            (property as ObjectProperty).value,
          );
        }

        return;
      },
    );

    const paginationProperty = j.property(
      "init",
      j.identifier("pagination"),
      j.objectExpression(paginationProperties.filter(Boolean)),
    );

    const filtersProperties = deprecatedUseTableFiltersProps.map((prop) => {
      const property = (
        (hasRefineCoreProps as ObjectProperty).value as ObjectExpression
      ).properties.find((p: Property) => (p.key as Identifier).name === prop);

      if (!property) {
        return;
      }

      if (prop === "initialFilter") {
        return j.property(
          "init",
          j.identifier("initial"),
          (property as ObjectProperty).value,
        );
      }

      if (prop === "permanentFilter") {
        return j.property(
          "init",
          j.identifier("permanent"),
          (property as ObjectProperty).value,
        );
      }

      if (prop === "defaultSetFilterBehavior") {
        return j.property(
          "init",
          j.identifier("defaultBehavior"),
          (property as ObjectProperty).value,
        );
      }

      return;
    });

    const filtersProperty = j.property(
      "init",
      j.identifier("filters"),
      j.objectExpression(filtersProperties.filter(Boolean)),
    );

    const sortersProperties = deprecatedUseTableSortersProps.map((prop) => {
      const property = (
        (hasRefineCoreProps as ObjectProperty).value as ObjectExpression
      ).properties.find((p: Property) => (p.key as Identifier).name === prop);

      if (!property) {
        return;
      }

      if (prop === "initialSorter") {
        return j.property(
          "init",
          j.identifier("initial"),
          (property as ObjectProperty).value,
        );
      }

      if (prop === "permanentSorter") {
        return j.property(
          "init",
          j.identifier("permanent"),
          (property as ObjectProperty).value,
        );
      }

      return;
    });

    const sortersProperty = j.property(
      "init",
      j.identifier("sorters"),
      j.objectExpression(sortersProperties.filter(Boolean)),
    );

    const otherRefineCoreProps = (
      (hasRefineCoreProps as ObjectProperty).value as ObjectExpression
    ).properties.filter(
      (p: Property) =>
        ![
          ...deprecatedUseTablePaginationProps,
          ...deprecatedUseTableSortersProps,
          ...deprecatedUseTableFiltersProps,
        ].includes((p.key as Identifier).name),
    );

    const refineCorePropsProperty = j.property(
      "init",
      j.identifier("refineCoreProps"),
      j.objectExpression(
        [
          ...otherRefineCoreProps,
          (paginationProperty.value as ObjectExpression).properties.length > 0
            ? paginationProperty
            : null,
          (filtersProperty.value as ObjectExpression).properties.length > 0
            ? filtersProperty
            : null,
          (sortersProperty.value as ObjectExpression).properties.length > 0
            ? sortersProperty
            : null,
        ].filter(Boolean),
      ),
    );

    p.node.arguments = [
      j.objectExpression([...otherProperties, refineCorePropsProperty]),
    ];

    return p.node;
  });
};

const fixDeprecatedUseTableProps = (j: JSCodeshift, source: Collection) => {
  const willCheckImports = ["useTable", "useDataGrid"];

  willCheckImports.forEach((hook) => {
    const useTableHooks = source.find(j.CallExpression, {
      callee: {
        name: hook,
      },
    });

    useTableHooks.replaceWith((p) => {
      if (p.node.arguments.length === 0) {
        return p.node;
      }

      const otherProperties = (
        p.node.arguments[0] as ObjectExpression
      ).properties.filter(
        (p: Property) =>
          ![
            ...deprecatedUseTablePaginationProps,
            ...deprecatedUseTableSortersProps,
            ...deprecatedUseTableFiltersProps,
          ].includes((p.key as Identifier).name),
      );

      const paginationProperties = deprecatedUseTablePaginationProps.map(
        (prop) => {
          const property = (
            p.node.arguments[0] as ObjectExpression
          ).properties.find(
            (p: Property) => (p.key as Identifier).name === prop,
          );

          if (!property) {
            return;
          }

          if (prop === "hasPagination") {
            return j.property(
              "init",
              j.identifier("mode"),
              j.literal(
                ((property as ObjectProperty).value as BooleanLiteral).value
                  ? "server"
                  : "off",
              ),
            );
          }

          if (prop === "initialCurrent") {
            return j.property(
              "init",
              j.identifier("current"),
              (property as ObjectProperty).value,
            );
          }

          if (prop === "initialPageSize") {
            return j.property(
              "init",
              j.identifier("pageSize"),
              (property as ObjectProperty).value,
            );
          }

          return;
        },
      );

      const paginationProperty = j.property(
        "init",
        j.identifier("pagination"),
        j.objectExpression(paginationProperties.filter(Boolean)),
      );

      const filtersProperties = deprecatedUseTableFiltersProps.map((prop) => {
        const property = (
          p.node.arguments[0] as ObjectExpression
        ).properties.find((p: Property) => (p.key as Identifier).name === prop);

        if (!property) {
          return;
        }

        if (prop === "initialFilter") {
          return j.property(
            "init",
            j.identifier("initial"),
            (property as ObjectProperty).value,
          );
        }

        if (prop === "permanentFilter") {
          return j.property(
            "init",
            j.identifier("permanent"),
            (property as ObjectProperty).value,
          );
        }

        if (prop === "defaultSetFilterBehavior") {
          return j.property(
            "init",
            j.identifier("defaultBehavior"),
            (property as ObjectProperty).value,
          );
        }

        return;
      });

      const filtersProperty = j.property(
        "init",
        j.identifier("filters"),
        j.objectExpression(filtersProperties.filter(Boolean)),
      );

      const sortersProperties = deprecatedUseTableSortersProps.map((prop) => {
        const property = (
          p.node.arguments[0] as ObjectExpression
        ).properties.find((p: Property) => (p.key as Identifier).name === prop);

        if (!property) {
          return;
        }

        if (prop === "initialSorter") {
          return j.property(
            "init",
            j.identifier("initial"),
            (property as ObjectProperty).value,
          );
        }

        if (prop === "permanentSorter") {
          return j.property(
            "init",
            j.identifier("permanent"),
            (property as ObjectProperty).value,
          );
        }

        return;
      });

      const sortersProperty = j.property(
        "init",
        j.identifier("sorters"),
        j.objectExpression(sortersProperties.filter(Boolean)),
      );

      p.node.arguments = [
        j.objectExpression(
          [
            ...otherProperties,
            (paginationProperty.value as ObjectExpression).properties.length > 0
              ? paginationProperty
              : null,
            (filtersProperty.value as ObjectExpression).properties.length > 0
              ? filtersProperty
              : null,
            (sortersProperty.value as ObjectExpression).properties.length > 0
              ? sortersProperty
              : null,
          ].filter(Boolean),
        ),
      ];

      return p.node;
    });
  });
};

const fixUseListHasPaginationToPaginationMode = (
  j: JSCodeshift,
  source: Collection,
) => {
  const useListHooks = source.find(j.CallExpression, {
    callee: {
      name: "useList",
    },
  });

  useListHooks.replaceWith((p) => {
    if (p.node.arguments.length === 0) {
      return p.node;
    }

    const hasPaginationProperty = (
      p.node.arguments[0] as ObjectExpression
    ).properties.find(
      (p: Property) => (p.key as Identifier).name === "hasPagination",
    );

    if (!hasPaginationProperty) {
      return p.node;
    }

    const paginationProperty = (
      p.node.arguments[0] as ObjectExpression
    ).properties.find(
      (p: Property) => (p.key as Identifier).name === "pagination",
    );

    if (paginationProperty) {
      (paginationProperty as any).value.properties.push(
        j.property(
          "init",
          j.identifier("mode"),
          j.literal(
            ((hasPaginationProperty as ObjectProperty).value as BooleanLiteral)
              .value
              ? "server"
              : "off",
          ),
        ),
      );
    } else {
      (p.node.arguments[0] as ObjectExpression).properties.push(
        j.property(
          "init",
          j.identifier("pagination"),
          j.objectExpression([
            j.property(
              "init",
              j.identifier("mode"),
              j.literal(
                (
                  (hasPaginationProperty as ObjectProperty)
                    .value as BooleanLiteral
                ).value
                  ? "server"
                  : "off",
              ),
            ),
          ]),
        ),
      );
    }

    (p.node.arguments[0] as ObjectExpression).properties = (
      p.node.arguments[0] as ObjectExpression
    ).properties.filter(
      (p: Property) => (p.key as Identifier).name !== "hasPagination",
    );

    return p.node;
  });
};

const fixUseSelectHasPaginationToPaginationMode = (
  j: JSCodeshift,
  source: Collection,
) => {
  const useSelectHooks = source.find(j.CallExpression, {
    callee: {
      name: "useSelect",
    },
  });

  useSelectHooks.replaceWith((p) => {
    const hasPaginationProperty = (
      p.node.arguments[0] as ObjectExpression
    ).properties.find(
      (p: Property) => (p.key as Identifier).name === "hasPagination",
    );

    const paginationProperty = (
      p.node.arguments[0] as ObjectExpression
    ).properties.find(
      (p: Property) => (p.key as Identifier).name === "pagination",
    );

    const hasMode = (
      paginationProperty as unknown as any
    )?.value?.properties?.find((p) => p["name"] === "mode");

    if (hasPaginationProperty && !hasMode) {
      if (paginationProperty) {
        (paginationProperty as any).value.properties.push(
          j.property(
            "init",
            j.identifier("mode"),
            j.literal(
              (
                (hasPaginationProperty as ObjectProperty)
                  .value as BooleanLiteral
              ).value
                ? "server"
                : "off",
            ),
          ),
        );
      } else {
        (p.node.arguments[0] as ObjectExpression).properties.push(
          j.property(
            "init",
            j.identifier("pagination"),
            j.objectExpression([
              j.property(
                "init",
                j.identifier("mode"),
                j.literal(
                  (
                    (hasPaginationProperty as ObjectProperty)
                      .value as BooleanLiteral
                  ).value
                    ? "server"
                    : "off",
                ),
              ),
            ]),
          ),
        );
      }
    }

    if (!hasPaginationProperty && !hasMode) {
      if (paginationProperty) {
        (paginationProperty as any).value.properties.push(
          j.property("init", j.identifier("mode"), j.stringLiteral("server")),
        );
      } else {
        (p.node.arguments[0] as ObjectExpression).properties.push(
          j.property(
            "init",
            j.identifier("pagination"),
            j.objectExpression([
              j.property(
                "init",
                j.identifier("mode"),
                j.stringLiteral("server"),
              ),
            ]),
          ),
        );
      }
    }

    (p.node.arguments[0] as ObjectExpression).properties = (
      p.node.arguments[0] as ObjectExpression
    ).properties.filter(
      (p: Property) => (p.key as Identifier).name !== "hasPagination",
    );

    return p.node;
  });
};

const useCustomConfigSortToSorters = (j: JSCodeshift, source: Collection) => {
  const useCustomHooks = source.find(j.CallExpression, {
    callee: {
      name: "useCustom",
    },
  });

  useCustomHooks.replaceWith((p) => {
    if (p.node.arguments.length === 0) {
      return p.node;
    }

    const configProperty = (
      p.node.arguments[0] as ObjectExpression
    ).properties.find((p: Property) => (p.key as Identifier).name === "config");

    if (!configProperty) {
      return p.node;
    }

    const sortProperty = (
      (configProperty as ObjectProperty).value as any
    ).properties.find((p: Property) => (p.key as Identifier).name === "sort");

    if (!sortProperty) {
      return p.node;
    }

    ((configProperty as ObjectProperty).value as any).properties.push(
      j.property(
        "init",
        j.identifier("sorters"),
        (sortProperty as ObjectProperty).value,
      ),
    );

    ((configProperty as ObjectProperty).value as any).properties = (
      (configProperty as ObjectProperty).value as any
    ).properties.filter((p: Property) => (p.key as Identifier).name !== "sort");

    return p.node;
  });
};

const setSortertoSetSorters = (j: JSCodeshift, source: Collection) => {
  const willCheckHooks = ["useTable", "useDataGrid"];

  willCheckHooks.forEach((hook) => {
    const updatedHooks = source.find(j.CallExpression, {
      callee: {
        name: hook,
      },
    });

    if (updatedHooks.length === 0) {
      return;
    }

    updatedHooks.forEach((path) => {
      const setSorterProperty = path.parentPath.node.id.properties.find(
        (p) => p.value.name === "setSorter",
      );

      if (setSorterProperty) {
        setSorterProperty.value.name = "setSorters: setSorter";
      }

      const sorterPropery = path.parentPath.node.id.properties.find(
        (p) => p.value.name === "sorter",
      );

      if (sorterPropery) {
        sorterPropery.value.name = "sorters: sorter";
      }
    });
  });
};

const addCommentToUseSimpleList = (j: JSCodeshift, source: Collection) => {
  const useSimpleListHooks = source.find(j.CallExpression, {
    callee: {
      name: "useSimpleList",
    },
  });

  useSimpleListHooks.forEach((path) => {
    const comment = j.commentLine(
      "`useSimpleList` does not accept all of Ant Design's `List` component props anymore. You can directly use `List` component instead.",
      false,
      true,
    );

    path.parentPath.insertBefore(comment);
  });
};

const resourceOptionstoMeta = (j: JSCodeshift, source: Collection) => {
  const refineElement = source.find(j.JSXElement, {
    openingElement: {
      name: {
        name: "Refine",
      },
    },
  });

  if (refineElement.length === 0) {
    return;
  }

  refineElement.forEach((path) => {
    const resources = path.node.openingElement.attributes.find(
      (p) => (p as JSXAttribute).name?.name === "resources",
    );

    if (!resources) {
      return;
    }

    const options = (
      ((resources as JSXAttribute).value as JSXExpressionContainer)
        .expression as ArrayExpression
    ).elements.filter((p) => {
      const properties = (p as ObjectExpression).properties;

      return (
        properties.find(
          (p) => ((p as ObjectProperty).key as Identifier).name === "options",
        ) !== undefined
      );
    });

    if (options.length === 0) {
      return;
    }

    options.forEach((p) => {
      const properties = (p as ObjectExpression).properties;

      const optionsProperty = properties.find(
        (p) => ((p as ObjectProperty).key as Identifier).name === "options",
      );

      if (!optionsProperty) {
        return;
      }

      (optionsProperty as ObjectProperty).key = j.identifier("meta");
    });
  });
};

export const fixV4Deprecations = async (j: JSCodeshift, source: Collection) => {
  configToSpreadConfig(j, source);
  sortToSorters(j, source);
  sorterToSorters(j, source);
  resourceNametoResource(j, source);
  fixDeprecatedReactTableProps(j, source);
  fixDeprecatedUseTableProps(j, source);
  fixUseListHasPaginationToPaginationMode(j, source);
  fixUseSelectHasPaginationToPaginationMode(j, source);
  useCustomConfigSortToSorters(j, source);
  setSortertoSetSorters(j, source);
  addCommentToUseSimpleList(j, source);
  resourceOptionstoMeta(j, source);
};