Wrap all plugin's JS assets into same IIFE
As discussed in #2200. For this to work we've introduced sub-bundles per plugin for the JS assets. CSS and LESS on the other hand are basically handled as before. We use a custom bundle class here since it's apparently not possible to keep a sub bundle from inheriting the parent's filters, thus not allowing us something like packed_plugins [js_plugin_delimiter_bundler] packed_plugin_a [js_delimiter_bundler] packed_plugin_b [js_delimiter_bundler] ... The individiual bundles per plugin would inherit js_plugin_delimiter_bundler and we'd get a double wrapper - not helpful. Instead we now have a modified JsPluginBundle that overwrites Bundle._merge_and_apply to further wrap the returned hunk.
This commit is contained in:
parent
ca7aa322c3
commit
328c08b775
3 changed files with 140 additions and 56 deletions
|
|
@ -1093,12 +1093,14 @@ class Server(object):
|
|||
self._logger.debug("Deleting {path}...".format(**locals()))
|
||||
shutil.rmtree(path)
|
||||
except:
|
||||
self._logger.exception("Error while trying to delete {path}, leaving it alone".format(**locals()))
|
||||
self._logger.exception("Error while trying to delete {path}, "
|
||||
"leaving it alone".format(**locals()))
|
||||
continue
|
||||
|
||||
# re-create path
|
||||
self._logger.debug("Creating {path}...".format(**locals()))
|
||||
error_text = "Error while trying to re-create {path}, that might cause errors with the webassets cache".format(**locals())
|
||||
error_text = "Error while trying to re-create {path}, that might cause " \
|
||||
"errors with the webassets cache".format(**locals())
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as e:
|
||||
|
|
@ -1108,13 +1110,16 @@ class Server(object):
|
|||
import time
|
||||
for n in range(3):
|
||||
time.sleep(0.5)
|
||||
self._logger.debug("Creating {path}: Retry #{retry} after {time}s".format(path=path, retry=n+1, time=(n + 1)*0.5))
|
||||
self._logger.debug("Creating {path}: Retry #{retry} after {time}s".format(path=path,
|
||||
retry=n+1,
|
||||
time=(n + 1)*0.5))
|
||||
try:
|
||||
os.makedirs(path)
|
||||
break
|
||||
except:
|
||||
if self._logger.isEnabledFor(logging.DEBUG):
|
||||
self._logger.exception("Ignored error while creating directory {path}".format(**locals()))
|
||||
self._logger.exception("Ignored error while creating "
|
||||
"directory {path}".format(**locals()))
|
||||
pass
|
||||
else:
|
||||
# this will only get executed if we never did
|
||||
|
|
@ -1134,9 +1139,9 @@ class Server(object):
|
|||
|
||||
self._logger.info("Reset webasset folder {path}...".format(**locals()))
|
||||
|
||||
AdjustedEnvironment = type(Environment)(Environment.__name__, (Environment,), dict(
|
||||
resolver_class=util.flask.PluginAssetResolver
|
||||
))
|
||||
AdjustedEnvironment = type(Environment)(Environment.__name__,
|
||||
(Environment,),
|
||||
dict(resolver_class=util.flask.PluginAssetResolver))
|
||||
class CustomDirectoryEnvironment(AdjustedEnvironment):
|
||||
@property
|
||||
def directory(self):
|
||||
|
|
@ -1145,9 +1150,9 @@ class Server(object):
|
|||
assets = CustomDirectoryEnvironment(app)
|
||||
assets.debug = not self._settings.getBoolean(["devel", "webassets", "bundle"])
|
||||
|
||||
UpdaterType = type(util.flask.SettingsCheckUpdater)(util.flask.SettingsCheckUpdater.__name__, (util.flask.SettingsCheckUpdater,), dict(
|
||||
updater=assets.updater
|
||||
))
|
||||
UpdaterType = type(util.flask.SettingsCheckUpdater)(util.flask.SettingsCheckUpdater.__name__,
|
||||
(util.flask.SettingsCheckUpdater,),
|
||||
dict(updater=assets.updater))
|
||||
assets.updater = UpdaterType
|
||||
|
||||
enable_gcodeviewer = self._settings.getBoolean(["gcodeViewer", "enabled"])
|
||||
|
|
@ -1223,12 +1228,6 @@ class Server(object):
|
|||
"js/app/client/util.js",
|
||||
"js/app/client/wizard.js"
|
||||
]
|
||||
js_core = dynamic_core_assets["js"] + \
|
||||
dynamic_plugin_assets["bundled"]["js"] + \
|
||||
["js/app/dataupdater.js",
|
||||
"js/app/helpers.js",
|
||||
"js/app/main.js"]
|
||||
js_plugins = dynamic_plugin_assets["external"]["js"]
|
||||
|
||||
css_libs = [
|
||||
"css/bootstrap.min.css",
|
||||
|
|
@ -1242,80 +1241,143 @@ class Server(object):
|
|||
"css/pnotify.buttons.min.css",
|
||||
"css/pnotify.history.min.css"
|
||||
]
|
||||
css_core = list(dynamic_core_assets["css"]) + list(dynamic_plugin_assets["bundled"]["css"])
|
||||
css_plugins = list(dynamic_plugin_assets["external"]["css"])
|
||||
|
||||
less_core = list(dynamic_core_assets["less"]) + list(dynamic_plugin_assets["bundled"]["less"])
|
||||
less_plugins = list(dynamic_plugin_assets["external"]["less"])
|
||||
|
||||
# a couple of custom filters
|
||||
from octoprint.server.util.webassets import LessImportRewrite, JsDelimiterBundler, JsPluginDelimiterBundler, \
|
||||
SourceMapRewrite, SourceMapRemove
|
||||
from octoprint.server.util.webassets import LessImportRewrite, JsDelimiterBundler, \
|
||||
SourceMapRewrite, SourceMapRemove, JsPluginBundle
|
||||
from webassets.filter import register_filter
|
||||
|
||||
register_filter(LessImportRewrite)
|
||||
register_filter(SourceMapRewrite)
|
||||
register_filter(SourceMapRemove)
|
||||
register_filter(JsDelimiterBundler)
|
||||
register_filter(JsPluginDelimiterBundler)
|
||||
|
||||
# JS
|
||||
def all_assets_for_plugins(collection):
|
||||
"""Gets all plugin assets for a dict of plugin->assets"""
|
||||
result = []
|
||||
for assets in collection.values():
|
||||
result += assets
|
||||
return result
|
||||
|
||||
# -- JS --------------------------------------------------------------------------------------------------------
|
||||
|
||||
js_filters = ["sourcemap_remove", "js_delimiter_bundler"]
|
||||
js_plugin_filters = ["sourcemap_remove", "js_delimiter_bundler"]
|
||||
|
||||
if self._settings.getBoolean(["feature", "legacyPluginAssets"]):
|
||||
# TODO remove again in 1.3.8
|
||||
js_plugin_filters = ["sourcemap_remove", "js_delimiter_bundler"]
|
||||
def js_bundles_for_plugins(collection, filters=None):
|
||||
"""Produces Bundle instances"""
|
||||
result = dict()
|
||||
for plugin, assets in collection.items():
|
||||
if len(assets):
|
||||
result[plugin] = Bundle(*assets, filters=filters)
|
||||
return result
|
||||
|
||||
else:
|
||||
js_plugin_filters = ["sourcemap_remove", "js_plugin_delimiter_bundler"]
|
||||
def js_bundles_for_plugins(collection, filters=None):
|
||||
"""Produces JsPluginBundle instances that output IIFE wrapped assets"""
|
||||
result = dict()
|
||||
for plugin, assets in collection.items():
|
||||
if len(assets):
|
||||
result[plugin] = JsPluginBundle(plugin, *assets, filters=filters)
|
||||
return result
|
||||
|
||||
js_libs_bundle = Bundle(*js_libs, output="webassets/packed_libs.js", filters=",".join(js_filters))
|
||||
|
||||
js_client_bundle = Bundle(*js_client, output="webassets/packed_client.js", filters=",".join(js_filters))
|
||||
js_core_bundle = Bundle(*js_core, output="webassets/packed_core.js", filters=",".join(js_filters))
|
||||
js_core = dynamic_core_assets["js"] + \
|
||||
all_assets_for_plugins(dynamic_plugin_assets["bundled"]["js"]) + \
|
||||
["js/app/dataupdater.js",
|
||||
"js/app/helpers.js",
|
||||
"js/app/main.js"]
|
||||
js_plugins = js_bundles_for_plugins(dynamic_plugin_assets["external"]["js"],
|
||||
filters="js_delimiter_bundler")
|
||||
|
||||
js_libs_bundle = Bundle(*js_libs,
|
||||
output="webassets/packed_libs.js",
|
||||
filters=",".join(js_filters))
|
||||
|
||||
js_client_bundle = Bundle(*js_client,
|
||||
output="webassets/packed_client.js",
|
||||
filters=",".join(js_filters))
|
||||
js_core_bundle = Bundle(*js_core,
|
||||
output="webassets/packed_core.js",
|
||||
filters=",".join(js_filters))
|
||||
|
||||
if len(js_plugins) == 0:
|
||||
js_plugins_bundle = Bundle(*[])
|
||||
else:
|
||||
js_plugins_bundle = Bundle(*js_plugins, output="webassets/packed_plugins.js", filters=",".join(js_plugin_filters))
|
||||
js_plugins_bundle = Bundle(*js_plugins.values(),
|
||||
output="webassets/packed_plugins.js",
|
||||
filters=",".join(js_plugin_filters))
|
||||
|
||||
js_app_bundle = Bundle(js_plugins_bundle, js_core_bundle, output="webassets/packed_app.js", filters=",".join(js_filters))
|
||||
js_app_bundle = Bundle(js_plugins_bundle, js_core_bundle,
|
||||
output="webassets/packed_app.js",
|
||||
filters=",".join(js_filters))
|
||||
|
||||
# -- CSS -------------------------------------------------------------------------------------------------------
|
||||
|
||||
# CSS
|
||||
css_filters = ["cssrewrite"]
|
||||
|
||||
css_libs_bundle = Bundle(*css_libs, output="webassets/packed_libs.css", filters=",".join(css_filters))
|
||||
css_core = list(dynamic_core_assets["css"]) \
|
||||
+ all_assets_for_plugins(dynamic_plugin_assets["bundled"]["css"])
|
||||
css_plugins = list(all_assets_for_plugins(dynamic_plugin_assets["external"]["css"]))
|
||||
|
||||
css_libs_bundle = Bundle(*css_libs,
|
||||
output="webassets/packed_libs.css",
|
||||
filters=",".join(css_filters))
|
||||
|
||||
if len(css_core) == 0:
|
||||
css_core_bundle = Bundle(*[])
|
||||
else:
|
||||
css_core_bundle = Bundle(*css_core, output="webassets/packed_core.css", filters=",".join(css_filters))
|
||||
css_core_bundle = Bundle(*css_core,
|
||||
output="webassets/packed_core.css",
|
||||
filters=",".join(css_filters))
|
||||
|
||||
if len(css_plugins) == 0:
|
||||
css_plugins_bundle = Bundle(*[])
|
||||
else:
|
||||
css_plugins_bundle = Bundle(*css_plugins, output="webassets/packed_plugins.css", filters=",".join(css_filters))
|
||||
css_plugins_bundle = Bundle(*css_plugins,
|
||||
output="webassets/packed_plugins.css",
|
||||
filters=",".join(css_filters))
|
||||
|
||||
css_app_bundle = Bundle(css_core, css_plugins, output="webassets/packed_app.css", filters=",".join(css_filters))
|
||||
css_app_bundle = Bundle(css_core, css_plugins,
|
||||
output="webassets/packed_app.css",
|
||||
filters=",".join(css_filters))
|
||||
|
||||
# -- LESS ------------------------------------------------------------------------------------------------------
|
||||
|
||||
# LESS
|
||||
less_filters = ["cssrewrite", "less_importrewrite"]
|
||||
|
||||
less_core = list(dynamic_core_assets["less"]) \
|
||||
+ all_assets_for_plugins(dynamic_plugin_assets["bundled"]["less"])
|
||||
less_plugins = all_assets_for_plugins(dynamic_plugin_assets["external"]["less"])
|
||||
|
||||
if len(less_core) == 0:
|
||||
less_core_bundle = Bundle(*[])
|
||||
else:
|
||||
less_core_bundle = Bundle(*less_core, output="webassets/packed_core.less", filters=",".join(less_filters))
|
||||
less_core_bundle = Bundle(*less_core,
|
||||
output="webassets/packed_core.less",
|
||||
filters=",".join(less_filters))
|
||||
|
||||
if len(less_plugins) == 0:
|
||||
less_plugins_bundle = Bundle(*[])
|
||||
else:
|
||||
less_plugins_bundle = Bundle(*less_plugins, output="webassets/packed_plugins.less", filters=",".join(less_filters))
|
||||
less_plugins_bundle = Bundle(*less_plugins,
|
||||
output="webassets/packed_plugins.less",
|
||||
filters=",".join(less_filters))
|
||||
|
||||
less_app_bundle = Bundle(less_core, less_plugins, output="webassets/packed_app.less", filters=",".join(less_filters))
|
||||
less_app_bundle = Bundle(less_core, less_plugins,
|
||||
output="webassets/packed_app.less",
|
||||
filters=",".join(less_filters))
|
||||
|
||||
# -- asset registration ----------------------------------------------------------------------------------------
|
||||
|
||||
# asset registration
|
||||
assets.register("js_libs", js_libs_bundle)
|
||||
assets.register("js_client", js_client_bundle)
|
||||
assets.register("js_core", js_core_bundle)
|
||||
for plugin, bundle in js_plugins.items():
|
||||
# register our collected plugin bundles so that they are bound to the environment
|
||||
assets.register("js_plugin_{}".format(plugin), bundle)
|
||||
assets.register("js_plugins", js_plugins_bundle)
|
||||
assets.register("js_app", js_app_bundle)
|
||||
assets.register("css_libs", css_libs_bundle)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import threading
|
|||
import logging
|
||||
import netaddr
|
||||
import os
|
||||
import collections
|
||||
|
||||
from octoprint.settings import settings
|
||||
import octoprint.server
|
||||
|
|
@ -1347,8 +1348,12 @@ def collect_plugin_assets(enable_gcodeviewer=True, preferred_stylesheet="css"):
|
|||
logger = logging.getLogger(__name__ + ".collect_plugin_assets")
|
||||
|
||||
supported_stylesheets = ("css", "less")
|
||||
assets = dict(bundled=dict(js=[], css=[], less=[]),
|
||||
external=dict(js=[], css=[], less=[]))
|
||||
assets = dict(bundled=dict(js=collections.defaultdict(list),
|
||||
css=collections.defaultdict(list),
|
||||
less=collections.defaultdict(list)),
|
||||
external=dict(js=collections.defaultdict(list),
|
||||
css=collections.defaultdict(list),
|
||||
less=collections.defaultdict(list)))
|
||||
|
||||
asset_plugins = octoprint.plugin.plugin_manager().get_implementations(octoprint.plugin.AssetPlugin)
|
||||
for implementation in asset_plugins:
|
||||
|
|
@ -1374,13 +1379,13 @@ def collect_plugin_assets(enable_gcodeviewer=True, preferred_stylesheet="css"):
|
|||
for asset in all_assets["js"]:
|
||||
if not asset_exists("js", asset):
|
||||
continue
|
||||
assets[asset_key]["js"].append('plugin/{name}/{asset}'.format(**locals()))
|
||||
assets[asset_key]["js"][name].append('plugin/{name}/{asset}'.format(**locals()))
|
||||
|
||||
if preferred_stylesheet in all_assets:
|
||||
for asset in all_assets[preferred_stylesheet]:
|
||||
if not asset_exists(preferred_stylesheet, asset):
|
||||
continue
|
||||
assets[asset_key][preferred_stylesheet].append('plugin/{name}/{asset}'.format(**locals()))
|
||||
assets[asset_key][preferred_stylesheet][name].append('plugin/{name}/{asset}'.format(**locals()))
|
||||
else:
|
||||
for stylesheet in supported_stylesheets:
|
||||
if not stylesheet in all_assets:
|
||||
|
|
@ -1389,7 +1394,7 @@ def collect_plugin_assets(enable_gcodeviewer=True, preferred_stylesheet="css"):
|
|||
for asset in all_assets[stylesheet]:
|
||||
if not asset_exists(stylesheet, asset):
|
||||
continue
|
||||
assets[asset_key][stylesheet].append('plugin/{name}/{asset}'.format(**locals()))
|
||||
assets[asset_key][stylesheet][name].append('plugin/{name}/{asset}'.format(**locals()))
|
||||
break
|
||||
|
||||
return assets
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ try:
|
|||
except:
|
||||
import urlparse
|
||||
|
||||
from webassets.bundle import Bundle
|
||||
from webassets.merge import MemoryHunk
|
||||
from webassets.filter import Filter
|
||||
from webassets.filter.cssrewrite.base import PatternRewriter
|
||||
import webassets.filter.cssrewrite.urlpath as urlpath
|
||||
|
|
@ -103,14 +105,29 @@ class JsDelimiterBundler(Filter):
|
|||
out.write("\n;\n")
|
||||
|
||||
|
||||
class JsPluginDelimiterBundler(Filter):
|
||||
name = "js_plugin_delimiter_bundler"
|
||||
options = {}
|
||||
_PLUGIN_BUNDLE_WRAPPER = \
|
||||
u"""// JS assets for plugin {plugin}
|
||||
(function () {{
|
||||
try {{
|
||||
{contents}
|
||||
}} catch (error) {{
|
||||
log.error("Error in JS assets for plugin {plugin}:", (error.stack || error));
|
||||
}}
|
||||
}})();
|
||||
"""
|
||||
|
||||
def input(self, _in, out, **kwargs):
|
||||
source = kwargs.get("source", "n/a")
|
||||
class JsPluginBundle(Bundle):
|
||||
def __init__(self, plugin, *args, **kwargs):
|
||||
Bundle.__init__(self, *args, **kwargs)
|
||||
self.plugin = plugin
|
||||
|
||||
out.write("// source: " + source + "\n")
|
||||
out.write("(function () {\n try {\n ")
|
||||
out.write(_in.read().replace('\n', '\n '))
|
||||
out.write("\n } catch (error) {\n log.error(\"Error in bundled asset " + source + ":\", (error.stack || error));\n }\n})();\n")
|
||||
def _merge_and_apply(self, ctx, output, force, parent_debug=None,
|
||||
parent_filters=None, extra_filters=None,
|
||||
disable_cache=None):
|
||||
hunk = Bundle._merge_and_apply(self, ctx, output, force, parent_debug=parent_debug,
|
||||
parent_filters=parent_filters, extra_filters=extra_filters,
|
||||
disable_cache=disable_cache)
|
||||
|
||||
# TODO find a solution that eats less memory - maybe a ChainedMemoryHunk instead?
|
||||
return MemoryHunk(_PLUGIN_BUNDLE_WRAPPER.format(contents=hunk.data().replace("\n", "\n "),
|
||||
plugin=self.plugin))
|
||||
|
|
|
|||
Loading…
Reference in a new issue