I am building a ASP.NET MVC4 based jQuery plug-in and I would like to use the standard Microsoft Bundler.
Now normally when I use the bundler the final usage is this:
@Scripts.Render(BundleConfig.jsBundleFile)
With a output like this in debug:
<script src="/Scripts/..."></script>
<script src="/Scripts/..."></script>
<script src="/Scripts/..."></script>
<script src="/Scripts/..."></script>
<script src="/Scripts/..."></script>
<script src="/Scripts/..."></script>
And a single tag to the minified and combined file in the official release.
However, because we are building a jQuery plug-in I want to use the minifier to insert the actual JavaScript from the various files i.e. in DEBUG, when I have Object1
defined in one file and Object2
defined in another then I get this:
/* Content from file #1 */
var Object1 = function() { /* Un-minified content of Object1 */ };
/* Content from file #2 */
var Object2 = function() { /* Un-minified content of Object2 */ };
and in the release I would get the minified content.
From the MSDN I saw that the bundler, by default can't do that. But I am wondering is there an extension that can do this or another minifing library that can? Though I'd prefer to stay with the Microsoft Bundler.
In my own research on how to extend the System.Web.Optimizer I found the CodePlex project of System.Web.Optimization
, where it is said it's not open source as of yet, with makes the extending kinda difficult to anyone not really working on it.
EDIT : So knowing that I can't really expand the System.Web.Optimizer I went with a compromise solution and this is what I ended up using.
In the Controller:
public JavaScriptResult jQueryComponent()
{
JavaScriptResult ret = new JavaScriptResult();
ClientSettings Model = new ClientSettings();
#if DEBUG
List<string> jsFiles = App_Start.BundleConfig.Main.FilesToBeBundledJS;
StringBuilder bundleBuilder = new StringBuilder();
foreach (string file in jsFiles)
{
bundleBuilder.Append(System.IO.File.ReadAllText(Server.MapPath(file)));
}
ViewBag.bundledJS = bundleBuilder.ToString();
#else
StringBuilder urlBuilder = new StringBuilder("http://localhost");
int port = Request.Url.Port;
if (port != -1) {
urlBuilder.Append(':');
urlBuilder.Append(port);
}
urlBuilder.Append(Scripts.Url(App_Start.BundleConfig.Main.jsBundleFile));
WebClient wc = new WebClient();
byte[] raw = wc.DownloadData(urlBuilder.ToString());
string bundledJS = System.Text.Encoding.UTF8.GetString(raw);
ViewBag.bundledJS = bundledJS;
#endif
ret.Script = RenderRazorViewToString("~/Views/Main/jQueryComponent.cshtml", Model);
return ret;
}
private string RenderRazorViewToString(string viewName, object model)
{
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
And in the View (jQueryComponent.cshtml):
@{
Layout = null;
}
if (typeof jQuery != 'function') {
throw "This is a jQuery plugin i.e. jQuery needs to be defined first";
}
(function ($) {
//Adds the bundled JS into the plug-in
@Html.Raw(ViewBag.bundledJS)
$.fn.jsPlugIn = function() {
/*
Creates a new instance of [MainObject] with is defined in the bundle.
*/
//uses the bundled JS
var Main = new MainObject();
Main.doSomething();
return this;
};
}(jQuery));
Any ideas how to improve this are welcome alongside the proper extention to the bundler.
Although works, this is far from ideal and I am well aware of this.
Please do not criticise this approach unless you have an alternative.
JavaScript became the default language for HTML5 and modern browsers. Therefore, now adding text/javascript isn't required in <script> tag.
SCRIPT tags have a negative impact on page performance because of their blocking behavior. While scripts are being downloaded and executed, most browsers won't download anything else.
Placing scripts in external files has some advantages: It separates HTML and code. It makes HTML and JavaScript easier to read and maintain. Cached JavaScript files can speed up page loads.
In the "Preferences" window select the "Security" tab. In the "Security" tab section "Web content" mark the "Enable JavaScript" checkbox. Click on the "Reload the current page" button of the web browser to refresh the page.
You can do what the BundleHandler does: Get a bundle based on the virtual path (same one you would use for
@Script.Render
), generate the response and write the contents.
@{
var context = new BundleContext(this.Context, BundleTable.Bundles, string.Empty);
var bundle = BundleTable.Bundles.GetBundleFor("~/VIRTUALPATH");
var response = bundle.GenerateBundleResponse(context);
var content = response.Content;
this.WriteLiteral(content);
}
Or in short:
@Html.Raw(BundleTable.Bundles.GetBundleFor("~/VIRTUALPATH").GenerateBundleResponse(new BundleContext(this.Context, BundleTable.Bundles, string.Empty)).Content)
EDIT: To get unminified content in debug, we can do what Scripts does: Get the bundle again, but instead of generating the response, we iterate its files and write their contents (delimited).
@{
var context = new BundleContext(this.Context, BundleTable.Bundles, string.Empty);
var bundle = BundleTable.Bundles.GetBundleFor("~/VIRTUALPATH");
if (BundleTable.EnableOptimizations)
{
var response = bundle.GenerateBundleResponse(context);
var content = response.Content;
this.WriteLiteral(content);
}
else
{
var files = bundle.EnumerateFiles(context);
foreach (var file in files)
{
var stream = file.VirtualFile.Open();
using (var reader = new StreamReader(stream))
{
this.Output.Write("{0}{1}", reader.ReadToEnd(), bundle.ConcatenationToken);
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With