AjaxMinTask additional options

Feb 9, 2012 at 9:11 PM

Some time ago there was a discussion which proposed a set of radical changes to AjaxMinTask. I would like to propose rather simple extensions.

First, let me describe the process I use. I'll cover JS version here, yet the same process can be applied to CSS files as well.

I prefer to have one JS source per class/component - this way it's much easier to manage the code. Then I have the "project" file which specifies what components to bundle together in what order - it's a simple text file that specify source files one per line. The output of the "project" is either .debug.js or .min.js file depending on configuration.

In debug mode project file compiles to a set of document.write("<script src=..."); directives pointing to original JS files. If runtime error is encountered, VS debugger pops up, and I can fix a problem and simply refresh the page to get updated version of the script. In this process AjaxMin runs merely to perform syntax verification. I'm not really interested in the output, yet build system needs it to recompile changed files only.

In release mode AjaxMinTask runs to minify each source file individually and then project file compiles to a concatenation of minified files.

In neither case the output of AjaxMinTask is used directly on a website and I'd rather prefer keep site folders clean of intermediate output. At the moment I let AjaxMinTask compile to the source folder and then move the output under $(IntermediateOutputPath).

Would AjaxMinTask support DestinationFiles option to provide either 1-1 mapping to SourceFiles or a single file to produce contatenated output or, at least, DestinationFolder option, it would make my life easier :)

Coordinator
Feb 10, 2012 at 9:16 PM

You can specify multiple input files to the tool, and it will concatenate and minify them into a single output file already, in one step. Is there a reason that wouldn't work for you? You can also use the -xml option to specify far more complicated combinations of input files into a series of related or unrelated output files.

So you're asking if I could add a DestinationFiles property to the task, right? Can you give me an example of how it would work? I'm not all that clear on what the proposed process might look like.

Feb 11, 2012 at 5:01 PM
Edited Feb 14, 2012 at 10:23 PM

Mere perfectionism. I know .exe can do what I need, but:

  1. I think invocation of in-process task .dll is a bit more performance effective
  2. Using the task I can set build system up to "recompile" changed files only.

Assuming there is DestinationFiles task parameter (oh, well, two, in fact: JsDestinationFiles and CssDestinationFiles) I can specify one-to-one mapping between source file path and output path as, for example:

<JsFile Include="*.js"/>
<AjaxMin JsSourceFiles="@(JsFile)"
         JsDestinationFiles="@(JsFile->'$(OutputPath)%(RelativeDir)%(Filename).min.js')
         ... other options  />

The length of SourceFiles and DestinationFiles arrays are equal, for each source file you have an output path. This approach is used in many other MSBuild tasks such as Copy or Move. BTW, when the output specified this way, you can get rid of JsSourceExtensionPattern and JsTargetExtension properties.

Another usage scenario: if single file is provided for DestinationFiles property let the task to concatenate all source files:

<AjaxMin JsSourceFiles="@(JsFile)"
	JsDestinationFiles="concatenated.js"
	 ... other options
/>
 
Feb 11, 2012 at 5:06 PM

Here is complete .targets file I’m using at the moment:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<UsingTask TaskName="AjaxMin" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\MicrosoftAjax\AjaxMinTask.dll" />

<PropertyGroup>

<JsOutSuffix>.min.js</JsOutSuffix>

<!-- kill moving "in" inside of for() - fix for Opera 11 -->

<JsSwitches>-term -warn:9 -ignore:JS1267 –global:jQuery,$ -kill:0x40000000000</JsSwitches>

</PropertyGroup>

<PropertyGroup Condition="'$(ConfigurationName)'=='Debug'">

<JsOutSuffix>.debug.js</JsOutSuffix>

<JsSwitches>-debug -define:DEBUG $(JsSwitches) -minify:false</JsSwitches>

</PropertyGroup>

<ItemGroup>

<JsProj Include="**\*.jsproj">

<JsProjOutput>%(RelativeDir)%(Filename)$(JsOutSuffix)</JsProjOutput>

</JsProj>

</ItemGroup>

<Target Name="ScanJsProj"

Inputs="%(JsProj.Identity)"

Outputs="%(JsProj.Identity).touch">

<ReadLinesFromFile File="%(JsProj.Identity)">

<Output TaskParameter="Lines" ItemName="JsProjLines" />

</ReadLinesFromFile>

<PropertyGroup>

<JsProjPath>%(JsProj.RelativeDir)</JsProjPath>

<JsProjFile>$(JsProjPath)%(JsProj.Filename)</JsProjFile>

<JsProjOutput>$(JsProjFile)$(JsOutSuffix)</JsProjOutput>

</PropertyGroup>

<ItemGroup>

<JsFile Include="@(JsProjLines->'$(JsProjPath)%(Identity)')">

<JsProj>$(JsProjFile)</JsProj>

<JsOutput>$(IntermediateOutputPath)$(JsProjFile)\%(RelativeDir)%(Filename)$(JsOutSuffix)</JsOutput>

<JsProjFile>$(JsProjFile)</JsProjFile>

<JsProjOutput>$(JsProjOutput)</JsProjOutput>

</JsFile>

</ItemGroup>

</Target>

<Target Name="CleanJsProjects" AfterTargets="Clean" DependsOnTargets="ScanJsProj">

<Message Text="Cleaning .js output" Importance="high"/>

<Delete Files="@(JsProj->'%(JsProjOutput)')"/>

<Delete Files="@(JsFile->'%(JsOutput)')"/>

</Target>

<Target Name="BuildJs"

DependsOnTargets="ScanJsProj"

Inputs="@(JsFile)"

Outputs="@(JsFile->'%(JsOutput)')">

<Message Text="AjaxMin.js: $(JsSwitches) @(JsFile->'%(Filename)%(Extension)',', ')" Importance="high"/>

<AjaxMin JsSourceFiles="@(JsFile)"

JsSourceExtensionPattern="\.js$"

JsTargetExtension="$(JsOutSuffix)"

TreatWarningsAsErrors="true"

Switches="$(JsSwitches)" />

<Move SourceFiles="@(JsFile->'%(RelativeDir)%(Filename)$(JsOutSuffix)')"

DestinationFiles="@(JsFile->'%(JsOutput)')"/>

</Target>

<Target Name="BuildJsProjDebug"

AfterTargets="Compile"

Condition="'$(ConfigurationName)'=='Debug'"

DependsOnTargets="ScanJsProj;BuildJs"

Inputs="@(JsFile);%(JsProj.Identity)"

Outputs="%(JsFile.JsProjOutput)">

<Message Text="Combining %(JsFile.JsProjOutput)" Importance="high"/>

<PropertyGroup>

<JsImpFiles>[@(JsFile->'"%(Identity)"',',')]</JsImpFiles>

<JsImpFilesNorm>$(JsImpFiles.Replace('\','/'))</JsImpFilesNorm>

<JsImports>(function(d,r){ for(var v=$(JsImpFilesNorm),i=0%3Bi<v.length%3Bi++){d.write("<script src=\""+r+"/"+v[i]+"\"><\/script>")} })(document,gRootURL)%3B</JsImports>

</PropertyGroup>

<WriteLinesToFile

File="%(JsFile.JsProjOutput)" Lines="$(JsImports)" Overwrite="true"/>

</Target>

<Target Name="BuildJsProjRelease"

AfterTargets="Compile"

Condition="'$(ConfigurationName)'!='Debug'"

DependsOnTargets="ScanJsProj;BuildJs"

Inputs="@(JsFile->'%(JsOutput)');%(JsProj.Identity);"

Outputs="%(JsFile.JsProjOutput)">

<Message Text="Combining %(JsFile.JsProjOutput)" Importance="high"/>

<ReadLinesFromFile

File="%(JsFile.JsOutput)">

<Output TaskParameter="Lines" ItemName="JsImports"/>

</ReadLinesFromFile>

<WriteLinesToFile

File="%(JsFile.JsProjOutput)" Lines="@(JsImports)" Overwrite="true"/>

</Target>

</Project>