import os
import json
from django.contrib.staticfiles import finders
from django.core.serializers.json import DjangoJSONEncoder
from django.utils.safestring import mark_safe
from optional_django import six
from .exceptions import ComponentSourceFileNotFound, ComponentWasNotBundled
from .services import RenderService
from .conf import settings
from .bundle import bundle_component
from .templates import MOUNT_JS
service = RenderService()
class RenderedComponent(object):
def __init__(self, markup, path_to_source, props, serialized_props, watch_source, bundle, to_static_markup):
self.markup = markup
self.path_to_source = path_to_source
self.props = props
self.serialized_props = serialized_props
self.watch_source = watch_source
self.bundle = bundle
self.to_static_markup = to_static_markup
def __str__(self):
return mark_safe(self.render_markup())
def __unicode__(self):
return mark_safe(unicode(self.render_markup()))
def render_markup(self):
markup = self.markup
if self.bundle and not self.to_static_markup:
template = '{markup}'
if six.PY2:
template = unicode(template)
markup = template.format(
id=self.get_container_id(),
markup=markup,
)
return mark_safe(markup)
def render_props(self):
if self.serialized_props:
return mark_safe(self.serialized_props)
return ''
def get_bundle(self):
if not self.bundle:
raise ComponentWasNotBundled(
(
'The component "{path}" was not bundled during the rendering process. '
'Call render_component with `bundle`, `translate`, or `watch_source` '
'keyword arguments set to `True` to ensure that it is bundled.'
).format(path=self.path_to_source)
)
return self.bundle
def get_var(self):
return self.get_bundle().get_library()
def get_container_id(self):
return 'reactComponent-' + self.get_var()
def render_mount_js(self):
return mark_safe(
MOUNT_JS.format(
var=self.get_var(),
props=self.serialized_props or 'null',
container_id=self.get_container_id()
)
)
def render_js(self):
return mark_safe(
'\n{bundle}\n\n'.format(
bundle=self.get_bundle().render(),
mount_js=self.render_mount_js(),
)
)
def render_component(
# Rendering options
path_to_source, props=None, to_static_markup=None,
# Bundling options
bundle=None, translate=None, watch_source=None,
# Prop handling
json_encoder=None
):
if not os.path.isabs(path_to_source):
absolute_path_to_source = finders.find(path_to_source)
if not absolute_path_to_source:
raise ComponentSourceFileNotFound(path_to_source)
path_to_source = absolute_path_to_source
if not os.path.exists(path_to_source):
raise ComponentSourceFileNotFound(path_to_source)
if watch_source is None:
watch_source = settings.WATCH_SOURCE
bundled_component = None
if bundle or translate or watch_source:
bundled_component = bundle_component(path_to_source, translate=translate, watch_source=watch_source)
path_to_source = bundled_component.get_assets()[0]['path']
if json_encoder is None:
json_encoder = DjangoJSONEncoder
if props is not None:
serialized_props = json.dumps(props, cls=json_encoder)
else:
serialized_props = None
markup = service.render(path_to_source, serialized_props, to_static_markup)
return RenderedComponent(
markup, path_to_source, props, serialized_props, watch_source, bundled_component, to_static_markup
)