imports.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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 the lib is an absolute path, we need to prefix it with a $
  21. lib = (
  22. "$" + lib
  23. if lib.startswith(("/utils/", "/components/", "/styles/", "/public/"))
  24. else lib
  25. )
  26. if isinstance(fields, (list, tuple, set)):
  27. all_imports[lib].extend(
  28. (
  29. ImportVar(field) if isinstance(field, str) else field
  30. for field in fields
  31. )
  32. )
  33. else:
  34. all_imports[lib].append(
  35. ImportVar(fields) if isinstance(fields, str) else fields
  36. )
  37. return all_imports
  38. def parse_imports(imports: ImportDict | ParsedImportDict) -> ParsedImportDict:
  39. """Parse the import dict into a standard format.
  40. Args:
  41. imports: The import dict to parse.
  42. Returns:
  43. The parsed import dict.
  44. """
  45. def _make_list(value: ImportTypes) -> list[str | ImportVar] | list[ImportVar]:
  46. if isinstance(value, (str, ImportVar)):
  47. return [value]
  48. return value
  49. return {
  50. package: [
  51. ImportVar(tag=tag) if isinstance(tag, str) else tag
  52. for tag in _make_list(maybe_tags)
  53. ]
  54. for package, maybe_tags in imports.items()
  55. }
  56. def collapse_imports(
  57. imports: ParsedImportDict | ImmutableParsedImportDict,
  58. ) -> ParsedImportDict:
  59. """Remove all duplicate ImportVar within an ImportDict.
  60. Args:
  61. imports: The import dict to collapse.
  62. Returns:
  63. The collapsed import dict.
  64. """
  65. return {
  66. lib: (
  67. list(set(import_vars))
  68. if isinstance(import_vars, list)
  69. else list(import_vars)
  70. )
  71. for lib, import_vars in (
  72. imports if isinstance(imports, tuple) else imports.items()
  73. )
  74. }
  75. @dataclasses.dataclass(frozen=True)
  76. class ImportVar:
  77. """An import var."""
  78. # The name of the import tag.
  79. tag: Optional[str]
  80. # whether the import is default or named.
  81. is_default: Optional[bool] = False
  82. # The tag alias.
  83. alias: Optional[str] = None
  84. # Whether this import need to install the associated lib
  85. install: Optional[bool] = True
  86. # whether this import should be rendered or not
  87. render: Optional[bool] = True
  88. # The path of the package to import from.
  89. package_path: str = "/"
  90. # whether this import package should be added to transpilePackages in next.config.js
  91. # https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages
  92. transpile: Optional[bool] = False
  93. @property
  94. def name(self) -> str:
  95. """The name of the import.
  96. Returns:
  97. The name(tag name with alias) of tag.
  98. """
  99. if self.alias:
  100. return (
  101. self.alias if self.is_default else " as ".join([self.tag, self.alias]) # pyright: ignore [reportCallIssue,reportArgumentType]
  102. )
  103. else:
  104. return self.tag or ""
  105. ImportTypes = Union[str, ImportVar, List[Union[str, ImportVar]], List[ImportVar]]
  106. ImportDict = Dict[str, ImportTypes]
  107. ParsedImportDict = Dict[str, List[ImportVar]]
  108. ImmutableParsedImportDict = Tuple[Tuple[str, Tuple[ImportVar, ...]], ...]