Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a Script bundle from multiple locations

Let's assume our app is offline, i.e. we can't use 3rd party CDNs thus we're creating our own. I'd like to host all of the vendor scripts in a separate (Parent) web app and then include them in the bundles in several other MVC Apps.

e.g.

  • http://localhost/parentWeb/Scripts/jquery.js
  • http://localhost/parentWeb/Scripts/jquery-ui.js
  • http://localhost/parentWeb/Scripts/globalize.js

I'd like to include in the ASP.NET MVC App Website located in: http://localhost/parentWeb/childWeb

i.e. do something like this:

bundles.UseCdn = true;
bundles.Add(
    new ScriptBundle(
        "~/bundles/VendorScripts",
        "http://localhost/parentWeb/Scripts/jquery.js",
        "http://localhost/parentWeb/Scripts/jquery-ui.js",
        "http://localhost/parentWeb/Scripts/globalize.js"));

...which of course isn't currently possible. Is there a good workaround?

like image 428
Ruslan Avatar asked Nov 27 '13 17:11

Ruslan


4 Answers

You can't bundle external resources. If you think about it, it makes sense why you can't. It would require the bundler to actually download the resource and save it to the filesystem before it could work with it, and of course do it all asynchronously with some sort of fallback if the external resource couldn't be reached. And, then, it would have to do this on every page load because it can't check for lastmod (and therefore, know whether it actually needs to rebundle or not) without fetching the resource first.

If you use a CDN resource, the bundler merely prints the URL directly to the page; it doesn't make any modifications. Even then, it only lets you create a "bundle" of just that one URL, because 1) it wouldn't make sense to bundle multiple CDN resources since that would defeat the purpose of a CDN and 2) the bundle only exists in this scenario to provide a fallback if the CDN resource is unavailable. Otherwise, you would be served just as well by just hardcoding it to the page and not worrying about setting up a bundle at all.

like image 59
Chris Pratt Avatar answered Nov 12 '22 06:11

Chris Pratt


I know this is an old topic, but I came here looking for an actual way to bundle CDN resources. From @Chris Pratt's answer, I understood it wasn't possible.

If you're wondering, I am working on optimizing an an existing project according to Google's Web Performance Best Practises which gives a low score when there are multiple script tags and a higher one when all scripts are bundled into a single script reference.

I needed a way to bundle all the CDN script resources as well as local resources in order. I worked on this github repo, which solved my problem.

With it, you build a bundle with a list of bundles, each containing a reference to the cdn resource, local resource to save to, and a Boolean indicating whether or not you want the bundle to be minified.

List<Bundle> jsBundles = new List<Bundle>();
jsBundles.Add(new Bundle("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js", @"~/jquery.min.js", Bundle.BundleType.JavaScript, false));
jsBundles.Add(new Bundle("https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js", @"~/jquery-ui.min.js", Bundle.BundleType.JavaScript, false));
jsBundles.Add(new Bundle(@"~/my-local-script.js", Bundle.BundleType.JavaScript, true));

To place on the page, you use

@jsBundles.Load();

This will process all bundles in the list, downloading content for bundles that have not been downloaded in the last 24 hours (It updates every 24 hours or when the web application restarts). All content downloaded will be placed in local files (where specified).

All content will be combined into the final result which will be spooled into the page in a script tag (or link tag for CSS).

The Load function also accepts a local File URL for the final script/css content. If specified, a tag with a src to the relative path for that local file will be given instead. E.g.

@jsBundles.Load("~/js/all-my-scripts.js");

The above statement will return something like:

<script src="~/js/all-my-scripts.js"></script>

An async attribute may be added to the script tag if the second parameter of the Load function is provided.

It also works on css cdn resources too. E.g.

List<Bundle> cssBundles = new List<Bundle>();
cssBundles.Add(new Bundle("https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.css", @"~/jquery.ui.css", Bundle.BundleType.CSS, false));
cssBundles.Add(new Bundle(@"~/css/my-local-style.css", Bundle.BundleType.CSS, true));


@cssBundles.Load("~/css/all-my-styles.css");

This is for the benefit of those, who like me, came in here looking for a way to actually bundle CDN resources.

like image 35
Ikechi Michael Avatar answered Nov 12 '22 06:11

Ikechi Michael


I have found a solution, which has nothing to do with CDN. Basically, granted the childWeb is hosted in the parentWeb's subdirectory, the following bundle configuration in the childWeb apps picks the file from the parentWeb and bundles them as usual:

bundles.Add(
    new ScriptBundle(
        "~/bundles/VendorScripts").Include(
        "~/../Scripts/jquery.js",
        "~/../Scripts/Scripts/jquery-ui.js",
        "~/../Scripts/globalize.js"));

the important bit being: ~/../, which takes you one level up from the root location.

like image 5
Ruslan Avatar answered Nov 12 '22 07:11

Ruslan


To use ScriptBundles with CDN resources, you need to use the overloaded constructor. Unfortunately you need to specify multiple ScriptBundles per file.

Here's a great blog post explaining things: http://www.hanselman.com/blog/CDNsFailButYourScriptsDontHaveToFallbackFromCDNToLocalJQuery.aspx

And here's a code snippet:

bundles.UseCdn = true;
var bundle = new ScriptBundle("~/bundles/bundleNameHere", "//cdn.host.path/to/file");
// Path to the version of the file on your server (in case the CDN fails)
bundle.Include("~/../Scripts/path/to/file");
// JS expression to run, to test if CDN delivered the file or not
bundle.CdnFallbackExpression = "window.ValueYouExpectToBeTruthy";

bundles.Add(bundle);
like image 2
Bryan Rayner Avatar answered Nov 12 '22 06:11

Bryan Rayner