I have recently been working on converting my web application to use requirejs. I have been very happy with the benefits of moving to a modular approach but optimizing using r.js during the deploy to test/production did not seem very elegant. Most solutions suggest building your javascript into a release folder within your project and then manipulating your initial requirejs starting point or the configuration to point the release folder (see here for an example). This seem to cause two issue:
- An additional folder and contents for release that I had no interest in seeing in my development workspace.
- A non-standard way of declaring your initial requirejs starting point.
What I wanted was for the optimization to occur during the deploy process – web publish in my case. I soon discovered that you can extend the web publish process in either the .pubxml publish profile files or by adding a [projectname].wpp.targets file. These files are msbuild files and thanks to a couple of posts by Sayed Ibrahim Hashimi and by poking around the msbuild files used for publishing, I figured out that during the publish I could:
- Run node.exe to build the requirejs javascript
- Replace the non-built javascript with the version I built in the previous step
This meant that my requirejs starting point would not need changing at all and the building of the javascript can occur outside my development workspace.
To build the javascript you need a build.js configuration file (see requirejs documentation), node.exe and r.js. You need to put them in a directory that can be referenced during the publish process. The build.js should be configured to output the built files to a directory that can also be accessed during the publish process. The rest of the magic is done in my .wpp.targets file, which I share below with comments on how to customise it to suit your own setup:
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <!-- This is the path to where node.exe is along with r.js and your build.js file for requirejs optimization--> <!-- You need to replace the bit in brackets with a relative path from your project folder--> <RequireJsBuildDir Condition=" '$(RequireJsBuildDir)' == '' ">$(MSBuildProjectDirectory)\[PATH TO FOLDER]</RequireJsBuildDir> <NodeExePath Condition=" '$(NodeExePath)' == '' ">$(RequireJsBuildDir)\node.exe</NodeExePath> <BuildRequireJSCommand>"$(NodeExePath)" r.js -o build.js </BuildRequireJSCommand> <!--This extends the publish process at the point where it has gathered the list of files to publish--> <OnAfterPipelineCollectFilesPhase> ReplaceJs; $(OnAfterPipelineCollectFilesPhase); </OnAfterPipelineCollectFilesPhase> </PropertyGroup> <!--This target builds the javascript--> <Target Name="AfterBuild"> <Message Text="Building requirejs javascript..." Importance="high"></Message> <Exec Command="$(BuildRequireJSCommand)" ContinueOnError="false" WorkingDirectory="$(RequireJsBuildDir)"/> </Target> <!--This target removes everything from the js folder in your project with the output of the built javascript--> <Target Name="ReplaceJs"> <Message Text="Replacing javascript with built version..." Importance="high"/> <ItemGroup> <!--Replace the bit in brackets with the path to the requirejs build output relative to this file--> <_BuiltJavascript Include="$(MSBuildThisFileDirectory)[PATH TO FOLDER]\**\*" /> <!--This removes the unbuilt javascript from the project, update the folder if yours isnt in /js --> <FilesForPackagingFromProject Remove="js\**\*" ></FilesForPackagingFromProject> <!--This puts the files found in the build output folder into the /js folder. Change the folder as necessary --> <FilesForPackagingFromProject Include="%(_BuiltJavascript.Identity)" > <DestinationRelativePath>js\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath> </FilesForPackagingFromProject> </ItemGroup> </Target> </Project> |