25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

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