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()