# -*- coding: utf-8 -*-
from Acquisition import aq_base
from datetime import datetime
from plone.registry.interfaces import IRegistry
from plone.resource.file import FilesystemFile
from plone.resource.interfaces import IResourceDirectory
from Products.CMFPlone.interfaces import IBundleRegistry
from Products.CMFPlone.interfaces.resources import OVERRIDE_RESOURCE_DIRECTORY_NAME  # noqa
from StringIO import StringIO
from zExceptions import NotFound
from zope.component import getUtility
from zope.component import queryUtility

from collections import OrderedDict
import logging
import re


PRODUCTION_RESOURCE_DIRECTORY = 'production'
logger = logging.getLogger(__name__)


def get_production_resource_directory():
    persistent_directory = queryUtility(IResourceDirectory, name='persistent')
    if persistent_directory is None:
        return ''
    container = persistent_directory[OVERRIDE_RESOURCE_DIRECTORY_NAME]
    try:
        production_folder = container[PRODUCTION_RESOURCE_DIRECTORY]
    except NotFound:
        return '%s/++unique++1' % PRODUCTION_RESOURCE_DIRECTORY
    if 'timestamp.txt' not in production_folder:
        return '%s/++unique++1' % PRODUCTION_RESOURCE_DIRECTORY
    timestamp = production_folder.readFile('timestamp.txt')
    return '%s/++unique++%s' % (
        PRODUCTION_RESOURCE_DIRECTORY, timestamp)


def get_resource(context, path):
    if path.startswith('++plone++'):
        # ++plone++ resources can be customized, we return their override
        # value if any
        overrides = get_override_directory(context)
        filepath = path[9:]
        if overrides.isFile(filepath):
            return overrides.readFile(filepath)

    try:
        resource = context.unrestrictedTraverse(path)
    except NotFound:
        logger.warn(u'Could not find resource {0}. You may have to create it first.'.format(path))  # noqa
        return

    if isinstance(resource, FilesystemFile):
        (directory, sep, filename) = path.rpartition('/')
        return context.unrestrictedTraverse(directory).readFile(filename)

    # calling the resource may modify the header, i.e. the content-type.
    # we do not want this, so keep the original header intact.
    response_before = context.REQUEST.response
    context.REQUEST.response = response_before.__class__()
    if hasattr(aq_base(resource), 'GET'):
        # for FileResource
        result = resource.GET()
    else:
        # any BrowserView
        result = resource()
    context.REQUEST.response = response_before
    return result


class MetaBundleWriter(object):

    def __init__(self, context, folder, name):
        self.context = context
        self.folder = folder
        self.name = name
        self.js_resources = OrderedDict()
        self.css_resources = OrderedDict()
        self.registry = getUtility(IRegistry)
        self.bundles = self.registry.collectionOfInterface(
            IBundleRegistry, prefix='plone.bundles', check=False)

    def write_js(self):

        # default resources
        if self.name == 'default' and self.registry.records.get(
            'plone.resources/jquery.js'
        ):
            self.js_resources['_jquery'] = get_resource(
                self.context,
                self.registry.records['plone.resources/jquery.js'].value)
            self.js_resources['_requirejs'] = get_resource(
                self.context,
                self.registry.records['plone.resources.requirejs'].value)
            self.js_resources['_configjs'] = get_resource(
                self.context,
                self.registry.records['plone.resources.configjs'].value)

        # bundles
        for name, bundle in self.bundles.items():
            self.load_js_bundle(name, bundle)

        self._write_out(self.js_resources, '.js')

    def load_js_bundle(self, name, bundle, depth=0):
        if depth > 10:
            # recursion detection
            return
        if bundle.merge_with != self.name:
            return
        if bundle.jscompilation:
            if bundle.depends and bundle.depends in self.bundles:
                self.load_js_bundle(
                    bundle.depends, self.bundles[bundle.depends], depth + 1)
            if name in self.js_resources:
                return
            resource = get_resource(self.context, bundle.jscompilation)
            if not resource:
                return
            self.js_resources[name] = resource

    def _write_out(self, resources, postfix):
        fi = StringIO()
        for bname, script in resources.items():
            fi.write('''
// Start Bundle: {0}
{1}
// End Bundle: {2}
'''.format(bname, script, bname))
        self.folder.writeFile(self.name + postfix, fi)
        resources.clear()

    def load_css_bundle(self, name, bundle, depth=0):
        if depth > 10:
            # recursion detection
            return

        if bundle.merge_with != self.name:
            return

        if bundle.csscompilation:
            if bundle.depends and bundle.depends in self.bundles:
                self.load_css_bundle(
                    bundle.depends, self.bundles[bundle.depends], depth + 1)
            if name in self.css_resources:
                return

            css = get_resource(self.context, bundle.csscompilation)
            if not css:
                return
            (path, sep, filename) = bundle.csscompilation.rpartition('/')
            # Process relative urls:
            # we prefix with current resource path any url not starting with
            # '/' or http: or data:
            css = re.sub(
                r'''(url\(['"]?(?!['"]?([a-z]+:|\/)))''',
                r'\1%s/' % path,
                css)
            self.css_resources[name] = css

    def write_css(self):
        for name, bundle in self.bundles.items():
            self.load_css_bundle(name, bundle)

        self._write_out(self.css_resources, '.css')


def get_override_directory(context):
    persistent_directory = queryUtility(IResourceDirectory, name='persistent')
    if persistent_directory is None:
        return
    if OVERRIDE_RESOURCE_DIRECTORY_NAME not in persistent_directory:
        persistent_directory.makeDirectory(OVERRIDE_RESOURCE_DIRECTORY_NAME)
    return persistent_directory[OVERRIDE_RESOURCE_DIRECTORY_NAME]


def combine_bundles(context):
    container = get_override_directory(context)
    if PRODUCTION_RESOURCE_DIRECTORY not in container:
        container.makeDirectory(PRODUCTION_RESOURCE_DIRECTORY)
    production_folder = container[PRODUCTION_RESOURCE_DIRECTORY]

    # store timestamp
    fi = StringIO()
    fi.write(datetime.now().isoformat())
    production_folder.writeFile('timestamp.txt', fi)

    # generate new combined bundles
    default_writer = MetaBundleWriter(
        context, production_folder, 'default')
    default_writer.write_js()
    logged_in_writer = MetaBundleWriter(
        context, production_folder, 'logged-in')
    logged_in_writer.write_js()
    default_writer.write_css()
    logged_in_writer.write_css()

Products/CMFPlone/resources/browser/combine.py
# -*- coding: utf-8 -*-
from Acquisition import aq_base
from datetime import datetime
from plone.registry.interfaces import IRegistry
from plone.resource.file import FilesystemFile
from plone.resource.interfaces import IResourceDirectory
from Products.CMFPlone.interfaces import IBundleRegistry
from Products.CMFPlone.interfaces.resources import OVERRIDE_RESOURCE_DIRECTORY_NAME  # noqa
from StringIO import StringIO
from zExceptions import NotFound
from zope.component import getUtility
from zope.component import queryUtility

from collections import OrderedDict
import logging
import re


PRODUCTION_RESOURCE_DIRECTORY = 'production'
logger = logging.getLogger(__name__)


def get_production_resource_directory():
    persistent_directory = queryUtility(IResourceDirectory, name='persistent')
    if persistent_directory is None:
        return ''
    container = persistent_directory[OVERRIDE_RESOURCE_DIRECTORY_NAME]
    try:
        production_folder = container[PRODUCTION_RESOURCE_DIRECTORY]
    except NotFound:
        return '%s/++unique++1' % PRODUCTION_RESOURCE_DIRECTORY
    if 'timestamp.txt' not in production_folder:
        return '%s/++unique++1' % PRODUCTION_RESOURCE_DIRECTORY
    timestamp = production_folder.readFile('timestamp.txt')
    return '%s/++unique++%s' % (
        PRODUCTION_RESOURCE_DIRECTORY, timestamp)


def get_resource(context, path):
    if path.startswith('++plone++'):
        # ++plone++ resources can be customized, we return their override
        # value if any
        overrides = get_override_directory(context)
        filepath = path[9:]
        if overrides.isFile(filepath):
            return overrides.readFile(filepath)

    try:
        resource = context.unrestrictedTraverse(path)
    except NotFound:
        logger.warn(u'Could not find resource {0}. You may have to create it first.'.format(path))  # noqa
        return

    if isinstance(resource, FilesystemFile):
        (directory, sep, filename) = path.rpartition('/')
        return context.unrestrictedTraverse(directory).readFile(filename)

    # calling the resource may modify the header, i.e. the content-type.
    # we do not want this, so keep the original header intact.
    response_before = context.REQUEST.response
    context.REQUEST.response = response_before.__class__()
    if hasattr(aq_base(resource), 'GET'):
        # for FileResource
        result = resource.GET()
    else:
        # any BrowserView
        result = resource()
    context.REQUEST.response = response_before
    return result


class MetaBundleWriter(object):

    def __init__(self, context, folder, name):
        self.context = context
        self.folder = folder
        self.name = name
        self.js_resources = OrderedDict()
        self.css_resources = OrderedDict()
        self.registry = getUtility(IRegistry)
        self.bundles = self.registry.collectionOfInterface(
            IBundleRegistry, prefix='plone.bundles', check=False)

    def write_js(self):

        # default resources
        if self.name == 'default' and self.registry.records.get(
            'plone.resources/jquery.js'
        ):
            self.js_resources['_jquery'] = get_resource(
                self.context,
                self.registry.records['plone.resources/jquery.js'].value)
            self.js_resources['_requirejs'] = get_resource(
                self.context,
                self.registry.records['plone.resources.requirejs'].value)
            self.js_resources['_configjs'] = get_resource(
                self.context,
                self.registry.records['plone.resources.configjs'].value)

        # bundles
        for name, bundle in self.bundles.items():
            self.load_js_bundle(name, bundle)

        self._write_out(self.js_resources, '.js')

    def load_js_bundle(self, name, bundle, depth=0):
        if depth > 10:
            # recursion detection
            return
        if bundle.merge_with != self.name:
            return
        if bundle.jscompilation:
            if bundle.depends and bundle.depends in self.bundles:
                self.load_js_bundle(
                    bundle.depends, self.bundles[bundle.depends], depth + 1)
            if name in self.js_resources:
                return
            resource = get_resource(self.context, bundle.jscompilation)
            if not resource:
                return
            self.js_resources[name] = resource

    def _write_out(self, resources, postfix):
        fi = StringIO()
        for bname, script in resources.items():
            fi.write('''
/* Start Bundle: {0} */
{1}
/* End Bundle: {2} */
'''.format(bname, script, bname))
        self.folder.writeFile(self.name + postfix, fi)
        resources.clear()

    def load_css_bundle(self, name, bundle, depth=0):
        if depth > 10:
            # recursion detection
            return

        if bundle.merge_with != self.name:
            return

        if bundle.csscompilation:
            if bundle.depends and bundle.depends in self.bundles:
                self.load_css_bundle(
                    bundle.depends, self.bundles[bundle.depends], depth + 1)
            if name in self.css_resources:
                return

            css = get_resource(self.context, bundle.csscompilation)
            if not css:
                return
            (path, sep, filename) = bundle.csscompilation.rpartition('/')
            # Process relative urls:
            # we prefix with current resource path any url not starting with
            # '/' or http: or data:
            css = re.sub(
                r'''(url\(['"]?(?!['"]?([a-z]+:|\/)))''',
                r'\1%s/' % path,
                css)
            self.css_resources[name] = css

    def write_css(self):
        for name, bundle in self.bundles.items():
            self.load_css_bundle(name, bundle)

        self._write_out(self.css_resources, '.css')


def get_override_directory(context):
    persistent_directory = queryUtility(IResourceDirectory, name='persistent')
    if persistent_directory is None:
        return
    if OVERRIDE_RESOURCE_DIRECTORY_NAME not in persistent_directory:
        persistent_directory.makeDirectory(OVERRIDE_RESOURCE_DIRECTORY_NAME)
    return persistent_directory[OVERRIDE_RESOURCE_DIRECTORY_NAME]


def combine_bundles(context):
    container = get_override_directory(context)
    if PRODUCTION_RESOURCE_DIRECTORY not in container:
        container.makeDirectory(PRODUCTION_RESOURCE_DIRECTORY)
    production_folder = container[PRODUCTION_RESOURCE_DIRECTORY]

    # store timestamp
    fi = StringIO()
    fi.write(datetime.now().isoformat())
    production_folder.writeFile('timestamp.txt', fi)

    # generate new combined bundles
    default_writer = MetaBundleWriter(
        context, production_folder, 'default')
    default_writer.write_js()
    logged_in_writer = MetaBundleWriter(
        context, production_folder, 'logged-in')
    logged_in_writer.write_js()
    default_writer.write_css()
    logged_in_writer.write_css()

Products/CMFPlone/resources/browser/combine.py
diff --git a/Products/CMFPlone/resources/browser/combine.py b/Products/CMFPlone/resources/browser/combine.py
index 2a0d20a63e..b00b4a7656 100644
--- a/Products/CMFPlone/resources/browser/combine.py
+++ b/Products/CMFPlone/resources/browser/combine.py
@@ -124,9 +124,9 @@ def _write_out(self, resources, postfix):
         fi = StringIO()
         for bname, script in resources.items():
             fi.write('''
-// Start Bundle: {0}
+/* Start Bundle: {0} */
 {1}
-// End Bundle: {2}
+/* End Bundle: {2} */
 '''.format(bname, script, bname))
         self.folder.writeFile(self.name + postfix, fi)
         resources.clear()
diff --git a/news/2820.bugfix b/news/2820.bugfix
new file mode 100644
index 0000000000..ece21fef4e
--- /dev/null
+++ b/news/2820.bugfix
@@ -0,0 +1 @@
+Don't use // as bundle separators in the resource registry's meta bundle generator. It comments the first css construct in every bundle that follows. [fredvd]
\ No newline at end of file

Plone 5.1.4 to 5.1.5 update: resource registry meta bundle generator comments first css construct of individual bundles
In one of our projects, after upgrading from Plone 5.1.4 to Plone 5.1.5  A very small part of the css became broken in plone.app.mosaic layouts . Images inside a tile no longer had a "height: auto" on them. This is normally included in mosaic-styles.css , and the mosaic styles were included in default.css.  

We quickly patched the missing statement into our theme file and did a patch release, but the underlying problem was vague.  The problem would only appear on production, running the site locally did not show the problem, so my attention was pulled to the metabundle generation. This was modified between 5.1.4 and 5.1.5 in https://github.com/plone/Products.CMFPlone/commit/397918cd39ba0be4e2e150df5f5f2220e6ecc828 by @vangheem 

The problematic code is in this part:

https://github.com/plone/Products.CMFPlone/blob/2195c4a43ba100fb2b7973dccb4299dad2de42fe/Products/CMFPlone/resources/browser/combine.py#L123-L132

The individual bundles are separated by comment lines with // Start bundle and // End Bundle, but // actually comments out the first following css construct , more info at  https://www.xanthir.com/b4U10

And the mosaic-styles.css individual bundle start with:

```
// Start Bundle: mosaic-styles
/* Images will never be bigger then a tile */
.mosaic-tile img {
  max-width: 100%;
  height: auto;
}
```

If even skips over the /* */ comment on the next line and comments the first {} it sees
So that is how only our height: auto; got disabled in production . 

This is  at the moment only broken in Plone 5.1 , In Plone 5.2 the whole bundler got rewritten again, partly because of Python3 support and doesn't seem to insert these comments. I have swapped the // comment for /* */ comments and this also solves the problem. I'll create a pull request shortly. 

