fetch_dependencies.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #!/usr/bin/env python3
  2. import re
  3. from pathlib import Path
  4. import requests
  5. from bs4 import BeautifulSoup
  6. import nicegui.elements.chart as highcharts
  7. USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36'
  8. PATH = Path('/tmp/nicegui_dependencies')
  9. PATH.mkdir(exist_ok=True)
  10. def url_to_filename(url: str) -> str:
  11. return re.sub(r'[^a-zA-Z0-9]', '_', url)
  12. def request_buffered_str(url: str) -> str:
  13. filepath = PATH / url_to_filename(url)
  14. if filepath.exists():
  15. return filepath.read_text()
  16. response = requests.get(url, headers={'User-Agent': USER_AGENT})
  17. filepath.write_text(response.text)
  18. return response.text
  19. def request_buffered(url: str) -> bytes:
  20. filepath = PATH / url_to_filename(url)
  21. if filepath.exists():
  22. return filepath.read_bytes()
  23. response = requests.get(url, headers={'User-Agent': USER_AGENT})
  24. filepath.write_bytes(response.content)
  25. return response.content
  26. # Google fonts
  27. url = 'https://fonts.googleapis.com/css2?family=Material+Icons&family=Roboto:wght@100;300;400;500;700;900'
  28. css = request_buffered_str(url)
  29. for font_url in re.findall(r'url\((.*?)\)', css):
  30. font = request_buffered(font_url)
  31. (Path('nicegui/static/fonts') / font_url.split('/')[-1]).write_bytes(font)
  32. css = css.replace('https://fonts.gstatic.com/s/materialicons/v140', 'fonts')
  33. css = css.replace('https://fonts.gstatic.com/s/roboto/v30', 'fonts')
  34. css = css.replace(' U+2122', '\n U+2122')
  35. css = css.replace("'", '"')
  36. Path('nicegui/static/fonts.css').write_text(css)
  37. # quasar.js
  38. url = 'https://cdn.jsdelivr.net/npm/quasar/dist/quasar.umd.prod.js'
  39. js = request_buffered_str(url)
  40. Path('nicegui/static/quasar.umd.prod.js').write_text(js)
  41. version = re.search(r'Quasar Framework v(\d+\.\d+\.\d+)', js).group(1)
  42. url = 'https://cdn.jsdelivr.net/npm/quasar/dist/quasar.prod.css'
  43. css = request_buffered_str(url)
  44. Path('nicegui/static/quasar.prod.css').write_text(css)
  45. print('Quasar:', version)
  46. # Quasar language packs
  47. url = 'https://cdn.jsdelivr.net/npm/quasar@2/dist/lang/'
  48. html = request_buffered_str(url)
  49. soup = BeautifulSoup(html, 'html.parser')
  50. languages = []
  51. for link in soup.find_all('a', href=re.compile(r'\.umd\.prod\.js$')):
  52. name = link.get('href').split('/')[-1]
  53. languages.append(name.split('.')[0])
  54. js = request_buffered_str(url + name)
  55. Path(f'nicegui/static/quasar.{name}').write_text(js)
  56. with open(Path(__file__).parent / 'nicegui' / 'language.py', 'w') as f:
  57. f.write(f'from typing_extensions import Literal\n\n')
  58. f.write(f'Language = Literal[\n')
  59. for language in languages:
  60. f.write(f" '{language}',\n")
  61. f.write(f']\n')
  62. # vue.js
  63. url = 'https://unpkg.com/vue@3/anything'
  64. info = request_buffered_str(url)
  65. version = re.search(r'Cannot find "/anything" in vue@(\d+\.\d+\.\d+)', info).group(1)
  66. url = 'https://unpkg.com/vue@3/dist/vue.global.prod.js'
  67. js = request_buffered_str(url)
  68. Path('nicegui/static/vue.global.prod.js').write_text(js)
  69. print('Vue:', version)
  70. # socket.io.js
  71. url = 'https://cdn.jsdelivr.net/npm/socket.io-client/dist/socket.io.min.js'
  72. js = request_buffered_str(url)
  73. Path('nicegui/static/socket.io.min.js').write_text(js)
  74. version = re.search(r'Socket.IO v(\d+\.\d+\.\d+)', js).group(1)
  75. print('Socket.io:', version)
  76. # tailwind.js
  77. url = 'https://cdn.tailwindcss.com/'
  78. js = request_buffered_str(url)
  79. Path('nicegui/static/tailwindcss.min.js').write_text(js)
  80. version = re.search(r'{name:"tailwindcss",version:"(\d+\.\d+\.\d+)"', js).group(1)
  81. print('Tailwind CSS:', version)
  82. # tween.js
  83. url = 'https://cdnjs.com/libraries/tween.js'
  84. html = request_buffered_str(url)
  85. soup = BeautifulSoup(html, 'html.parser')
  86. version = soup.find('span', class_='vs__selected').text.strip()
  87. url = f'https://cdnjs.cloudflare.com/ajax/libs/tween.js/{version}/tween.umd.min.js'
  88. js = request_buffered_str(url)
  89. Path('nicegui/elements/lib/tween.umd.min.js').write_text(js)
  90. print('Tween.js:', version)
  91. # plotly.js
  92. url = 'https://cdnjs.com/libraries/plotly.js'
  93. html = request_buffered_str(url)
  94. soup = BeautifulSoup(html, 'html.parser')
  95. version = soup.find('span', class_='vs__selected').text.strip()
  96. url = f'https://cdnjs.cloudflare.com/ajax/libs/plotly.js/{version}/plotly.min.js'
  97. js = request_buffered_str(url)
  98. Path('nicegui/elements/lib/plotly.min.js').write_text(js)
  99. print('Plotly.js:', version)
  100. # ag-grid.js
  101. url = 'https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js'
  102. js = request_buffered_str(url)
  103. Path('nicegui/elements/lib/ag-grid-community.min.js').write_text(js)
  104. version = re.search(r'@version v(\d+\.\d+\.\d+)', js).group(1)
  105. print('AG Grid:', version)
  106. # nipplejs.js
  107. url = 'https://www.npmjs.com/package/nipplejs'
  108. html = request_buffered_str(url)
  109. soup = BeautifulSoup(html, 'html.parser')
  110. version = soup.find('h3', string='Version').find_next_sibling('div').text.strip()
  111. url = f'https://cdn.jsdelivr.net/npm/nipplejs@{version}/dist/nipplejs.min.js'
  112. js = request_buffered_str(url)
  113. Path('nicegui/elements/lib/nipplejs.min.js').write_text(js)
  114. print('NippleJS:', version)
  115. # mermaid.min.js
  116. url = 'https://cdn.jsdelivr.net/npm/mermaid@9/dist/'
  117. html = request_buffered_str(url)
  118. soup = BeautifulSoup(html, 'html.parser')
  119. # find a with href starting with /npm/mermaid@
  120. version = soup.find('a', href=re.compile(r'^/npm/mermaid@')).text.strip().removeprefix('mermaid@')
  121. url = f'https://cdn.jsdelivr.net/npm/mermaid@{version}/dist/mermaid.min.js'
  122. js = request_buffered_str(url)
  123. Path('nicegui/elements/lib/mermaid.min.js').write_text(js)
  124. print('Mermaid:', version)
  125. # TODO: upgrade to Mermaid 10.0.x? (ESM only and potentially breaking changes)
  126. # highcharts.js
  127. for dependency in highcharts.dependencies:
  128. name = dependency.split('/')[-1]
  129. url = f'https://code.highcharts.com/{name}'
  130. js = request_buffered_str(url)
  131. Path(f'nicegui/elements/lib/{name}').write_text(js)
  132. v = re.search(r'Highcharts JS v(\d+\.\d+\.\d+)', js).group(1)
  133. if name == 'highcharts.js':
  134. version = v
  135. print('Highcharts:', version)
  136. else:
  137. assert version == v
  138. for dependency in highcharts.optional_dependencies:
  139. name = dependency.split('/')[-1]
  140. url = f'https://code.highcharts.com/modules/{name}'
  141. js = request_buffered_str(url)
  142. Path(f'nicegui/elements/lib/highcharts_modules/{name}').write_text(js)
  143. v = re.search(r'JS v(\d+\.\d+\.\d+)', js).group(1)
  144. assert version == v
  145. # three.js
  146. url = 'https://www.npmjs.com/package/three'
  147. html = request_buffered_str(url)
  148. soup = BeautifulSoup(html, 'html.parser')
  149. version = soup.find('h3', string='Version').find_next_sibling('div').text.strip()
  150. url = f'https://cdn.jsdelivr.net/npm/three@{version}/build/three.min.js'
  151. js = request_buffered_str(url)
  152. Path('nicegui/elements/lib/three.min.js').write_text(js)
  153. print('Three.js:', version)
  154. # TODO: using script JS files is not supported after version 0.160.0 --> use ES module instead
  155. # TODO: CSS2DRenderer.js, CSS3DRenderer.js, OrbitControls.js, STLLoader.js (require ES modules)