Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

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