* Added jsminifier unit

git-svn-id: trunk@34652 -
This commit is contained in:
michael 2016-10-08 08:14:42 +00:00
parent 3a3f490234
commit 95eb4ed2eb
4 changed files with 465 additions and 0 deletions

2
.gitattributes vendored
View File

@ -2481,8 +2481,10 @@ packages/fcl-js/Makefile svneol=native#text/plain
packages/fcl-js/Makefile.fpc svneol=native#text/plain
packages/fcl-js/Makefile.fpc.fpcmake svneol=native#text/plain
packages/fcl-js/README.TXT svneol=native#text/plain
packages/fcl-js/examples/fpjsmin.pp svneol=native#text/plain
packages/fcl-js/fpmake.pp svneol=native#text/plain
packages/fcl-js/src/jsbase.pp svneol=native#text/plain
packages/fcl-js/src/jsminifier.pp svneol=native#text/plain
packages/fcl-js/src/jsparser.pp svneol=native#text/plain
packages/fcl-js/src/jsscanner.pp svneol=native#text/plain
packages/fcl-js/src/jstoken.pp svneol=native#text/plain

View File

@ -0,0 +1,21 @@
{$mode objfpc}{$h+}
{$inline on}
program fpjsmin;
uses jsminifier;
begin
if ParamCount<>2 then
begin
Writeln('Usage: fpjsmin infile outfile');
halt(1);
end;
With TJSONMinifier.Create(Nil) do
try
FileHeader.Add(paramstr(1));
Execute(ParamStr(1),ParamStr(2));
finally
Free
end;
end.

View File

@ -37,6 +37,8 @@ begin
T.ResourceStrings:=true;
T:=P.Targets.AddUnit('jswriter.pp');
T.ResourceStrings:=true;
T:=P.Targets.AddUnit('jsminifier.pp');
T.ResourceStrings:=true;
{$ifndef ALLPACKAGES}
Run;
end;

View File

@ -0,0 +1,440 @@
{ *********************************************************************
This file is part of the Free Component Library (FCL)
Copyright (c) 2016 Michael Van Canneyt.
Javascript minifier
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{ ---------------------------------------------------------------------
Javascript minifier, based on an implementation by Douglas Crockford,
see original copyright.
---------------------------------------------------------------------}
{ jsmin.c
2013-03-29
Copyright (c) 2002 Douglas Crockford (www.crockford.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
}
unit jsminifier;
{$mode objfpc}{$H+}
{$inline on}
interface
uses sysutils,classes,bufstream;
Const
EOS = #0;
Type
{ TJSONMinifier }
EJSONMinifier = Class(Exception);
TJSONMinifier = Class(TComponent)
Private
FA : char;
FB : char;
FFileHeader: TStrings;
FLookahead : char;
FX : char;
FY : char ;
Fin : TStream;
Fout : TStream;
procedure SetFileHeader(AValue: TStrings);
Protected
// Token reading routines
function Peek : char;
function Get : char;inline;
function Next : char;
// Token writing routines
procedure Putc(c: char);inline;
Procedure Reset;
procedure DoHeader; virtual;
procedure Error(Const Msg: string);
Class Function isAlphaNum(c: char): boolean;
Class Function iif(B : Boolean; Const ifTrue,ifFalse : integer) : integer; inline;
procedure Action(d: Byte);
procedure Minify;
Public
Constructor Create(AOwner : TComponent); override;
Destructor Destroy; override;
Procedure Execute(Const SourceFilename,DestFilename : String);
Procedure Execute(Source,Dest : TStream);
Procedure Execute(SourceFilenames : TStrings; Const DestFilename : String);
Procedure Execute(SourceFileNames : Array of string; Const DestFilename : String);
Published
Property FileHeader : TStrings Read FFileHeader Write SetFileHeader;
end;
Implementation
Resourcestring
SErrUnterminatedComment = 'Unterminated comment.';
SErrUnterminatedStringLiteral = 'Unterminated string literal.';
SErrUnterminatedSetInRegexp = 'Unterminated set in Regular Expression literal.';
SerrUnterminatedRegexp = 'Unterminated Regular Expression literal.';
class function TJSONMinifier.iif(B: Boolean; const ifTrue, ifFalse: integer
): integer;
begin
if B then
Result:=ifTrue
else
Result:=ifFalse;
end;
procedure TJSONMinifier.Error(const Msg: string);
begin
Raise EJSONMinifier.Create('JSMIN Error: '+Msg);
end;
procedure TJSONMinifier.SetFileHeader(AValue: TStrings);
begin
if FFileHeader=AValue then Exit;
FFileHeader.Assign(AValue);
end;
procedure TJSONMinifier.Reset;
begin
FA:=EOS;
FB:=EOS;
FLookahead:=EOS;
FX:=EOS;
FY:=EOS;
end;
class function TJSONMinifier.isAlphaNum(c: char): boolean;
begin
Result:= (C in ['a'..'z']) or (c in ['0'..'9']) or (c in ['A'..'Z']) or (C in ['_','$','\']) or (c > #126);
end;
function TJSONMinifier.Get: char;
begin
Result:=FLookahead;
FLookahead:=EOS;
if (Result=EOS) then
if Fin.Read(Result,sizeof(Result))=0 then exit;
if (Result>' ') or (Result in [#10,EOS]) then
Exit;
if (Result=#13) then
Result:=#10
else
Result:=' ';
end;
function TJSONMinifier.Peek: char;
begin
FLookahead := get();
result:=FLookahead;
end;
function TJSONMinifier.Next: char;
var
c : char;
begin
c:= get();
if (c='/') then
case peek of
'/': Repeat
c := get();
until (c <= #10);
'*':
begin
Get();
while (c <> ' ') do
case get of
'*':
begin
if (peek()= '/') then
begin
get();
c:=' ';
end;
end;
EOS:
Error(SErrUnterminatedComment);
end;
end;
end;
FY:=FX;
FX:=c;
Result:=c;
end;
procedure TJSONMinifier.Putc(c: char);
begin
Fout.writebuffer(c,sizeof(c));
end;
procedure TJSONMinifier.Action(d : Byte);
Procedure Do1;
begin
putc(FA);
if ((FY in [#10,' '])
and (FA in ['+','-','*','/'])
and (FB in ['+','-','*','/'])) then
putc(FY);
end;
Procedure Do2;
begin
FA:=FB;
if (FA in ['''','"','`']) then
While true do
begin
putc(FA);
FA:= get();
if (FA=FB) then
break;
if (FA='\') then
begin
putc(FA);
FA:=get();
end;
if (FA=EOS) then
Error(SErrUnterminatedStringLiteral);
end;
end;
begin
if (D=1) then
Do1;
if (D in [1,2]) then
Do2;
FB := next();
if (FB='/') and (FA in ['(',',','=',':','[','!','&','|','?','+','-','~','*','/','{',#10]) then
begin
putc(FA);
if (FA in ['/','*']) then
putc(' ');
putc(FB);
While true do
begin
FA := get();
if (FA='[') then
begin
While true do
begin
putc(FA);
FA := get();
if (FA = ']') then
break;
if (FA = '\') then
begin
putc(FA);
FA := get();
end;
if (FA = EOS) then
Error(SErrUnterminatedSetInRegexp);
end
end
else if (FA = '/') then
begin
case (peek()) of
'/', '*':
Error(SErrUnterminatedSetInRegexp);
end;
Break;
end
else if (FA ='\') then
begin
putc(FA);
FA := get();
end;
if (FA = EOS) then
Error(SErrUnterminatedRegexp);
putc(FA);
end;
FB := next();
end;
end;
procedure TJSONMinifier.Minify;
begin
if (peek()= #$EF) then
begin
get();
get();
get();
end;
FA:=#10;
action(3);
while (FA <> EOS) do
begin
case (FA) of
' ':
action(iif(isAlphanum(FB),1,2));
#10:
case (FB) of
'{', '[', '(', '+', '-', '!', '~':
Action(1);
' ':
Action(3);
else
Action(iif(isAlphanum(FB), 1 , 2));
end;
else
case (FB) of
' ':
Action(iif(isAlphanum(FA),1,3));
#10:
case (FA) of
'}',']',')','+','-','"', '''', '`':
Action(1);
else
Action(iif(isAlphanum(FA), 1, 3));
end;
else
Action(1);
end;
end;
end;
end;
constructor TJSONMinifier.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FFileHeader:=TStringList.Create;
end;
destructor TJSONMinifier.Destroy;
begin
FreeAndNil(FFileHeader);
inherited Destroy;
end;
procedure TJSONMinifier.Execute(const SourceFilename, DestFilename: String);
Var
Src,Dest : TBufStream;
begin
Dest:=Nil;
Src:=TReadBufStream.Create(TFileStream.Create(SourceFileName,fmOpenRead or fmShareDenyWrite),1000);
try
Src.SourceOwner:=True;
Dest:=TWriteBufStream.Create(TFileStream.create(DestFileName,fmCreate),1000);
Dest.SourceOwner:=True;
Execute(Src,Dest);
finally
Src.Free;
Dest.Free;
end;
end;
procedure TJSONMinifier.DoHeader;
Var
S,L : String;
begin
For S in FFileHeader do
begin
L:='// '+S+sLineBreak;
Fout.WriteBuffer(L[1],Length(L));
end;
end;
procedure TJSONMinifier.Execute(Source, Dest: TStream);
begin
Fin:=Source;
Fout:=Dest;
try
Reset;
DoHeader;
Minify;
finally
Fin:=Nil;
Fout:=Nil;
end;
end;
procedure TJSONMinifier.Execute(SourceFilenames: TStrings;const DestFilename: String);
Var
Src,Dest : TBufStream;
I : Integer;
begin
Dest:=Src;
Dest:=TWriteBufStream.Create(TFileStream.create(DestFileName,fmCreate),1000);
try
Dest.SourceOwner:=True;
for I:=0 to SourceFileNames.Count-1 do
begin
Src:=TReadBufStream.Create(TFileStream.Create(SourceFileNames[i],fmOpenRead or fmShareDenyWrite),1000);
Src.SourceOwner:=True;
Execute(Src,Dest);
FreeAndNil(Src);
end;
finally
FreeAndNil(Src);
FreeAndNil(Dest);
end;
end;
procedure TJSONMinifier.Execute(SourceFileNames: array of string;
const DestFilename: String);
Var
S : TStrings;
begin
S:=TStringList.Create;
try
S.AddStrings(SourceFileNames);
Execute(S,DestFileName);
finally
S.Free;
end;
end;
end.