minify_css_tutorial.py
import cssutilsimport reimport loggingimport osimport timecssutils.log.setLevel(logging.CRITICAL)startTime = time.time()os.system('cls')def getFilesByExtension(ext, root): foundFiles = [] for root, directories, files in os.walk(root): for f in files: if f.endswith(ext): # os.path.join(root, f) is the full path to the file foundFiles.append(os.path.join(root, f)) return foundFilesdef flattenStyleSheet(sheet): ruleList = [] for rule in sheet.cssRules: if rule.typeString == 'MEDIA_RULE': ruleList += rule.cssRules elif rule.typeString == 'STYLE_RULE': ruleList.append(rule) return ruleListdef findAllCSSClasses(): usedClasses = {} # Find all used classes for htmlFile in htmlFiles: with open(htmlFile, 'r') as f: htmlContent = f.read() regex = r'class="(.*?)"' # re.DOTALL is needed to match newlines matched = re.finditer(regex, htmlContent, re.MULTILINE | re.DOTALL) # matched is a list of re.Match objects for i in matched: for className in i.groups()[0].split(' '): # i.groups()[0] is the first group in the regex usedClasses[className] = '' return list(usedClasses.keys())def translateUsedClasses(classList): for i, usedClass in enumerate(classList): for translation in translations: # If the class is found in the translations list, replace it regex = translation[0] subst = translation[1] if re.search(regex, usedClass): # re.sub() replaces the regex with the subst result = re.sub(regex, subst, usedClass, 1, re.MULTILINE) # 1 is the max number of replacements # Replace the class in the list classList[i] = result return classListhtmlFiles = getFilesByExtension('.html', '.')cssFiles = getFilesByExtension('.css', 'style')# Use Translations if the class names in the Markup dont exactly # match the CSS Selector ( Except for the dot at the begining. )translations = [ [ '@', '\\@' ], [ r"(.*?):(.*)", r"\g<1>\\:\g<2>:\g<1>", ], [ r"child(.*)", "child\\g<1> > *", ],]usedClasses = findAllCSSClasses()usedClasses = translateUsedClasses(usedClasses)output = 'min.css'newCSS = ''for cssFile in cssFiles: # Parse the CSS File sheet = cssutils.parseFile(cssFile) rules = flattenStyleSheet(sheet) noClassSelectors = [] for rule in rules: for usedClass in usedClasses: if '.' + usedClass == rule.selectorText: # If the class is used in the HTML, add it to the new CSS usedClasses.remove(usedClass) # Remove the class from the list if rule.parentRule: newCSS += str(rule.parentRule.cssText) else: newCSS += str(rule.cssText) if rule.selectorText[0] != '.' and not rule.selectorText in noClassSelectors: # If the selector doesnt start with a dot and is not already in the list, # add it noClassSelectors.append(rule.selectorText) if rule.parentRule: newCSS += str(rule.parentRule.cssText) else: newCSS += str(rule.cssText)newCSS = newCSS.replace('\n', '')newCSS = newCSS.replace(' ', '')with open(output, 'w') as f: f.write(newCSS)print('TIME: ', time.time() - startTime)