Mercurial > piecrust2
changeset 206:cba781477bd0
processing: Add `concat`, `uglifyjs` and `cleancss` processors.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 18 Jan 2015 12:13:28 -0800 |
parents | e725af1d48fb |
children | c5330cb35794 |
files | piecrust/plugins/builtin.py piecrust/processing/compressors.py piecrust/processing/util.py |
diffstat | 3 files changed, 183 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/piecrust/plugins/builtin.py Sun Jan 18 12:12:57 2015 -0800 +++ b/piecrust/plugins/builtin.py Sun Jan 18 12:13:28 2015 -0800 @@ -21,10 +21,13 @@ from piecrust.plugins.base import PieCrustPlugin from piecrust.processing.base import CopyFileProcessor from piecrust.processing.compass import CompassProcessor +from piecrust.processing.compressors import ( + CleanCssProcessor, UglifyJSProcessor) from piecrust.processing.less import LessProcessor from piecrust.processing.requirejs import RequireJSProcessor from piecrust.processing.sass import SassProcessor from piecrust.processing.sitemap import SitemapProcessor +from piecrust.processing.util import ConcatProcessor from piecrust.sources.base import DefaultPageSource from piecrust.sources.posts import ( FlatPostsSource, ShallowPostsSource, HierarchyPostsSource) @@ -90,11 +93,14 @@ def getProcessors(self): return [ CopyFileProcessor(), + ConcatProcessor(), CompassProcessor(), LessProcessor(), SassProcessor(), RequireJSProcessor(), - SitemapProcessor()] + SitemapProcessor(), + CleanCssProcessor(), + UglifyJSProcessor()] def getImporters(self): return [
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/piecrust/processing/compressors.py Sun Jan 18 12:13:28 2015 -0800 @@ -0,0 +1,93 @@ +import os +import os.path +import logging +import platform +import subprocess +from piecrust.processing.base import SimpleFileProcessor + + +logger = logging.getLogger(__name__) + + +class CleanCssProcessor(SimpleFileProcessor): + PROCESSOR_NAME = 'cleancss' + + def __init__(self): + super(CleanCssProcessor, self).__init__({'css': 'css'}) + self._conf = None + + def _doProcess(self, in_path, out_path): + self._ensureInitialized() + + args = [self._conf['bin'], '-o', out_path] + args += self._conf['options'] + args.append(in_path) + logger.debug("Cleaning CSS file: %s" % args) + + # On Windows, we need to run the process in a shell environment + # otherwise it looks like `PATH` isn't taken into account. + shell = (platform.system() == 'Windows') + try: + retcode = subprocess.call(args, shell=shell) + except FileNotFoundError as ex: + logger.error("Tried running CleanCSS processor with command: %s" % + args) + raise Exception("Error running CleanCSS processor. " + "Did you install it?") from ex + if retcode != 0: + raise Exception("Error occured in CleanCSS. Please check " + "log messages above for more information.") + return True + + def _ensureInitialized(self): + if self._conf is not None: + return + + self._conf = self.app.config.get('cleancss') or {} + self._conf.setdefault('bin', 'cleancss') + self._conf.setdefault('options', ['--skip-rebase']) + if not isinstance(self._conf['options'], list): + raise Exception("The `cleancss/options` configuration setting " + "must be an array of arguments.") + + +class UglifyJSProcessor(SimpleFileProcessor): + PROCESSOR_NAME = 'uglifyjs' + + def __init__(self): + super(UglifyJSProcessor, self).__init__({'js': 'js'}) + self._conf = None + + def _doProcess(self, in_path, out_path): + self._ensureInitialized() + + args = [self._conf['bin'], in_path, '-o', out_path] + args += self._conf['options'] + logger.debug("Uglifying JS file: %s" % args) + + # On Windows, we need to run the process in a shell environment + # otherwise it looks like `PATH` isn't taken into account. + shell = (platform.system() == 'Windows') + try: + retcode = subprocess.call(args, shell=shell) + except FileNotFoundError as ex: + logger.error("Tried running UglifyJS processor with command: %s" % + args) + raise Exception("Error running UglifyJS processor. " + "Did you install it?") from ex + if retcode != 0: + raise Exception("Error occured in UglifyJS. Please check " + "log messages above for more information.") + return True + + def _ensureInitialized(self): + if self._conf is not None: + return + + self._conf = self.app.config.get('uglifyjs') or {} + self._conf.setdefault('bin', 'uglifyjs') + self._conf.setdefault('options', ['--compress']) + if not isinstance(self._conf['options'], list): + raise Exception("The `uglify/options` configuration setting " + "must be an array of arguments.") +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/piecrust/processing/util.py Sun Jan 18 12:13:28 2015 -0800 @@ -0,0 +1,83 @@ +import os.path +import time +import logging +import yaml +from piecrust.processing.base import Processor + + +logger = logging.getLogger(__name__) + + +class _ConcatInfo(object): + timestamp = 0 + files = None + delim = "\n" + + +class ConcatProcessor(Processor): + PROCESSOR_NAME = 'concat' + + def __init__(self): + super(ConcatProcessor, self).__init__() + self._cache = {} + + def matches(self, path): + return path.endswith('.concat') + + def getDependencies(self, path): + info = self._load(path) + return info.files + + def getOutputFilenames(self, filename): + return [filename[:-7]] + + def process(self, path, out_dir): + dirname, filename = os.path.split(path) + out_path = os.path.join(out_dir, filename[:-7]) + info = self._load(path) + if not info.files: + raise Exception("No files specified in: %s" % + os.path.relpath(path, self.app.root_dir)) + + logger.debug("Concatenating %d files to: %s" % + (len(info.files), out_path)) + encoded_delim = info.delim.encode('utf8') + with open(out_path, 'wb') as ofp: + for p in info.files: + with open(p, 'rb') as ifp: + ofp.write(ifp.read()) + if info.delim: + ofp.write(encoded_delim) + return True + + def _load(self, path): + cur_time = time.time() + info = self._cache.get(path) + if (info is not None and + (cur_time - info.timestamp <= 1 or + os.path.getmtime(path) < info.timestamp)): + return info + + if info is None: + info = _ConcatInfo() + self._cache[path] = info + + with open(path, 'r') as fp: + config = yaml.load(fp) + + info.files = config.get('files', []) + info.delim = config.get('delim', "\n") + info.timestamp = cur_time + + path_mode = config.get('path_mode', 'relative') + if path_mode == 'relative': + dirname, _ = os.path.split(path) + info.files = [os.path.join(dirname, f) for f in info.files] + elif path_mode == 'absolute': + info.files = [os.path.join(self.app.root_dir, f) + for f in info.files] + else: + raise Exception("Unknown path mode: %s" % path_mode) + + return info +