Merge pull request #1765 from odoo-dev/master-fix-less-compilation-errors-fme
[FIX] Asset bundles compilation bug + support for mixed preprocessing languages - moving imports to the top of the source to compile breaks the assets recomposition trough markers - improved performance by avoiding useless assets fragments recomposition - added support for mixed preprocessing languages in the same bundle
This commit is contained in:
@@ -1073,7 +1073,8 @@ class AssetsBundle(object):
|
||||
response = []
|
||||
if debug:
|
||||
if css and self.stylesheets:
|
||||
self.preprocess_css()
|
||||
compiled = self.preprocess_css()
|
||||
self.recompose_css(compiled)
|
||||
for style in self.stylesheets:
|
||||
response.append(style.to_html())
|
||||
if js:
|
||||
@@ -1119,13 +1120,13 @@ class AssetsBundle(object):
|
||||
return self.cache[key][1]
|
||||
|
||||
def css(self):
|
||||
"""Generate css content from given bundle"""
|
||||
key = 'css_%s' % self.xmlid
|
||||
if key in self.cache and self.cache[key][0] != self.version:
|
||||
# Invalidate cache on version mismach
|
||||
self.cache.pop(key)
|
||||
if key not in self.cache:
|
||||
self.preprocess_css()
|
||||
content = '\n'.join(asset.minify() for asset in self.stylesheets)
|
||||
content = self.preprocess_css()
|
||||
|
||||
if self.css_errors:
|
||||
msg = '\n'.join(self.css_errors)
|
||||
@@ -1162,32 +1163,45 @@ class AssetsBundle(object):
|
||||
def preprocess_css(self):
|
||||
"""
|
||||
Checks if the bundle contains any sass/less content, then compiles it to css.
|
||||
Returns the bundle's flat css.
|
||||
"""
|
||||
to_preprocess = [asset for asset in self.stylesheets if isinstance(asset, PreprocessedCSS)]
|
||||
if not to_preprocess:
|
||||
return
|
||||
to_compile = [asset for asset in to_preprocess if type(asset) == type(to_preprocess[0])]
|
||||
if len(to_preprocess) != len(to_compile):
|
||||
self.css_errors.append("You can't mix css preprocessors languages in the same bundle. (%s)" % self.xmlid)
|
||||
command = to_compile[0].get_command()
|
||||
source = '\n'.join([asset.get_source() for asset in to_compile])
|
||||
sass = [asset for asset in self.stylesheets if isinstance(asset, SassStylesheetAsset)]
|
||||
if sass:
|
||||
cmd = sass[0].get_command()
|
||||
source = '\n'.join([asset.get_source() for asset in sass])
|
||||
compiled = self.compile_css(cmd, source)
|
||||
self.recompose_css(compiled)
|
||||
|
||||
# move up all @import rules to the top and exclude file imports
|
||||
less = [asset for asset in self.stylesheets if isinstance(asset, LessStylesheetAsset)]
|
||||
if less:
|
||||
cmd = less[0].get_command()
|
||||
source = ''
|
||||
for asset in self.stylesheets:
|
||||
if isinstance(asset, LessStylesheetAsset):
|
||||
source += asset.get_source()
|
||||
else:
|
||||
source += asset.content
|
||||
compiled = self.compile_css(cmd, source)
|
||||
return compiled
|
||||
|
||||
return '\n'.join(asset.minify() for asset in self.stylesheets)
|
||||
|
||||
def compile_css(self, cmd, source):
|
||||
"""Sanitizes @import rules, remove duplicates @import rules, then compile"""
|
||||
imports = []
|
||||
def push(matchobj):
|
||||
def sanitize(matchobj):
|
||||
ref = matchobj.group(2)
|
||||
line = '@import "%s"%s' % (ref, matchobj.group(3))
|
||||
if '.' not in ref and line not in imports and not ref.startswith(('.', '/', '~')):
|
||||
imports.append(line)
|
||||
return line
|
||||
return ''
|
||||
source = re.sub(self.rx_preprocess_imports, push, source)
|
||||
imports.append(source)
|
||||
source = u'\n'.join(imports)
|
||||
source = re.sub(self.rx_preprocess_imports, sanitize, source)
|
||||
|
||||
try:
|
||||
compiler = Popen(command, stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
compiler = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
except Exception:
|
||||
msg = "Could not execute command %r" % command[0]
|
||||
msg = "Could not execute command %r" % cmd[0]
|
||||
_logger.error(msg)
|
||||
self.css_errors.append(msg)
|
||||
return
|
||||
@@ -1198,14 +1212,18 @@ class AssetsBundle(object):
|
||||
self.css_errors.append(error)
|
||||
return
|
||||
compiled = result[0].strip().decode('utf8')
|
||||
return compiled
|
||||
|
||||
def recompose_css(self, compiled):
|
||||
"""Flattenize StylesheetAsset's content from compiled source"""
|
||||
fragments = self.rx_css_split.split(compiled)[1:]
|
||||
while fragments:
|
||||
asset_id = fragments.pop(0)
|
||||
asset = next(asset for asset in to_compile if asset.id == asset_id)
|
||||
asset = next(asset for asset in self.stylesheets if asset.id == asset_id)
|
||||
asset._content = fragments.pop(0)
|
||||
|
||||
def get_preprocessor_error(self, stderr, source=None):
|
||||
# TODO: try to find out which asset the error belongs to
|
||||
"""Improve and remove sensitive information from sass/less compilator error messages"""
|
||||
error = stderr.split('Load paths')[0].replace(' Use --trace for backtrace.', '')
|
||||
if 'Cannot load compass' in error:
|
||||
error += "Maybe you should install the compass gem using this extra argument:\n\n" \
|
||||
|
||||
Reference in New Issue
Block a user