You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

216 line
5.8 KiB

  1. # Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. #
  3. # MIT License (MIT)
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a
  6. # copy of this software and associated documentation files (the "Software"),
  7. # to deal in the Software without restriction, including without limitation
  8. # the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. # and/or sell copies of the Software, and to permit persons to whom the
  10. # Software is furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. #
  22. from __future__ import unicode_literals
  23. from webnotes.utils.minify import JavascriptMinify
  24. import os, sys, webnotes
  25. class Bundle:
  26. """
  27. Concatenate, compress and mix (if required) js+css files from build.json
  28. """
  29. no_compress = False
  30. timestamps = {}
  31. path = '.'
  32. def concat(self, filelist, outfile=None):
  33. """
  34. Concat css and js files into a bundle
  35. """
  36. from cStringIO import StringIO
  37. out_type = outfile and outfile.split('.')[-1] or 'js'
  38. outtxt = ''
  39. for f in filelist:
  40. suffix = None
  41. if ':' in f:
  42. f, suffix = f.split(':')
  43. if not os.path.exists(f) or os.path.isdir(f):
  44. continue
  45. self.timestamps[f] = os.path.getmtime(f)
  46. # get datas
  47. try:
  48. with open(f, 'r') as infile:
  49. # get file type
  50. ftype = f.split('.')[-1]
  51. data = unicode(infile.read(), 'utf-8', errors='ignore')
  52. outtxt += ('\n/*\n *\t%s\n */' % f)
  53. # append
  54. if suffix=='concat' or out_type != 'js' or self.no_compress or ('.min.' in f):
  55. outtxt += '\n' + data + '\n'
  56. else:
  57. jsm = JavascriptMinify()
  58. tmpin = StringIO(data.encode('utf-8'))
  59. tmpout = StringIO()
  60. jsm.minify(tmpin, tmpout)
  61. tmpmin = unicode(tmpout.getvalue() or '', 'utf-8')
  62. tmpmin.strip('\n')
  63. outtxt += tmpmin
  64. except Exception, e:
  65. print "--Error in:" + f + "--"
  66. print webnotes.getTraceback()
  67. with open(outfile, 'w') as f:
  68. f.write(outtxt.encode("utf-8"))
  69. print "Wrote %s - %sk" % (outfile, str(int(os.path.getsize(outfile)/1024)))
  70. def dirty(self):
  71. """check if build files are dirty"""
  72. self.make_build_data()
  73. for builddict in self.bdata:
  74. for f in self.get_infiles(builddict):
  75. if ':' in f:
  76. f, suffix = f.split(':')
  77. if not os.path.exists(f) or os.path.isdir(f):
  78. continue
  79. if os.path.getmtime(f) != self.timestamps.get(f):
  80. print f + ' dirty'
  81. return True
  82. else:
  83. return False
  84. def make(self):
  85. """Build (stitch + compress) the file defined in build.json"""
  86. print "Building js and css files..."
  87. self.make_build_data()
  88. for builddict in self.bdata:
  89. outfile = builddict.keys()[0]
  90. infiles = self.get_infiles(builddict)
  91. self.concat(infiles, os.path.relpath(os.path.join(self.path, outfile), os.curdir))
  92. self.reset_app_html()
  93. def reset_app_html(self):
  94. import webnotes
  95. if os.path.exists("public/app.html"):
  96. os.remove("public/app.html")
  97. splash = ""
  98. if os.path.exists("public/app/images/splash.svg"):
  99. with open("public/app/images/splash.svg") as splash_file:
  100. splash = splash_file.read()
  101. with open('lib/public/html/app.html', 'r') as app_html:
  102. data = app_html.read()
  103. data = data % {
  104. "_version_number": webnotes.generate_hash(),
  105. "splash": splash
  106. }
  107. with open('public/app.html', 'w') as new_app_html:
  108. new_app_html.write(data)
  109. def get_infiles(self, builddict):
  110. """make list of files to merge"""
  111. outfile = builddict.keys()[0]
  112. infiles = builddict[outfile]
  113. # add app js and css to the list
  114. if outfile in self.appfiles:
  115. for f in self.appfiles[outfile]:
  116. if f not in infiles:
  117. infiles.append(f)
  118. fl = []
  119. for f in infiles:
  120. ## load files from directory
  121. if f.endswith('/'):
  122. # add init js first
  123. fl += [os.path.relpath(os.path.join(f, 'init.js'), os.curdir)]
  124. # files other than init.js and beginning with "_"
  125. fl += [os.path.relpath(os.path.join(f, tmp), os.curdir) \
  126. for tmp in os.listdir(f) if (tmp != 'init.js' and not tmp.startswith('_'))]
  127. else:
  128. fl.append(os.path.relpath(os.path.join(self.path, f), os.curdir))
  129. return fl
  130. def make_build_data(self):
  131. """merge build.json and lib/build.json"""
  132. # framework js and css files
  133. with open('lib/public/build.json', 'r') as bfile:
  134. bdata = eval(bfile.read())
  135. # app js and css files
  136. if os.path.exists('app/public/build.json'):
  137. with open('app/public/build.json', 'r') as bfile:
  138. appfiles = eval(bfile.read())
  139. else:
  140. appfiles = {}
  141. # add additional app files in bdata
  142. buildfile_list = [builddict.keys()[0] for builddict in bdata]
  143. for f in appfiles:
  144. if f not in buildfile_list:
  145. bdata.append({f: appfiles[f]})
  146. self.appfiles = appfiles
  147. self.bdata = bdata
  148. def check_public():
  149. from webnotes.install_lib.setup_public_folder import make
  150. make()
  151. def bundle(no_compress, cms_make=True):
  152. """concat / minify js files"""
  153. # build js files
  154. check_public()
  155. bundle = Bundle()
  156. bundle.no_compress = no_compress
  157. bundle.make()
  158. if cms_make:
  159. # build index.html and app.html
  160. from website.helpers.make_web_include_files import make
  161. make()
  162. def watch(no_compress):
  163. """watch and rebuild if necessary"""
  164. import time
  165. bundle = Bundle()
  166. bundle.no_compress = no_compress
  167. while True:
  168. if bundle.dirty():
  169. bundle.make()
  170. time.sleep(3)