npm.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. """
  2. Update dependencies according to npm.json configurations using the NPM packagist.
  3. npm.json file is a JSON object key => dependency.
  4. - key: is the key name of the dependency. It will be the folder name where the dependency will be stored.
  5. - dependency: a JSON object key-pair value with the following meaning full keys:
  6. - package (optional): if provided, this is the NPM package name. Otherwise, key is used as an NPM package name.
  7. - version (optional): if provided, this will fix the version to use. Otherwise, the latest available NPM package version will be used.
  8. - destination: the destination folder where the dependency should end up.
  9. - keep: an array of regexp of files to keep within the downloaded NPM package.
  10. - rename: an array of rename rules (string replace). Used to change the package structure after download to match NiceGUI expectations.
  11. """
  12. import json
  13. import tarfile
  14. from pathlib import Path
  15. import urllib.request
  16. import re
  17. def delete_folder(path):
  18. if not path.exists():
  19. return path
  20. for sub in path.iterdir():
  21. if sub.is_dir():
  22. delete_folder(sub)
  23. else:
  24. sub.unlink()
  25. path.rmdir()
  26. return path
  27. def ensure(path):
  28. path.parent.mkdir(parents=True, exist_ok=True)
  29. return path
  30. # Create a hidden folder to work in.
  31. tmp = Path('.npm')
  32. delete_folder(tmp).mkdir()
  33. with open('npm.json') as file:
  34. dependencies = json.load(file)
  35. for key in dependencies:
  36. dependency = dependencies[key]
  37. # Reset destination folder.
  38. destination = Path('nicegui', dependency['destination'], key)
  39. delete_folder(destination).mkdir()
  40. # ---
  41. # Handle the special case of tailwind. Hopefully remove this soon.
  42. if 'download' in dependency:
  43. USER_AGENT = 'Mozilla/5.0'
  44. request = urllib.request.Request(dependency['download'], headers={'User-Agent': USER_AGENT})
  45. with urllib.request.urlopen(request) as resource:
  46. content = resource.read().decode()
  47. version = resource.geturl().rsplit('/', 1)[-1]
  48. print(f"tailwind: {version} - https://cdn.tailwindcss.com")
  49. with Path(destination, dependency['rename']).open('w') as dest:
  50. dest.write(content)
  51. continue
  52. # ---
  53. package = dependency['package'] if 'package' in dependency else key
  54. npm_url = f"https://registry.npmjs.org/{package}"
  55. with urllib.request.urlopen(npm_url) as npm:
  56. # Get package info from npm.
  57. npm_data = json.load(npm)
  58. npm_version = dependency['version'] if 'version' in dependency else npm_data['dist-tags']['latest']
  59. npm_tarball = npm_data['versions'][npm_version]['dist']['tarball']
  60. print(f"{key}: {npm_version} - {npm_tarball}")
  61. # Download and extract.
  62. Path(tmp, key).mkdir()
  63. urllib.request.urlretrieve(npm_tarball, Path(tmp, key, f"{key}.tgz"))
  64. with tarfile.open(Path(tmp, key, f"{key}.tgz")) as archive:
  65. to_be_extracted = []
  66. for tarinfo in archive.getmembers():
  67. for keep in dependency['keep']:
  68. if re.match(f"^{keep}$", tarinfo.name):
  69. to_be_extracted.append(tarinfo)
  70. archive.extractall(members=to_be_extracted, path=Path(tmp, key))
  71. for extracted in to_be_extracted:
  72. filename = extracted.name
  73. for rename in dependency['rename']:
  74. filename = filename.replace(rename, dependency['rename'][rename])
  75. newfile = Path(destination, filename)
  76. if newfile.exists():
  77. newfile.unlink()
  78. Path(tmp, key, extracted.name).rename(ensure(newfile))
  79. # Delete destination folder if empty.
  80. if not any(Path(destination).iterdir()):
  81. delete_folder(destination)
  82. delete_folder(tmp)