#! /usr/bin/python3 # # Run sassc and watch for changes in source files (using inotitfy) to rebuild # automatically. # # Usage: sassw INPUT # (output will automatically be INPUT with .scss changed to .css) import json import glob import os import pyinotify import subprocess import sys import time filename = sys.argv[1] if len(sys.argv) > 1 else '.' if os.path.isdir(filename): scss_filenames = [x for x in os.listdir(filename) if not x.startswith('_') and x.endswith('.scss')] if not scss_filenames: print('Error: directory specified but no proper scss file within.', file=sys.stderr) filenames = [os.path.join(filename, x) for x in scss_filenames] else: filenames = sys.argv[1:] def build(): global sources, directories directories = set() sources = set() for filename in filenames: result = subprocess.run( ['sassc', '-mauto', filename, filename.replace('.scss', '.css')], capture_output=True, text=True) sys.stdout.write(result.stdout) sys.stderr.write(result.stderr) if result.returncode: # error with open(filename.replace('.scss', '.css'), 'w') as fd: print('''body::before { white-space: pre; font-family: monospace; content: "%s"; }''' % result.stderr.replace('\n', '\\A').replace('"', '\\"'), file=fd) basepath = os.path.abspath(os.path.dirname(filename)) sources = sources.union(set([os.path.abspath(os.path.join(basepath, x)) for x in json.load(open(filename.replace('.scss', '.css.map')))['sources']])) directories = directories.union(set([os.path.dirname(x) for x in sources])) class EventManager(pyinotify.ProcessEvent): def process_default(self, event): if event.pathname in sources: filename = os.path.basename(event.pathname) print(f'{filename} changed, building', end='') t0 = time.time() build() print(f' ({time.time() - t0:.2f}s)') print('>>> Building %s' % ' '.join(filenames)) build() wm = pyinotify.WatchManager() notifier = pyinotify.Notifier(wm, default_proc_fun=EventManager()) for directory in directories: wm.add_watch(directory, pyinotify.IN_CLOSE_WRITE) notifier.coalesce_events() print('>>> Sassw is watching for changes. Press Ctrl-C to stop.') notifier.loop()