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.
 
 
 
 
 
 

223 line
5.3 KiB

  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. from webnotes.utils.minify import JavascriptMinify
  5. """
  6. Build the `public` folders and setup languages
  7. """
  8. import os, sys, webnotes
  9. from cssmin import cssmin
  10. def bundle(no_compress, cms_make=True):
  11. """concat / minify js files"""
  12. # build js files
  13. check_public()
  14. check_lang()
  15. bundle = Bundle()
  16. bundle.no_compress = no_compress
  17. bundle.make()
  18. if cms_make:
  19. try:
  20. from startup.event_handlers import on_build
  21. on_build()
  22. except ImportError, e:
  23. pass
  24. clear_pyc_files()
  25. def watch(no_compress):
  26. """watch and rebuild if necessary"""
  27. import time
  28. bundle = Bundle()
  29. bundle.no_compress = no_compress
  30. while True:
  31. if bundle.dirty():
  32. bundle.make()
  33. time.sleep(3)
  34. def check_public():
  35. from webnotes.install_lib.setup_public_folder import make
  36. make()
  37. def check_lang():
  38. from webnotes.translate import update_translations
  39. update_translations()
  40. def clear_pyc_files():
  41. from webnotes.utils import get_base_path
  42. for path, folders, files in os.walk(get_base_path()):
  43. if 'locale' in folders: folders.remove('locale')
  44. for f in files:
  45. if f.decode("utf-8").endswith(".pyc"):
  46. os.remove(os.path.join(path, f))
  47. class Bundle:
  48. """
  49. Concatenate, compress and mix (if required) js+css files from build.json
  50. """
  51. no_compress = False
  52. timestamps = {}
  53. path = '.'
  54. def concat(self, filelist, outfile=None):
  55. """
  56. Concat css and js files into a bundle
  57. """
  58. from cStringIO import StringIO
  59. out_type = outfile and outfile.split('.')[-1] or 'js'
  60. outtxt = ''
  61. for f in filelist:
  62. suffix = None
  63. if ':' in f:
  64. f, suffix = f.split(':')
  65. if not os.path.exists(f) or os.path.isdir(f):
  66. continue
  67. self.timestamps[f] = os.path.getmtime(f)
  68. # get datas
  69. try:
  70. with open(f, 'r') as infile:
  71. # get file type
  72. ftype = f.split('.')[-1]
  73. data = unicode(infile.read(), 'utf-8', errors='ignore')
  74. outtxt += ('\n/*\n *\t%s\n */' % f)
  75. # append
  76. if suffix=='concat' or out_type != 'js' or self.no_compress or ('.min.' in f):
  77. outtxt += '\n' + data + '\n'
  78. else:
  79. jsm = JavascriptMinify()
  80. tmpin = StringIO(data.encode('utf-8'))
  81. tmpout = StringIO()
  82. jsm.minify(tmpin, tmpout)
  83. tmpmin = unicode(tmpout.getvalue() or '', 'utf-8')
  84. tmpmin.strip('\n')
  85. outtxt += tmpmin
  86. except Exception, e:
  87. print "--Error in:" + f + "--"
  88. print webnotes.getTraceback()
  89. if not self.no_compress and out_type == 'css':
  90. outtxt = cssmin(outtxt)
  91. with open(outfile, 'w') as f:
  92. f.write(outtxt.encode("utf-8"))
  93. print "Wrote %s - %sk" % (outfile, str(int(os.path.getsize(outfile)/1024)))
  94. def dirty(self):
  95. """check if build files are dirty"""
  96. self.make_build_data()
  97. for builddict in self.bdata:
  98. for f in self.get_infiles(builddict):
  99. if ':' in f:
  100. f, suffix = f.split(':')
  101. if not os.path.exists(f) or os.path.isdir(f):
  102. continue
  103. if os.path.getmtime(f) != self.timestamps.get(f):
  104. print f + ' dirty'
  105. return True
  106. else:
  107. return False
  108. def make(self):
  109. """Build (stitch + compress) the file defined in build.json"""
  110. print "Building js and css files..."
  111. self.make_build_data()
  112. for builddict in self.bdata:
  113. outfile = builddict.keys()[0]
  114. infiles = self.get_infiles(builddict)
  115. self.concat(infiles, os.path.relpath(os.path.join(self.path, outfile), os.curdir))
  116. self.reset_app_html()
  117. def reset_app_html(self):
  118. import webnotes
  119. if os.path.exists("public/app.html"):
  120. os.remove("public/app.html")
  121. splash = ""
  122. if os.path.exists("public/app/images/splash.svg"):
  123. with open("public/app/images/splash.svg") as splash_file:
  124. splash = splash_file.read()
  125. with open('lib/public/html/app.html', 'r') as app_html:
  126. data = app_html.read()
  127. data = data % {
  128. "_version_number": webnotes.generate_hash(),
  129. "splash": splash
  130. }
  131. with open('public/app.html', 'w') as new_app_html:
  132. new_app_html.write(data)
  133. def get_infiles(self, builddict):
  134. """make list of files to merge"""
  135. outfile = builddict.keys()[0]
  136. infiles = builddict[outfile]
  137. # add app js and css to the list
  138. if outfile in self.appfiles:
  139. for f in self.appfiles[outfile]:
  140. if f not in infiles:
  141. infiles.append(f)
  142. fl = []
  143. for f in infiles:
  144. ## load files from directory
  145. if f.endswith('/'):
  146. # add init js first
  147. fl += [os.path.relpath(os.path.join(f, 'init.js'), os.curdir)]
  148. # files other than init.js and beginning with "_"
  149. fl += [os.path.relpath(os.path.join(f, tmp), os.curdir) \
  150. for tmp in os.listdir(f) if (tmp != 'init.js' and not tmp.startswith('_'))]
  151. else:
  152. fl.append(os.path.relpath(os.path.join(self.path, f), os.curdir))
  153. return fl
  154. def make_build_data(self):
  155. """merge build.json and lib/build.json"""
  156. # framework js and css files
  157. with open('lib/public/build.json', 'r') as bfile:
  158. bdata = eval(bfile.read())
  159. # app js and css files
  160. if os.path.exists('app/public/build.json'):
  161. with open('app/public/build.json', 'r') as bfile:
  162. appfiles = eval(bfile.read())
  163. else:
  164. appfiles = {}
  165. # add additional app files in bdata
  166. buildfile_list = [builddict.keys()[0] for builddict in bdata]
  167. for f in appfiles:
  168. if f not in buildfile_list:
  169. bdata.append({f: appfiles[f]})
  170. self.appfiles = appfiles
  171. self.bdata = bdata