JavaScript, CSS and PHP Compacter for Python

No Comments »Written on March 5th, 2011 by
Categories: Python

Here’s a port of the Hx compacter library. Originally written in PHP, it’s now available as a Python module.

You can get the original PHP version of the JavaScript compacter here.

This is a work-in-progress. It’s been superficially tested. Please report any issues you find!

#! /usr/bin/python

import re
import sys

__author__="Neil E. Pearson"
__date__ ="$04/05/2007 1:22:19 PM$"

def js(input):
	cd=re.match(r"^\s*/[/*]\s*<!\[CDATA\[\s*(?:\*/)?(.*)/[/*]\s*\]\]>\s*(?:\*/)?\s*$", input, re.DOTALL)
	if cd: input=cd.group(1)
	ph=0
	while str(ph) in input: ph+=1
	lines=re.split(r"(?s)\s*[\r\n]+\s*", input)
	subs=(
		(r"\s*//.*$"		, r""),
		(r"\s*([^\w$])\s*"	, r"\1"),
		(r";}"				, r"}")
	)
	s=[]
	cm=False
	for k in range(len(lines)-1):
		l=lines[k]
		if cm:
			if '*/' in l:
				l=re.sub(r"^.*?\*/","",l)
				cm=False
			else: l=""
		if not cm:
			c=0
			while c<len(l):
				n=l1
				if n in "\"'/":
					if re.search(r"([=(?:]\s*/|[\'\"])$", l[:c+1]):
						cl=findDelimiter(l,c)
						if cl is not False:
							s.append(l1)
							l="%s%s.%s%s" % (l[:c], ph, len(s)-1, l[cl+1:])
							c=0
				c+=1

			l=re.sub(r"/\*.*?\*/","",l)
			if "/*" in l:
				l=re.sub(r"/\*.*$","",l)
				cm=True
			for x in subs: l=re.sub(x[0], x[1], l)
			l=l.strip()
			if k<len(lines)-1:
				if lines[k+1]:
					if not re.search(r"^\s*[-{};:+=&|^,?.]", lines[k+1]):
						if re.search(r"([^-{};:+=&|^,.?([\\]]|[-+]{2})$", l): l+=";"
						m=re.search(r"(if|for|while|with)(\(.*?\));$", l, re.IGNORECASE)
						if m:
							if findDelimiter(m.group(2))==len(m.group(2))-1:
								l=l[:-1]
		l=l.replace('else;','else ')
		lines[k]=l
	ret="".join(lines)
	f=-1
	while(True):
		f=ret.lower().find("do{",f+1)
		if(f<0): break
		b=findDelimiter(ret,f+2)
		if b is not False:
			b=ret.lower().find("while(",b)
			if b is not False:
				b+=5
				b=findDelimiter(ret,b)
				ret="%s;%s" % (ret[:b+1], ret[b+1:])
	i=0
	while i<len(ret):
		if ret[i] in "=:":
			o=i+1
			if ret[o:o+9].lower()=="function(":
				o=findDelimiter(ret,o+8)+1
			if ret[o]=="{":
				cl=findDelimiter(ret,o)
				if cl<len(ret):
					if re.match(r"""[-\w(${["']""",ret[cl+1]):
						ret="%s;%s" % (ret[:cl+1], ret[cl+1:])
		i+=1
	ret=ret.replace(";}","}")
	ret=re.sub(str(ph)+r"\.(\d+)", lambda m: s[int(m.group(1))], ret)
	if cd:
		ret="/*<![CDATA[*/%s//]]>" % ret
	return ret

def css(input):
	subs=(
		(r"\s+"					, r" "),
		(r"([{;,}:])\s"			, r"\1"),
		(r"\s([}{,;])"			, r"\1"),
		(r"(?s)/\*.*?\*/"		, r""),
		(r";\s*(}|$)"			, r"\1"),
		(r"(?i)#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3(\s|\}|;|$)" , r"#\1\2\3\4"),
		(r"\b0[a-z]{2}\b"		, r"0"),
		(r"\b0(\.\d+[a-z]{2})\b", r"\1")
	)
	ret=input
	for i in subs: ret=re.sub(i[0],i[1],ret)
	return ret.strip()

def php(input):
	subs=(
		("(?m)(?<!:)//.*$",					""),
		(r"(?s)/\*.*\*/",					""),
		(r"(\s)\s+",						r"\1"),
		(r"""([^\w\s'"])\s+([^\w\s'"])""",	r"\1\2")
	)
	ret=input
	for i in subs: ret=re.sub(i[0],i[1],ret)
	return ret.strip()

def findDelimiter(str, pos=0, dir=0, esc="\\"):
	if pos>len(str): return False
	pairs="\"\"''//<>[]{}()"
	pp=pairs.find(str[pos])
	if pp<0: return False
	op=pp%2 and -1 or 1
	o=None
	if pairs[pp]!=pairs[pp+op]:
		dir=op
		o=pairs[pp]
	if not dir: dir=1
	c=pairs[pp+op]
	nest=1
	pos+=dir
	while pos>=0 and pos<len(str):
		if esc:
			if pos<len(str)-1:
				if str[pos+dir-1]==esc: pos+=dir*2
			if pos>=0 and pos<len(str):
				if str[pos]==o: nest+=1
				if str[pos]==c: nest-=1
		if not nest: return pos
		pos+=dir
	return False

if __name__ == "__main__":
	if len(sys.argv)==2:
		filename=sys.argv[1]
		func=css if filename.lower().endswith(".css") else js
		f=open(filename,"r")
		print func(f.read())
		f.close()

Tags: , , , , , , ,

Leave a Reply