Source code for ievv_opensource.utils.ievvbuildstatic.sassbuild

from __future__ import unicode_literals
import os

from ievv_opensource.utils.ievvbuildstatic import cssbuildbaseplugin
from ievv_opensource.utils.ievvbuildstatic import filepath
from ievv_opensource.utils.ievvbuildstatic import utils
from ievv_opensource.utils.shellcommandmixin import ShellCommandError


[docs]class Plugin(cssbuildbaseplugin.AbstractPlugin): """ SASS build plugin --- builds .scss files into css, and supports watching for changes. By default, we assume ``sassc`` is available on PATH, but you can override the path to the sassc executable by setting the ``IEVVTASKS_BUILDSTATIC_SASSC_EXECUTABLE`` environment variable. Examples: Very simple example where the source file is in ``demoapp/staticsources/styles/theme.scss``:: IEVVTASKS_BUILDSTATIC_APPS = ievvbuildstatic.config.Apps( ievvbuildstatic.config.App( appname='demoapp', version='1.0.0', plugins=[ ievvbuildstatic.sassbuild.Plugin(sourcefile='theme.scss'), ] ) ) A more complex example that builds a django-cradmin theme where sources are split in multiple directories, and the bower install directory is on the scss path (the example also uses :class:`ievv_opensource.utils.ievvbuildstatic.bowerinstall.Plugin`):: IEVVTASKS_BUILDSTATIC_APPS = ievvbuildstatic.config.Apps( ievvbuildstatic.config.App( appname='demoapp', version='1.0.0', plugins=[ ievvbuildstatic.bowerinstall.Plugin( packages={ 'bootstrap': '~3.1.1' } ), ievvbuildstatic.sassbuild.Plugin( sourcefolder='styles/cradmin_theme_demoapp', sourcefile='theme.scss', other_sourcefolders=[ 'styles/cradmin_base', 'styles/cradmin_theme_default', ], sass_include_paths=[ 'bower_components', ] ) ] ) ) """ name = 'sassbuild' def __init__(self, sourcefile, sourcefolder='styles', destinationfolder=None, other_sourcefolders=None, sass_include_paths=None, sass_variables=None, **kwargs): """ Parameters: sourcefile: Main source file (the one including all other scss files) relative to ``sourcefolder``. sourcefolder: The folder where ``sourcefile`` is located relative to the source folder of the :class:`~ievv_opensource.utils.ievvbuild.config.App`. You can specify a folder in another app using a :class:`ievv_opensource.utils.ievvbuildstatic.filepath.SourcePath` object, but this is normally not recommended. sass_include_paths: Less include paths as a list. Paths are relative to the source folder of the :class:`~ievv_opensource.utils.ievvbuild.config.App`. You can specify folders in other apps using :class:`ievv_opensource.utils.ievvbuildstatic.filepath.SourcePath` objects **kwargs: Kwargs for :class:`ievv_opensource.utils.ievvbuildstatic.cssbuildbaseplugin.AbstractPlugin`. """ self.sourcefolder = sourcefolder if isinstance(sourcefolder, filepath.FilePathInterface) and destinationfolder is None: raise ValueError('destinationfolder must be specifed when sourcefolder is not a string.') self.destinationfolder = destinationfolder or sourcefolder self.other_sourcefolders = other_sourcefolders or [] self.sass_include_paths = sass_include_paths or [] self.sourcefile = sourcefile self.sass_variables = sass_variables or {} super(Plugin, self).__init__(**kwargs)
[docs] def install(self): super(Plugin, self).install() self.app.get_installer('npm').queue_install( 'postcss-scss')
def get_sourcefolder_path(self): return self.app.get_source_path(self.sourcefolder) def get_sourcefile_path(self): return self.app.get_source_path(self.sourcefolder, self.sourcefile)
[docs] def get_destinationfile_path(self): return self.app.get_destination_path( self.destinationfolder, self.sourcefile, new_extension='.css')
def get_other_sourcefolders_paths(self): return map(self.app.get_source_path, self.other_sourcefolders) def get_sass_include_paths(self, temporary_directory): sass_include_paths = list(map(self.app.get_source_path, self.sass_include_paths)) sass_include_paths.append(self.get_importable_sass_directory(temporary_directory)) return sass_include_paths def format_sass_include_paths(self, temporary_directory): if self.sass_include_paths: return ':'.join(self.get_sass_include_paths(temporary_directory)) else: return '' def get_sassc_executable(self): return os.environ.get('IEVVTASKS_BUILDSTATIC_SASSC_EXECUTABLE', 'sassc') def get_sass_kwargs(self, temporary_directory): kwargs = {} sass_include_paths = self.format_sass_include_paths(temporary_directory=temporary_directory) if sass_include_paths: kwargs['load_path'] = sass_include_paths # style = 'nested' # if self.app.apps.is_in_production_mode(): # style = 'compressed' # kwargs['style'] = style return kwargs def get_importable_sass_directory(self, temporary_directory): importable_sass_directory_path = os.path.join(temporary_directory, 'automatically_on_sass_path') if not os.path.exists(importable_sass_directory_path): os.makedirs(importable_sass_directory_path) return importable_sass_directory_path def make_importable_sass_file(self, temporary_directory, sass_file_name, sass_code): directory_path = os.path.join(self.get_importable_sass_directory(temporary_directory), 'autogenerated_partials') if not os.path.exists(directory_path): os.mkdir(directory_path) sass_file_path = os.path.join(directory_path, sass_file_name) with open(sass_file_path, 'wb') as f: f.write(sass_code.encode('utf-8')) def make_importable_variables_file_content(self): contentlist = [] for variablename, value in self.sass_variables.items(): contentlist.append('${}: {};'.format(variablename, value)) header = '// Autogenerated by "ievv buildstatic". Do not edit this file.' return '{}\n\n{}'.format(header, '\n'.join(contentlist)) def make_importable_variables_file(self, temporary_directory): self.make_importable_sass_file(temporary_directory=temporary_directory, sass_file_name='_variables.scss', sass_code=self.make_importable_variables_file_content()) def preprocess_styles(self, temporary_directory): super(Plugin, self).preprocess_styles(temporary_directory) self.make_importable_variables_file(temporary_directory=temporary_directory)
[docs] def build_css(self, temporary_directory): self.get_logger().command_start('Building {source} into {destination}.'.format( source=self.get_sourcefile_path(), destination=self.get_destinationfile_path())) destinationdirectory = os.path.dirname(self.get_destinationfile_path()) if not os.path.exists(destinationdirectory): os.makedirs(destinationdirectory) executable = self.get_sassc_executable() try: self.run_shell_command(executable, args=[ self.get_sourcefile_path(), self.get_destinationfile_path() ], kwargs=self.get_sass_kwargs(temporary_directory=temporary_directory)) except ShellCommandError: self.get_logger().command_error('SASS build FAILED!') self.add_deferred_warning('SASS build FAILED!') raise cssbuildbaseplugin.CssBuildException() else: self.get_logger().command_success('SASS build successful :)') self.add_deferred_success('SASS build successful :)')
[docs] def get_watch_folders(self): """ We only watch the folder where the scss sources are located, so this returns the absolute path of the ``sourcefolder``. """ folders = [self.app.get_source_path(self.sourcefolder)] if self.other_sourcefolders: folders.extend(self.get_other_sourcefolders_paths()) return folders
[docs] def get_watch_regexes(self): return ['^.+\.scss$']
def __str__(self): return '{}({})'.format(super(Plugin, self).__str__(), os.path.join(self.sourcefolder, self.sourcefile))
[docs] def get_all_source_file_paths(self): regexfilelist = utils.RegexFileList(include_patterns=self.get_watch_regexes()) source_file_paths = [] sourcefolders = [self.get_sourcefolder_path()] sourcefolders.extend(self.get_other_sourcefolders_paths()) for folder in sourcefolders: source_file_paths.extend( os.path.join(folder, relative_path) for relative_path in regexfilelist.get_files_as_list(folder)) return source_file_paths