diff --git a/RequireJsNet.Examples/RequireJS.complex.json b/RequireJsNet.Examples/RequireJS.complex.json index 001cfe7..f2e8b5a 100644 --- a/RequireJsNet.Examples/RequireJS.complex.json +++ b/RequireJsNet.Examples/RequireJS.complex.json @@ -1,15 +1,19 @@ { - "paths": { - "jquery": [ "http://no-domain.unknown/jquery-2.1.3", "jquery-2.1.3" ], - "jqMin": "jquery-2.1.3.min", - "req1": "req1", - "req2": "req2", - "jquery-validate": { - "path": "jquery.validate", - "defaultBundle": "jqValidate" - }, - "jquery-validate-unobtrusive": "jquery.validate.unobtrusive" + "paths": { + "jquery": [ "http://no-domain.unknown/jquery-2.1.3", "jquery-2.1.3" ], + "jqMin": "jquery-2.1.3.min", + "req1": "req1", + "req2": "req2", + "jquery-validate": { + "path": "jquery.validate", + "defaultBundle": "jqValidate" }, + "jquery-validate-unobtrusive": "jquery.validate.unobtrusive" + }, + "packages": [ + "cart", + { "name": "store", "main": "store", "location": "/Scripts/CommonJS/store" } + ], "shim": { "jquery-validate": { "deps": [ "jquery" ], diff --git a/RequireJsNet.Examples/RequireJsNet.Examples.csproj b/RequireJsNet.Examples/RequireJsNet.Examples.csproj index 81f4d34..4a18b46 100644 --- a/RequireJsNet.Examples/RequireJsNet.Examples.csproj +++ b/RequireJsNet.Examples/RequireJsNet.Examples.csproj @@ -157,6 +157,7 @@ + @@ -187,6 +188,8 @@ + + diff --git a/RequireJsNet.Examples/Scripts/CommonJS/store/store.js b/RequireJsNet.Examples/Scripts/CommonJS/store/store.js new file mode 100644 index 0000000..0475710 --- /dev/null +++ b/RequireJsNet.Examples/Scripts/CommonJS/store/store.js @@ -0,0 +1,2 @@ +//CommonJS package format +alert('store/store.js loaded'); diff --git a/RequireJsNet.Examples/Scripts/CommonJS/store/util.js b/RequireJsNet.Examples/Scripts/CommonJS/store/util.js new file mode 100644 index 0000000..e75179b --- /dev/null +++ b/RequireJsNet.Examples/Scripts/CommonJS/store/util.js @@ -0,0 +1,2 @@ +//CommonJS package format +alert('store/util.js loaded'); diff --git a/RequireJsNet.Examples/Scripts/Controllers/Root/Home/complexLoad.js b/RequireJsNet.Examples/Scripts/Controllers/Root/Home/complexLoad.js index 7480d0a..9e1d638 100644 --- a/RequireJsNet.Examples/Scripts/Controllers/Root/Home/complexLoad.js +++ b/RequireJsNet.Examples/Scripts/Controllers/Root/Home/complexLoad.js @@ -1,5 +1,12 @@ define(['req1', "jquery"], function(req1, $) { $(function () { alert('jquery loaded'); + + require(["cart", "store", "store/util"], function (cart, store, util) { + + alert('commonJS packages cart, store and util has been loaded'); + + }); + }); }); \ No newline at end of file diff --git a/RequireJsNet.Examples/Scripts/cart/main.js b/RequireJsNet.Examples/Scripts/cart/main.js new file mode 100644 index 0000000..50b46ab --- /dev/null +++ b/RequireJsNet.Examples/Scripts/cart/main.js @@ -0,0 +1,2 @@ +//CommonJS package format +alert('cart/main.js loaded'); diff --git a/RequireJsNet.Tests/ConfigMergerShould.cs b/RequireJsNet.Tests/ConfigMergerShould.cs index dbe0b34..c625c33 100644 --- a/RequireJsNet.Tests/ConfigMergerShould.cs +++ b/RequireJsNet.Tests/ConfigMergerShould.cs @@ -553,5 +553,48 @@ public void UnifyBundleItemsForSameId() CustomAssert.JsonEquals(expected, merged); } + + [Fact] + public void ReplacePackagesWithSameName() + { + var packageA1 = new RequirePackage("a", "main"); + var packageA2 = new RequirePackage("a", "start", "/somewhere"); + + var firstConfig = ConfigurationCreators.CreateCollectionWithPackages(packageA1); + var secondConfig = ConfigurationCreators.CreateCollectionWithPackages(packageA2); + + var merger = ConfigurationCreators.CreateBundleProcessingConfigMerger(firstConfig, secondConfig); + var merged = merger.GetMerged(); + + var expected = ConfigurationCreators.CreateEmptyCollection(); + expected.Packages.PackageList = new List + { + new RequirePackage("a", "start", "/somewhere") + }; + + CustomAssert.JsonEquals(expected, merged); + } + + [Fact] + public void AppendPackagesWithDifferentName() + { + var packageA1 = new RequirePackage("a", "main"); + var packageA2 = new RequirePackage("b", "start", "/somewhere"); + + var firstConfig = ConfigurationCreators.CreateCollectionWithPackages(packageA1); + var secondConfig = ConfigurationCreators.CreateCollectionWithPackages(packageA2); + + var merger = ConfigurationCreators.CreateBundleProcessingConfigMerger(firstConfig, secondConfig); + var merged = merger.GetMerged(); + + var expected = ConfigurationCreators.CreateEmptyCollection(); + expected.Packages.PackageList = new List + { + new RequirePackage("a", "main"), + new RequirePackage("b", "start", "/somewhere") + }; + + CustomAssert.JsonEquals(expected, merged); + } } } diff --git a/RequireJsNet.Tests/DataCreation/ConfigurationCreators.cs b/RequireJsNet.Tests/DataCreation/ConfigurationCreators.cs index b513dab..685fadb 100644 --- a/RequireJsNet.Tests/DataCreation/ConfigurationCreators.cs +++ b/RequireJsNet.Tests/DataCreation/ConfigurationCreators.cs @@ -17,6 +17,9 @@ public static ConfigurationCollection CreateEmptyCollection() collection.Paths = new RequirePaths(); collection.Paths.PathList = new List(); + collection.Packages = new RequirePackages(); + collection.Packages.PackageList = new List(); + collection.AutoBundles = new AutoBundles(); collection.AutoBundles.Bundles = new List(); @@ -41,6 +44,14 @@ public static ConfigurationCollection CreateCollectionWithPaths(params RequirePa return collection; } + public static ConfigurationCollection CreateCollectionWithPackages(params RequirePackage[] packages) + { + var collection = CreateEmptyCollection(); + collection.Packages = new RequirePackages(); + collection.Packages.PackageList = packages.ToList(); + return collection; + } + public static ConfigurationCollection CreateCollectionWithShims(params ShimEntry[] shim) { var collection = CreateEmptyCollection(); diff --git a/RequireJsNet.Tests/JsonReaderShould.cs b/RequireJsNet.Tests/JsonReaderShould.cs index e0a1b88..5002c60 100644 --- a/RequireJsNet.Tests/JsonReaderShould.cs +++ b/RequireJsNet.Tests/JsonReaderShould.cs @@ -61,6 +61,38 @@ public void ReadPathArray() CustomAssert.JsonEquals(expected, config); } + [Fact] + public void ReadPackagesArray() + { + var expectedPackages = new[] + { + new RequirePackage("cart", "main"), + new RequirePackage("store", "store") + }; + var expectedCollection = ConfigurationCreators.CreateCollectionWithPackages(expectedPackages); + + var config = ReadJson(new TestFileReader()); + + Assert.Equal(2, config.Packages.PackageList.Count); + CustomAssert.JsonEquals(expectedCollection, config); + } + + [Fact] + public void SerializesPackagesInRequireJSFormat() + { + var expected = "var require = {\"baseUrl\":\"\",\"locale\":\"en\",\"urlArgs\":null,\"waitSeconds\":7,\"paths\":{},\"packages\":[{\"name\":\"cart\",\"main\":\"main\"},{\"name\":\"store\",\"main\":\"store\"}],\"shim\":{},\"map\":{}};"; + + var config = new RequireRendererConfiguration(); + var collection = ConfigurationCreators.CreateCollectionWithPackages( + new RequirePackage("cart", "main"), + new RequirePackage("store", "store") + ); + var outputConfig = RequireJsHtmlHelpers.createOutputConfigFrom(collection, config, "en"); + var actual = Helpers.JavaScriptHelpers.SerializeAsVariable(outputConfig, "require"); + + Assert.Equal(expected, actual); + } + [Fact] public void ReadShimWithExports() { diff --git a/RequireJsNet.Tests/RequireJsNet.Tests.csproj b/RequireJsNet.Tests/RequireJsNet.Tests.csproj index 6696670..1e09dcd 100644 --- a/RequireJsNet.Tests/RequireJsNet.Tests.csproj +++ b/RequireJsNet.Tests/RequireJsNet.Tests.csproj @@ -68,6 +68,7 @@ + @@ -93,6 +94,7 @@ + diff --git a/RequireJsNet.Tests/RequirePackageShould.cs b/RequireJsNet.Tests/RequirePackageShould.cs new file mode 100644 index 0000000..1e72212 --- /dev/null +++ b/RequireJsNet.Tests/RequirePackageShould.cs @@ -0,0 +1,39 @@ +using RequireJsNet.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace RequireJsNet.Tests +{ + public class RequirePackageShould + { + [Fact] + public void InitializeWithOnlyName() + { + var name = "package1"; + + var actual = new RequirePackage(name); + + Assert.Equal(name, actual.Name); + Assert.Equal("main", actual.Main); + Assert.Equal(null, actual.Location); + } + + [Fact] + public void InitializeWithAllProperties() + { + var name = "package2"; + var main = "start"; + var location = "/Scripts/CommonJS/package2"; + + var actual = new RequirePackage(name, main, location); + + Assert.Equal(name, actual.Name); + Assert.Equal(main, actual.Main); + Assert.Equal(location, actual.Location); + } + } +} diff --git a/RequireJsNet.Tests/TestData/JsonReaderShould/ReadPackagesArray.json b/RequireJsNet.Tests/TestData/JsonReaderShould/ReadPackagesArray.json new file mode 100644 index 0000000..8e4ce60 --- /dev/null +++ b/RequireJsNet.Tests/TestData/JsonReaderShould/ReadPackagesArray.json @@ -0,0 +1,6 @@ +{ + "packages": [ + "cart", + { "name": "store", "main": "store" } + ] +} diff --git a/RequireJsNet/Configuration/ConfigMerger.cs b/RequireJsNet/Configuration/ConfigMerger.cs index 1c453cc..d7487d7 100644 --- a/RequireJsNet/Configuration/ConfigMerger.cs +++ b/RequireJsNet/Configuration/ConfigMerger.cs @@ -26,6 +26,8 @@ public ConfigMerger(List collections, ConfigLoaderOptio this.collections = collections; finalCollection.Paths = new RequirePaths(); finalCollection.Paths.PathList = new List(); + finalCollection.Packages = new RequirePackages(); + finalCollection.Packages.PackageList = new List(); finalCollection.Shim = new RequireShim(); finalCollection.Shim.ShimEntries = new List(); finalCollection.Map = new RequireMap(); @@ -46,6 +48,11 @@ public ConfigurationCollection GetMerged() MergePaths(coll); } + if (coll.Packages != null && coll.Packages.PackageList != null) + { + MergePackages(coll); + } + if (coll.Shim != null && coll.Shim.ShimEntries != null) { MergeShims(coll); @@ -99,6 +106,24 @@ private void MergePaths(ConfigurationCollection collection) } } + private void MergePackages(ConfigurationCollection collection) + { + var finalPackages = finalCollection.Packages.PackageList; + foreach (var package in collection.Packages.PackageList) + { + var existing = finalPackages.Where(r => r.Name == package.Name).FirstOrDefault(); + if (existing != null) + { + existing.Main = package.Main; + existing.Location = package.Location; + } + else + { + finalPackages.Add(package); + } + } + } + private void MergeShims(ConfigurationCollection collection) { var finalShims = finalCollection.Shim.ShimEntries; diff --git a/RequireJsNet/Configuration/JsonReader.cs b/RequireJsNet/Configuration/JsonReader.cs index bb6d04c..29ecfcf 100644 --- a/RequireJsNet/Configuration/JsonReader.cs +++ b/RequireJsNet/Configuration/JsonReader.cs @@ -70,6 +70,7 @@ private ConfigurationCollection ProcessConfig() var deserialized = (JObject)JsonConvert.DeserializeObject(text); collection.FilePath = Path; collection.Paths = GetPaths(deserialized); + collection.Packages = GetPackages(deserialized); collection.Shim = GetShim(deserialized); collection.Map = GetMap(deserialized); @@ -133,6 +134,38 @@ private static RequirePath requirePathFrom(JProperty prop) return result; } + private RequirePackages GetPackages(JObject document) + { + var packages = new RequirePackages(); + packages.PackageList = new List(); + + if (document != null && document["packages"] != null) + { + packages.PackageList = document["packages"] + .Select(r => requirePackageFrom(r)) + .ToList(); + } + + return packages; + } + + private static RequirePackage requirePackageFrom(JToken token) + { + if (token is JValue) + { + var name = ((JValue)token).Value(); + return new RequirePackage(name); + } + else + { + var packageObj = (JObject)token; + var name = (string)packageObj["name"]; + var main = (string)packageObj["main"]; + var location = (string)packageObj["location"]; + return new RequirePackage(name, main, location); + } + } + private RequireShim GetShim(JObject document) { var shim = new RequireShim(); diff --git a/RequireJsNet/Models/ConfigurationCollection.cs b/RequireJsNet/Models/ConfigurationCollection.cs index b651519..7335849 100644 --- a/RequireJsNet/Models/ConfigurationCollection.cs +++ b/RequireJsNet/Models/ConfigurationCollection.cs @@ -18,6 +18,9 @@ internal class ConfigurationCollection [JsonProperty(PropertyName = "paths")] public RequirePaths Paths { get; set; } + [JsonProperty(PropertyName = "packages")] + public RequirePackages Packages { get; set; } + [JsonProperty(PropertyName = "shim")] public RequireShim Shim { get; set; } diff --git a/RequireJsNet/Models/JsonRequireOutput.cs b/RequireJsNet/Models/JsonRequireOutput.cs index f25793d..fae6a19 100644 --- a/RequireJsNet/Models/JsonRequireOutput.cs +++ b/RequireJsNet/Models/JsonRequireOutput.cs @@ -28,6 +28,9 @@ public class JsonRequireOutput [JsonProperty(PropertyName = "paths")] public Dictionary> Paths { get; set; } + [JsonProperty(PropertyName = "packages")] + public List Packages { get; set; } + [JsonProperty(PropertyName = "shim")] public Dictionary Shim { get; set; } diff --git a/RequireJsNet/Models/RequirePackage.cs b/RequireJsNet/Models/RequirePackage.cs new file mode 100644 index 0000000..c0ce83d --- /dev/null +++ b/RequireJsNet/Models/RequirePackage.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RequireJsNet.Models +{ + public class RequirePackage + { + [JsonProperty(PropertyName = "name")] + public string Name { get; set; } + + [JsonProperty(PropertyName = "main")] + public string Main { get; set; } + + [JsonProperty(PropertyName = "location", NullValueHandling = NullValueHandling.Ignore)] + public string Location { get; set; } + + public RequirePackage(string name, string main = "main", string location = null) + { + this.Name = name; + this.Main = main ?? "main"; + this.Location = location; + } + } +} diff --git a/RequireJsNet/Models/RequirePackages.cs b/RequireJsNet/Models/RequirePackages.cs new file mode 100644 index 0000000..2f5db5d --- /dev/null +++ b/RequireJsNet/Models/RequirePackages.cs @@ -0,0 +1,16 @@ +// RequireJS.NET +// Copyright VeriTech.io +// http://veritech.io +// Dual licensed under the MIT and GPL licenses: +// http://www.opensource.org/licenses/mit-license.php +// http://www.gnu.org/licenses/gpl.html + +using System.Collections.Generic; + +namespace RequireJsNet.Models +{ + public class RequirePackages + { + public List PackageList { get; set; } + } +} diff --git a/RequireJsNet/RequireJsHtmlHelpers.cs b/RequireJsNet/RequireJsHtmlHelpers.cs index dbfb588..d9aa210 100644 --- a/RequireJsNet/RequireJsHtmlHelpers.cs +++ b/RequireJsNet/RequireJsHtmlHelpers.cs @@ -62,35 +62,9 @@ public static MvcHtmlString RenderRequireJsSetup( var locale = config.LocaleSelector(html); - var outputConfig = new JsonRequireOutput - { - BaseUrl = config.BaseUrl, - Locale = locale, - UrlArgs = config.UrlArgs, - WaitSeconds = config.WaitSeconds, - Paths = resultingConfig.Paths.PathList.ToDictionary(r => r.Key, r => r.Value), - Shim = resultingConfig.Shim.ShimEntries.ToDictionary( - r => r.For, - r => new JsonRequireDeps - { - Dependencies = r.Dependencies.Select(x => x.Dependency).ToList(), - Exports = r.Exports - }), - Map = resultingConfig.Map.MapElements.ToDictionary( - r => r.For, - r => r.Replacements.ToDictionary(x => x.OldKey, x => x.NewKey)) - }; - - config.ProcessConfig(outputConfig); + var outputConfig = createOutputConfigFrom(resultingConfig, config, locale); - var options = new JsonRequireOptions - { - Locale = locale, - PageOptions = RequireJsOptions.GetPageOptions(html.ViewContext.HttpContext), - WebsiteOptions = RequireJsOptions.GetGlobalOptions(html.ViewContext.HttpContext) - }; - - config.ProcessOptions(options); + var options = createOptionsFrom(html.ViewContext.HttpContext, config, locale); var configBuilder = new JavaScriptBuilder(); configBuilder.AddStatement(JavaScriptHelpers.SerializeAsVariable(options, "requireConfig")); @@ -102,17 +76,57 @@ public static MvcHtmlString RenderRequireJsSetup( var requireEntryPointBuilder = new JavaScriptBuilder(); requireEntryPointBuilder.AddStatement( JavaScriptHelpers.MethodCall( - "require", + "require", (object)new[] { entryPointPath.ToString() })); return new MvcHtmlString( - configBuilder.Render() + configBuilder.Render() + Environment.NewLine - + requireRootBuilder.Render() + + requireRootBuilder.Render() + Environment.NewLine + requireEntryPointBuilder.Render()); } + internal static JsonRequireOptions createOptionsFrom(System.Web.HttpContextBase httpContext, RequireRendererConfiguration config, string locale) + { + var options = new JsonRequireOptions + { + Locale = locale, + PageOptions = RequireJsOptions.GetPageOptions(httpContext), + WebsiteOptions = RequireJsOptions.GetGlobalOptions(httpContext) + }; + + config.ProcessOptions(options); + return options; + } + + internal static JsonRequireOutput createOutputConfigFrom(ConfigurationCollection resultingConfig, RequireRendererConfiguration config, string locale) + { + var outputConfig = new JsonRequireOutput + { + BaseUrl = config.BaseUrl, + Locale = locale, + UrlArgs = config.UrlArgs, + WaitSeconds = config.WaitSeconds, + Paths = resultingConfig.Paths.PathList.ToDictionary(r => r.Key, r => r.Value), + Packages = resultingConfig.Packages.PackageList, + Shim = resultingConfig.Shim.ShimEntries.ToDictionary( + r => r.For, + r => new JsonRequireDeps + { + Dependencies = r.Dependencies.Select(x => x.Dependency).ToList(), + Exports = r.Exports + }), + Map = resultingConfig.Map.MapElements.ToDictionary( + r => r.For, + r => r.Replacements.ToDictionary(x => x.OldKey, x => x.NewKey)) + }; + + config.ProcessConfig(outputConfig); + + return outputConfig; + } + private static HashStore configObjectHash = new HashStore(); private static ConfigurationCollection GetCachedOverridenConfig( diff --git a/RequireJsNet/RequireJsNet.csproj b/RequireJsNet/RequireJsNet.csproj index 6e36186..cd82633 100644 --- a/RequireJsNet/RequireJsNet.csproj +++ b/RequireJsNet/RequireJsNet.csproj @@ -111,6 +111,8 @@ + +