/bomb/cfile.py
Python | 284 lines | 276 code | 6 blank | 2 comment | 0 complexity | 92b818c24ce2dd9e2b7dc8c8ffdc3b5e MD5 | raw file
- # coding=utf-8
- import re
- import logging
- import os
- import tempfile
- from config import Config
- from utils import normalize_path, filename
- from compiler import compile_csjs
- logger = logging.getLogger('bomb')
- class CFile(object):
- '''config file object'''
- STALE_AGE = 8
- rdead = re.compile(r'^\s*/\*\s*@?dead\s*\*/\s*$')
- rmap = re.compile(r'''^\s*/\*\s*
- @?map\s*=\s*(.+?)
- \s*\*/ ''', re.VERBOSE)
- rbootstrap = re.compile(r'^\s*/\*\s*@?bootstrap\s*\*/\s*$', re.VERBOSE)
- rplaceholder = re.compile(r'^\s*/\*\s*@?placeholder\s*\*/\s*$')
- rversion = re.compile(r'''^\s*/\*\s*
- @?version\s*=\s*(\d+)
- \s*\*/\s*$''', re.VERBOSE)
- rimport = re.compile(r'''^\s*\$?import\((.+)\)|
- ^\s*@import\s+url\((.+)\)
- ''', re.VERBOSE)
- rdepend = re.compile(r'^\s*\$?depend\((.+)\)')
- rfile = re.compile(r'\.js$|\.css$')
- def __init__(self, path, url_base=None, url_map=dict()):
- self.path = path
- self.url_base = url_base if url_base != None\
- else (os.path.dirname(path) + os.sep\
- if os.path.dirname(path) != '' else '')
- self.url_map = url_map
- self.filename, self.basename, self.extension = filename(path)
- self.dead = False
- self.frozen = False
- self.bootstrap = False
- self.placeholder = False
- self.stale_age = self.STALE_AGE
- self._map = ''
- self._version = -1
- self._file_depend = []
- # scan cfile attribute
- with open(path) as lines:
- for line in lines:
- matchobj = re.search(self.rdead, line)
- if matchobj:
- self.dead = True
- return
- matchobj = re.search(self.rbootstrap, line)
- if matchobj:
- self.bootstrap = True
- matchobj = re.search(self.rplaceholder, line)
- if matchobj:
- self.placeholder = True
- matchobj = re.search(self.rmap, line)
- if matchobj:
- self._map = matchobj.group(1)
- matchobj = re.search(self.rversion, line)
- if matchobj:
- self._version = int(matchobj.group(1))
- matchobj = re.search(self.rdepend, line)
- if matchobj:
- dependstr = matchobj.group(1)
- self._file_depend.extend(dependstr.split(','))
- if self.version < 0:
- self.update_version()
- @property
- def version(self):
- return self._version
- @version.setter
- def version(self, version):
- content = []
- with open(self.path) as lines:
- for line in lines:
- matchobj = re.search(self.rversion, line)
- if not matchobj:
- content.append(line)
- self._version = version
- content.insert(0, '/* @version=' + str(self._version) + ' */\n')
- with open(self.path, 'w') as handler:
- handler.write(''.join(content))
- @property
- def map(self):
- return self._map
- @map.setter
- def map(self, newmap):
- new = []
- with open(self.path) as lines:
- for line in lines:
- matchobj = re.search(self.rmap, line)
- if not matchobj:
- new.append(line)
- new.insert(0, '/* @map = ' + newmap + ' */\n')
- with open(self.path, 'w') as handler:
- handler.write(''.join(new))
- self._map = newmap
- def update_version(self):
- self.version = self.version + 1
- def get_version_name(self, version=None):
- version = '_' + str(version if version !=None else self.version) + '.'
- return self.basename + version + self.extension
- def get_stale_name(self, stale_age=None):
- return self.get_version_name(self.version - \
- (stale_age or self.stale_age))
- def get_version_name_re(self):
- return re.compile(self.basename + '_' + r'(\d+)' + r'\.' + \
- self.extension)
- def get_placeholder_re(self):
- name = self.basename + '\\.' + self.extension
- return re.compile(r'''(\/\*\s*_PLACEHOLDER_%s\s+START\s*\*\/)
- ([\s\S]*?)
- (\/\*\s*_PLACEHOLDER_%s\s+END\s*\*\/)
- ''' % (name, name), re.VERBOSE)
- def parse_content(self):
- if self.dead:
- return
- with open(self.path) as lines:
- for line in lines:
- importmatch = re.search(self.rimport, line)
- if importmatch:
- path = importmatch.group(1) or importmatch.group(2)
- path = re.sub('["\']', '', path)
- path = self.parse_import_path(path)
- for file_generator in self.import_file(path):
- for l in file_generator:
- yield l
- elif re.search(self.rdepend, line):
- continue
- else:
- yield line
- def parse_import_path(self, path):
- url_base = self.url_base
- url_map = self.url_map
- path = normalize_path(path)
- if path.find('!') < 0 :
- return url_base + path
- path = path.split('!')
- prefix = path[0]
- if prefix in url_map:
- return normalize_path(url_base + url_map[prefix] + path[1])
- else:
- raise Exception('prefix not found ! (' + prefix + ')')
- def import_file(self, path):
- def _import(path):
- yield '\n'
- with open(path) as lines:
- for line in lines:
- yield line.decode('utf-8')
- if os.path.isdir(path):
- for (dirpath, dirnames, filenames) in os.walk(path):
- if '.svn' in dirnames:
- dirnames.remove('.svn')
- for fname in filenames:
- if fname.endswith('.js') or fname.endswith('.css'):
- yield _import(os.path.join(dirpath, fname))
- else:
- yield _import(path)
- def update_map(self):
- reg = re.compile(r'''^\s*([A-Z]\w*)
- (?:\.add|\[['"]add['"]\])
- \((.*?)\s*,\s*function''', re.VERBOSE)
- rinternal = re.compile(r'^[\'"]?!')
- mods = []
- boom = None
- file_depend = self._file_depend
- dependstr = ''
- for line in self.parse_content():
- matchobj = re.match(reg, line)
- if matchobj:
- mods.append(matchobj.group(2))
- boom = matchobj.group(1)
- if not boom:
- return
- # strip module name begined with !
- mods = [m for m in mods if not re.match(rinternal, m)];
- if len(mods) > 0:
- modstr = ',\'mods\':[' + ','.join(mods) + ']'
- else:
- modstr = ''
- if len(file_depend) > 0 :
- dependstr = ",'requires':[" + ",".join(file_depend) + "]"
- version_name = self.get_version_name()
- mstr = '''%s['add']('%s',{'path':'%s'%s%s}); ''' % (boom,
- self.filename,
- version_name,
- modstr,
- dependstr
- );
-
- self.map = mstr
- def dump(self, extension=[]):
- content = []
- self.update_map()
- for line in self.parse_content():
- content.append(line.encode('utf-8'))
- for line in extension:
- content.append(line.encode('utf-8'))
- return content
- def push(self, destination, extension=[]):
- content = self.dump(extension=extension)
- path = normalize_path(destination, self.get_version_name())
- with open(path, 'w') as handler:
- handler.write(''.join(content))
- return path
- def update_referrer(self, referrer):
- pattern = self.get_placeholder_re() if self.placeholder else \
- self.get_version_name_re()
- referrer = normalize_path(referrer)
- with open(referrer) as handler:
- content = handler.read().decode('utf-8')
- if self.placeholder:
- logger.info('replace placeholder: ' + self.filename)
- spawn = ''.join(self.dump())
- handle, abspath = tempfile.mkstemp('.' + self.extension, text=True)
- with open(abspath, 'w') as handler:
- handler.write(spawn)
- spawn = compile_csjs(abspath)
- content = re.sub(pattern, '\g<1>' + spawn + '\g<3>', content)
- os.close(handle)
- os.remove(abspath)
- else:
- content = re.sub(pattern, self.get_version_name(), content)
- with open(referrer, 'w') as handler:
- handler.write(content.encode('utf-8'))