Skip to content

Commit 5c0dadc

Browse files
committed
Named bundles
Added support for named bundles established through configuration.
1 parent f93d522 commit 5c0dadc

20 files changed

+277
-43
lines changed

Jacobsoft.Amd.Test/AmdControllerTests.cs

+30-18
Original file line numberDiff line numberDiff line change
@@ -257,28 +257,30 @@ public void GetModule_WithMinifier()
257257
[TestMethod]
258258
public void GetBundle()
259259
{
260-
var moduleA = Mock.Of<IModule>();
261-
var moduleB = Mock.Of<IModule>();
262-
var moduleD = Mock.Of<IModule>();
260+
this.ArrangeModule("a", "a");
261+
this.ArrangeModule("b", "b");
262+
this.ArrangeModule("c/d", "d");
263263

264-
Mock.Get(moduleA).Setup(m => m.Content).Returns("a");
265-
Mock.Get(moduleB).Setup(m => m.Content).Returns("b");
266-
Mock.Get(moduleD).Setup(m => m.Content).Returns("d");
264+
var result = this.controller.Bundle("a+b+c/d");
265+
Assert.IsInstanceOfType(result, typeof(JavaScriptResult));
266+
Assert.AreEqual("a;b;d", (result as JavaScriptResult).Script);
267+
}
268+
269+
[TestMethod]
270+
public void GetBundle_WithNamedBundle()
271+
{
272+
this.ArrangeModule("a", "a");
273+
this.ArrangeModule("b", "b");
274+
this.ArrangeModule("c/d", "d");
267275

268276
this.autoMocker
269-
.GetMock<IModuleResolver>()
270-
.Setup(r => r.Resolve("a"))
271-
.Returns(moduleA);
272-
this.autoMocker
273-
.GetMock<IModuleResolver>()
274-
.Setup(r => r.Resolve("b"))
275-
.Returns(moduleB);
276-
this.autoMocker
277-
.GetMock<IModuleResolver>()
278-
.Setup(r => r.Resolve("c/d"))
279-
.Returns(moduleD);
277+
.GetMock<IAmdConfiguration>()
278+
.Setup(c => c.Bundles)
279+
.Returns(new Dictionary<string, IEnumerable<string>> {
280+
{ "common", new[] { "a", "b", "c/d" } }
281+
});
280282

281-
var result = this.controller.Bundle("a+b+c/d");
283+
var result = this.controller.Bundle("common");
282284
Assert.IsInstanceOfType(result, typeof(JavaScriptResult));
283285
Assert.AreEqual("a;b;d", (result as JavaScriptResult).Script);
284286
}
@@ -315,6 +317,16 @@ private void ArrangeFile(string filePath, string content)
315317
.Returns(new MemoryStream(Encoding.UTF8.GetBytes(content)));
316318
}
317319

320+
private void ArrangeModule(string id, string content)
321+
{
322+
var moduleA = Mock.Of<IModule>();
323+
Mock.Get(moduleA).Setup(m => m.Content).Returns(content);
324+
this.autoMocker
325+
.GetMock<IModuleResolver>()
326+
.Setup(r => r.Resolve(id))
327+
.Returns(moduleA);
328+
}
329+
318330
private void ArrangeMinifier(string content, string minifiedContent)
319331
{
320332
this.ArrangeMinifier(m => m.Minify(content), minifiedContent);

Jacobsoft.Amd.Test/App.config

+6-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
<module id="baz" export="baz" dependencies="bat"/>
2323
<module id="bat"/>
2424
</shims>
25+
<bundles>
26+
<bundle id="bundleA">a b c</bundle>
27+
<bundle id="bundleB">d, e</bundle>
28+
</bundles>
2529
</jacobsoft.amd>
2630

2731
<jacobsoft.amd.dynamic
@@ -61,5 +65,6 @@
6165
versionProvider="Jacobsoft.Amd.Test.StaticVersionProvider, Jacobsoft.Amd.Test"/>
6266

6367
<jacobsoft.amd.minifying
64-
loaderUrl="require.js"/>
68+
loaderUrl="require.js"
69+
minifier="Jacobsoft.Amd.Test.TestMinifier, Jacobsoft.Amd.Test"/>
6570
</configuration>

Jacobsoft.Amd.Test/Config/AmdConfigurationSectionTests.cs

+16
Original file line numberDiff line numberDiff line change
@@ -128,5 +128,21 @@ public void Shims()
128128

129129
Assert.AreEqual("bat", shims[2].Id);
130130
}
131+
132+
[TestMethod]
133+
public void Bundles()
134+
{
135+
var configSection = ConfigurationManager.GetSection("jacobsoft.amd")
136+
as AmdConfigurationSection;
137+
var bundles = configSection.Bundles.ToList();
138+
139+
Assert.AreEqual(2, bundles.Count);
140+
141+
Assert.AreEqual("bundleA", bundles[0].Id);
142+
Assert.IsTrue(bundles[0].Modules.SequenceEqual(new[] { "a", "b", "c" }));
143+
144+
Assert.AreEqual("bundleB", bundles[1].Id);
145+
Assert.IsTrue(bundles[1].Modules.SequenceEqual(new[] { "d", "e" }));
146+
}
131147
}
132148
}

Jacobsoft.Amd.Test/Config/AmdConfigurationTests.cs

+31
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ public void Initialize()
2121
this.mocker = new AutoMoqer();
2222
}
2323

24+
[TestMethod]
25+
public void Constructor_WithNoConfigFile()
26+
{
27+
var config = new AmdConfiguration(null, Mock.Of<IVersionProvider>());
28+
Assert.AreEqual(0, config.Shims.Count);
29+
Assert.AreEqual(0, config.Bundles.Count);
30+
}
31+
2432
[TestMethod]
2533
public void Constructor_ReadsConfigFile()
2634
{
@@ -82,6 +90,29 @@ public void Constructor_WithMinifierInConfigSection_ActivatesObject()
8290
Assert.IsInstanceOfType(config.Minifier, expectedMinifierType);
8391
}
8492

93+
[TestMethod]
94+
public void Constructor_WithBundles()
95+
{
96+
var bundleA = new Mock<IBundle>();
97+
bundleA.Setup(b => b.Id).Returns("bundleA");
98+
bundleA.Setup(b => b.Modules).Returns(new[] { "a", "b", "c" });
99+
100+
var bundleB = new Mock<IBundle>();
101+
bundleB.Setup(b => b.Id).Returns("bundleB");
102+
bundleB.Setup(b => b.Modules).Returns(new[] { "d", "e" });
103+
104+
var configSection = this.mocker.GetMock<IAmdConfigurationSection>();
105+
configSection
106+
.Setup(s => s.Bundles)
107+
.Returns(new[] { bundleA.Object, bundleB.Object });
108+
109+
var config = this.GetConfiguration();
110+
Assert.IsTrue(config.Bundles.ContainsKey("bundleA"));
111+
Assert.IsTrue(config.Bundles["bundleA"].SequenceEqual(new[] { "a", "b", "c" }));
112+
Assert.IsTrue(config.Bundles.ContainsKey("bundleB"));
113+
Assert.IsTrue(config.Bundles["bundleB"].SequenceEqual(new[] { "d", "e" }));
114+
}
115+
85116
private AmdConfiguration GetConfiguration()
86117
{
87118
return new AmdConfiguration(

Jacobsoft.Amd.Test/HtmlHelperExtensionsTests.cs

+24-1
Original file line numberDiff line numberDiff line change
@@ -259,14 +259,37 @@ public void ModuleBundle()
259259
Mock.Get(moduleA).Setup(m => m.Dependencies).Returns(new[] { moduleC });
260260
Mock.Get(moduleC).Setup(m => m.Dependencies).Returns(new[] { moduleD });
261261

262-
var html = this.htmlHelper.ModuleBundle("a");
262+
var html = this.htmlHelper.ModuleBundle(new[] { "a" });
263263
var scripts = this.ExtractScriptTags(html);
264264

265265
this.AssertScriptInclude(scripts[0], "/amd/loader");
266266
this.AssertScriptInclude(scripts[1], "/amd/config");
267267
this.AssertScriptInclude(scripts[2], "/amd/bundle/a%2cb/c%2cb/d");
268268
}
269269

270+
[TestMethod]
271+
public void ModuleBundle_WithNamedBundle()
272+
{
273+
this.autoMocker
274+
.GetMock<IAmdConfiguration>()
275+
.Setup(cfg => cfg.Bundles)
276+
.Returns(new Dictionary<string, IEnumerable<string>> {
277+
{ "common", new[] { "a", "b" } }
278+
});
279+
var html = this.htmlHelper.ModuleBundle("common");
280+
var scripts = this.ExtractScriptTags(html);
281+
282+
this.AssertScriptInclude(scripts[0], "/amd/loader");
283+
this.AssertScriptInclude(scripts[1], "/amd/config");
284+
this.AssertScriptInclude(scripts[2], "/amd/bundle/common");
285+
}
286+
287+
[TestMethod, ExpectedException(typeof(ArgumentException))]
288+
public void ModuleBundle_WithUnknownNamedBundle()
289+
{
290+
this.htmlHelper.ModuleBundle("common");
291+
}
292+
270293
private IList<XElement> ExtractScriptTags(IHtmlString html)
271294
{
272295
// To help us inspect the HTML, we will parse as XML.

Jacobsoft.Amd/AmdController.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,12 @@ public JavaScriptResult Bundle(
130130
string id,
131131
[Bind(Prefix = "v")] string version = null)
132132
{
133+
var moduleIds = this.config.Bundles.OrEmpty().ContainsKey(id)
134+
? this.config.Bundles[id]
135+
: id.Split(new[] { '+', ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
133136
return this.MinifiedJavaScript(string.Join(
134137
";",
135-
from moduleId in id.Split(new[] { '+', ' ', ',' }, StringSplitOptions.RemoveEmptyEntries)
138+
from moduleId in moduleIds
136139
let module = this.resolver.Resolve(moduleId)
137140
where module != null
138141
select module.Content

Jacobsoft.Amd/Config/AmdConfiguration.cs

+16-8
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,28 @@ internal AmdConfiguration(
1919
IAmdConfigurationSection configSection,
2020
IVersionProvider defaultVersionProvider)
2121
{
22-
if (configSection != null)
22+
if (configSection == null)
23+
{
24+
this.Shims = new Dictionary<string, IShim>();
25+
this.Bundles = new Dictionary<string, IEnumerable<string>>();
26+
}
27+
else
2328
{
2429
this.LoaderUrl = configSection.LoaderUrl;
2530
this.ModuleRootUrl = configSection.RootModuleUrl;
2631
this.ScriptLoadingMode = configSection.ScriptLoadingMode;
2732
this.Shims = configSection.Shims.ToDictionary(s => s.Id);
33+
this.Bundles = configSection.Bundles.ToDictionary(b => b.Id, b => b.Modules);
34+
35+
if (configSection.Minifier != null)
36+
{
37+
this.Minifier = Activator.CreateInstance(configSection.Minifier) as IScriptMinifier;
38+
}
2839
}
2940

30-
this.VersionProvider = configSection.VersionProvider == null
41+
this.VersionProvider = configSection.IfExists(c => c.VersionProvider) == null
3142
? defaultVersionProvider
3243
: Activator.CreateInstance(configSection.VersionProvider) as IVersionProvider;
33-
34-
if (configSection.Minifier != null)
35-
{
36-
this.Minifier = Activator.CreateInstance(configSection.Minifier) as IScriptMinifier;
37-
}
3844
}
3945

4046
public string LoaderUrl { get; set; }
@@ -47,6 +53,8 @@ internal AmdConfiguration(
4753

4854
public ScriptLoadingMode ScriptLoadingMode { get; set; }
4955

50-
public IDictionary<string, IShim> Shims { get; private set; }
56+
public IDictionary<string, IShim> Shims { get; set; }
57+
58+
public IDictionary<string, IEnumerable<string>> Bundles { get; set; }
5159
}
5260
}

Jacobsoft.Amd/Config/AmdConfiguration.xsd

+15
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
</xs:sequence>
1818
</xs:complexType>
1919
</xs:element>
20+
<xs:element name="bundles" minOccurs="0" maxOccurs="1">
21+
<xs:complexType>
22+
<xs:sequence>
23+
<xs:element name="bundle" type="ModuleBundle" minOccurs="0" maxOccurs="unbounded"/>
24+
</xs:sequence>
25+
</xs:complexType>
26+
</xs:element>
2027
</xs:sequence>
2128
<xs:attribute name="loaderUrl" type="xs:string" use="optional"/>
2229
<xs:attribute name="moduleRootUrl" type="xs:string" default="~/Scripts"/>
@@ -30,6 +37,14 @@
3037
<xs:attribute name="dependencies" type="xs:string" use="optional"/>
3138
<xs:attribute name="export" type="xs:string" use="optional"/>
3239
</xs:complexType>
40+
41+
<xs:complexType name="ModuleBundle">
42+
<xs:simpleContent>
43+
<xs:extension base="xs:string">
44+
<xs:attribute name="id" type="xs:string" use="required"/>
45+
</xs:extension>
46+
</xs:simpleContent>
47+
</xs:complexType>
3348

3449
<xs:simpleType name="ScriptLoadingMode">
3550
<xs:restriction base="xs:string">

Jacobsoft.Amd/Config/AmdConfigurationSection.cs

+16
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class AmdConfigurationSection : ConfigurationSection, IAmdConfigurationSe
1919
private const string VersionProviderAttribute = "versionProvider";
2020
private const string MinifierAttribute = "minifier";
2121
private const string ShimsElement = "shims";
22+
private const string BundlesElement = "bundles";
2223

2324
[ConfigurationProperty("xmlns")]
2425
private string Ignored { get; set; }
@@ -68,11 +69,26 @@ public AmdShimCollectionConfigurationSection Shims
6869
}
6970
}
7071

72+
[ConfigurationCollection(typeof(AmdBundleConfigurationElement), AddItemName = "bundle")]
73+
[ConfigurationProperty(BundlesElement)]
74+
public AmdModuleBundleCollectionConfigurationSection Bundles
75+
{
76+
get
77+
{
78+
return base[BundlesElement] as AmdModuleBundleCollectionConfigurationSection;
79+
}
80+
}
81+
7182
IEnumerable<IShim> IAmdConfigurationSection.Shims
7283
{
7384
get { return this.Shims; }
7485
}
7586

87+
IEnumerable<IBundle> IAmdConfigurationSection.Bundles
88+
{
89+
get { return this.Bundles; }
90+
}
91+
7692
private class ScriptLoadingModeConfigConverter : ConfigurationConverterBase
7793
{
7894
public override object ConvertFrom(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Configuration;
5+
using System.Linq;
6+
using System.Text;
7+
using Jacobsoft.Amd.Internals.Config;
8+
9+
namespace Jacobsoft.Amd.Config
10+
{
11+
public class AmdModuleBundleCollectionConfigurationSection
12+
: ConfigurationElementCollection, IEnumerable<IBundle>
13+
{
14+
protected override ConfigurationElement CreateNewElement()
15+
{
16+
return new AmdBundleConfigurationElement();
17+
}
18+
19+
protected override object GetElementKey(ConfigurationElement element)
20+
{
21+
return (element as AmdBundleConfigurationElement).Id;
22+
}
23+
24+
IEnumerator<IBundle> IEnumerable<IBundle>.GetEnumerator()
25+
{
26+
var baseEnumerator = base.GetEnumerator();
27+
while (baseEnumerator.MoveNext())
28+
{
29+
yield return (AmdBundleConfigurationElement)baseEnumerator.Current;
30+
}
31+
}
32+
}
33+
}

Jacobsoft.Amd/Config/IAmdConfiguration.cs

+2
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,7 @@ public interface IAmdConfiguration
1818
IScriptMinifier Minifier { get; set; }
1919

2020
IDictionary<string, IShim> Shims { get; }
21+
22+
IDictionary<string, IEnumerable<string>> Bundles { get; }
2123
}
2224
}

Jacobsoft.Amd/Config/IBundle.cs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
6+
namespace Jacobsoft.Amd.Config
7+
{
8+
public interface IBundle
9+
{
10+
string Id { get; }
11+
IEnumerable<string> Modules { get; }
12+
}
13+
}

Jacobsoft.Amd/HtmlHelperExtensions.cs

+20
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,26 @@ public static IHtmlString ModuleInvoke(
6868
return new HtmlString(stringBuilder.ToString());
6969
}
7070

71+
public static IHtmlString ModuleBundle(this HtmlHelper helper, string bundleId)
72+
{
73+
var bundles = ServiceLocator.Instance.Get<IAmdConfiguration>().Bundles.OrEmpty();
74+
if (!bundles.ContainsKey(bundleId))
75+
{
76+
throw new ArgumentException(
77+
"Unrecognized bundle ID: " + bundleId,
78+
"bundleId");
79+
}
80+
81+
var outputContext = GetScriptOutputContext(helper.ViewContext.HttpContext);
82+
var stringBuilder = new StringBuilder();
83+
helper.EnsureAmdSystemInitialized(outputContext, stringBuilder);
84+
85+
helper.WriteScriptActionInclude("Bundle", bundleId, stringBuilder);
86+
outputContext.WrittenModules.UnionWith(bundles[bundleId]);
87+
88+
return new HtmlString(stringBuilder.ToString());
89+
}
90+
7191
public static IHtmlString ModuleBundle(
7292
this HtmlHelper helper,
7393
params string[] moduleIds)

0 commit comments

Comments
 (0)