imports.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. """Import operations."""
  2. from __future__ import annotations
  3. import dataclasses
  4. from collections import defaultdict
  5. from typing import DefaultDict, Dict, List, Optional, Tuple, Union
  6. def merge_imports(
  7. *imports: ImportDict | ParsedImportDict | ImmutableParsedImportDict,
  8. ) -> ParsedImportDict:
  9. """Merge multiple import dicts together.
  10. Args:
  11. *imports: The list of import dicts to merge.
  12. Returns:
  13. The merged import dicts.
  14. """
  15. all_imports: DefaultDict[str, List[ImportVar]] = defaultdict(list)
  16. for import_dict in imports:
  17. for lib, fields in (
  18. import_dict if isinstance(import_dict, tuple) else import_dict.items()
  19. ):
  20. if isinstance(fields, (list, tuple, set)):
  21. all_imports[lib].extend(
  22. (
  23. ImportVar(field) if isinstance(field, str) else field
  24. for field in fields
  25. )
  26. )
  27. else:
  28. all_imports[lib].append(
  29. ImportVar(fields) if isinstance(fields, str) else fields
  30. )
  31. return all_imports
  32. def parse_imports(imports: ImportDict | ParsedImportDict) -> ParsedImportDict:
  33. """Parse the import dict into a standard format.
  34. Args:
  35. imports: The import dict to parse.
  36. Returns:
  37. The parsed import dict.
  38. """
  39. def _make_list(value: ImportTypes) -> list[str | ImportVar] | list[ImportVar]:
  40. if isinstance(value, (str, ImportVar)):
  41. return [value]
  42. return value
  43. return {
  44. package: [
  45. ImportVar(tag=tag) if isinstance(tag, str) else tag
  46. for tag in _make_list(maybe_tags)
  47. ]
  48. for package, maybe_tags in imports.items()
  49. }
  50. def collapse_imports(
  51. imports: ParsedImportDict | ImmutableParsedImportDict,
  52. ) -> ParsedImportDict:
  53. """Remove all duplicate ImportVar within an ImportDict.
  54. Args:
  55. imports: The import dict to collapse.
  56. Returns:
  57. The collapsed import dict.
  58. """
  59. return {
  60. lib: (
  61. list(set(import_vars))
  62. if isinstance(import_vars, list)
  63. else list(import_vars)
  64. )
  65. for lib, import_vars in (
  66. imports if isinstance(imports, tuple) else imports.items()
  67. )
  68. }
  69. @dataclasses.dataclass(order=True, frozen=True)
  70. class ImportVar:
  71. """An import var."""
  72. # The name of the import tag.
  73. tag: Optional[str]
  74. # whether the import is default or named.
  75. is_default: Optional[bool] = False
  76. # The tag alias.
  77. alias: Optional[str] = None
  78. # Whether this import need to install the associated lib
  79. install: Optional[bool] = True
  80. # whether this import should be rendered or not
  81. render: Optional[bool] = True
  82. # whether this import package should be added to transpilePackages in next.config.js
  83. # https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages
  84. transpile: Optional[bool] = False
  85. @property
  86. def name(self) -> str:
  87. """The name of the import.
  88. Returns:
  89. The name(tag name with alias) of tag.
  90. """
  91. if self.alias:
  92. return (
  93. self.alias if self.is_default else " as ".join([self.tag, self.alias]) # type: ignore
  94. )
  95. else:
  96. return self.tag or ""
  97. ImportTypes = Union[str, ImportVar, List[Union[str, ImportVar]], List[ImportVar]]
  98. ImportDict = Dict[str, ImportTypes]
  99. ParsedImportDict = Dict[str, List[ImportVar]]
  100. ImmutableParsedImportDict = Tuple[Tuple[str, Tuple[ImportVar, ...]], ...]