Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 
 
 
 

199 рядки
5.4 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. from frappe.utils.minify import JavascriptMinify
  5. """
  6. Build the `public` folders and setup languages
  7. """
  8. import os, frappe, json, shutil, re
  9. # from cssmin import cssmin
  10. app_paths = None
  11. def setup():
  12. global app_paths
  13. pymodules = []
  14. for app in frappe.get_all_apps(True):
  15. try:
  16. pymodules.append(frappe.get_module(app))
  17. except ImportError: pass
  18. app_paths = [os.path.dirname(pymodule.__file__) for pymodule in pymodules]
  19. def bundle(no_compress, make_copy=False, verbose=False):
  20. """concat / minify js files"""
  21. # build js files
  22. setup()
  23. make_asset_dirs(make_copy=make_copy)
  24. build(no_compress, verbose)
  25. def watch(no_compress):
  26. """watch and rebuild if necessary"""
  27. setup()
  28. import time
  29. compile_less()
  30. build(no_compress=True)
  31. while True:
  32. compile_less()
  33. if files_dirty():
  34. build(no_compress=True)
  35. time.sleep(3)
  36. def make_asset_dirs(make_copy=False):
  37. assets_path = os.path.join(frappe.local.sites_path, "assets")
  38. for dir_path in [
  39. os.path.join(assets_path, 'js'),
  40. os.path.join(assets_path, 'css')]:
  41. if not os.path.exists(dir_path):
  42. os.makedirs(dir_path)
  43. # symlink app/public > assets/app
  44. for app_name in frappe.get_all_apps(True):
  45. pymodule = frappe.get_module(app_name)
  46. app_base_path = os.path.abspath(os.path.dirname(pymodule.__file__))
  47. symlinks = []
  48. symlinks.append([os.path.join(app_base_path, 'public'), os.path.join(assets_path, app_name)])
  49. symlinks.append([os.path.join(app_base_path, 'docs'), os.path.join(assets_path, app_name + '_docs')])
  50. for source, target in symlinks:
  51. source = os.path.abspath(source)
  52. if not os.path.exists(target) and os.path.exists(source):
  53. if make_copy:
  54. shutil.copytree(source, target)
  55. else:
  56. os.symlink(source, target)
  57. def build(no_compress=False, verbose=False):
  58. assets_path = os.path.join(frappe.local.sites_path, "assets")
  59. for target, sources in get_build_maps().iteritems():
  60. pack(os.path.join(assets_path, target), sources, no_compress, verbose)
  61. def get_build_maps():
  62. """get all build.jsons with absolute paths"""
  63. # framework js and css files
  64. build_maps = {}
  65. for app_path in app_paths:
  66. path = os.path.join(app_path, 'public', 'build.json')
  67. if os.path.exists(path):
  68. with open(path) as f:
  69. try:
  70. for target, sources in json.loads(f.read()).iteritems():
  71. # update app path
  72. source_paths = []
  73. for source in sources:
  74. if isinstance(source, list):
  75. s = frappe.get_pymodule_path(source[0], *source[1].split("/"))
  76. else:
  77. s = os.path.join(app_path, source)
  78. source_paths.append(s)
  79. build_maps[target] = source_paths
  80. except ValueError, e:
  81. print path
  82. print 'JSON syntax error {0}'.format(str(e))
  83. return build_maps
  84. timestamps = {}
  85. def pack(target, sources, no_compress, verbose):
  86. from cStringIO import StringIO
  87. outtype, outtxt = target.split(".")[-1], ''
  88. jsm = JavascriptMinify()
  89. for f in sources:
  90. suffix = None
  91. if ':' in f: f, suffix = f.split(':')
  92. if not os.path.exists(f) or os.path.isdir(f):
  93. print "did not find " + f
  94. continue
  95. timestamps[f] = os.path.getmtime(f)
  96. try:
  97. with open(f, 'r') as sourcefile:
  98. data = unicode(sourcefile.read(), 'utf-8', errors='ignore')
  99. extn = f.rsplit(".", 1)[1]
  100. if outtype=="js" and extn=="js" and (not no_compress) and suffix!="concat" and (".min." not in f):
  101. tmpin, tmpout = StringIO(data.encode('utf-8')), StringIO()
  102. jsm.minify(tmpin, tmpout)
  103. minified = tmpout.getvalue()
  104. if minified:
  105. outtxt += unicode(minified or '', 'utf-8').strip('\n') + ';'
  106. if verbose:
  107. print "{0}: {1}k".format(f, int(len(minified) / 1024))
  108. elif outtype=="js" and extn=="html":
  109. # add to frappe.templates
  110. outtxt += html_to_js_template(f, data)
  111. else:
  112. outtxt += ('\n/*\n *\t%s\n */' % f)
  113. outtxt += '\n' + data + '\n'
  114. except Exception:
  115. print "--Error in:" + f + "--"
  116. print frappe.get_traceback()
  117. if not no_compress and outtype == 'css':
  118. pass
  119. #outtxt = cssmin(outtxt)
  120. with open(target, 'w') as f:
  121. f.write(outtxt.encode("utf-8"))
  122. print "Wrote %s - %sk" % (target, str(int(os.path.getsize(target)/1024)))
  123. def html_to_js_template(path, content):
  124. # remove whitespace to a single space
  125. content = re.sub("\s+", " ", content)
  126. # strip comments
  127. content = re.sub("(<!--.*?-->)", "", content)
  128. return """frappe.templates["{key}"] = '{content}';\n""".format(\
  129. key=path.rsplit("/", 1)[-1][:-5], content=content.replace("'", "\'"))
  130. def files_dirty():
  131. for target, sources in get_build_maps().iteritems():
  132. for f in sources:
  133. if ':' in f: f, suffix = f.split(':')
  134. if not os.path.exists(f) or os.path.isdir(f): continue
  135. if os.path.getmtime(f) != timestamps.get(f):
  136. print f + ' dirty'
  137. return True
  138. else:
  139. return False
  140. def compile_less():
  141. from distutils.spawn import find_executable
  142. if not find_executable("lessc"):
  143. return
  144. for path in app_paths:
  145. less_path = os.path.join(path, "public", "less")
  146. if os.path.exists(less_path):
  147. for fname in os.listdir(less_path):
  148. if fname.endswith(".less") and fname != "variables.less":
  149. fpath = os.path.join(less_path, fname)
  150. mtime = os.path.getmtime(fpath)
  151. if fpath in timestamps and mtime == timestamps[fpath]:
  152. continue
  153. timestamps[fpath] = mtime
  154. print "compiling {0}".format(fpath)
  155. css_path = os.path.join(path, "public", "css", fname.rsplit(".", 1)[0] + ".css")
  156. os.system("lessc {0} > {1}".format(fpath, css_path))