glav avatar glav committed 3a3676b

First cut of .less support

Comments (0)

Files changed (13)

Add a comment to this file

Dependencies/dotless.Core.dll

Binary file added.

ScriptDependencyExtension/Constants/ScriptEnumerations.cs

     public enum ScriptType
     {
         Javascript,
-        CSS 
+        CSS,
+		dotLess
     }
 
 }

ScriptDependencyExtension/Constants/XmlConstants.cs

 		public const string MinifyScriptsInReleaseMode = "MinifyCombinedScriptsInReleaseMode";
 
         public const string CSSTypeValue = "css";
+		public const string JSTypeValue = "js";
+		public const string DOTLessTypeValue = "less";
 
 
     }

ScriptDependencyExtension/Filters/dotLessFilter.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using ScriptDependencyExtension.Http;
+using ScriptDependencyExtension.Model;
+
+namespace ScriptDependencyExtension.Filters
+{
+	public class dotLessFilter : IScriptProcessingFilter
+	{
+		private dotless.Core.ILessEngine _lessEngine;
+		private IHttpContext _context;
+		private static IScriptProcessingFilter _filter;
+		private static object _lockObject = new object();
+		private ScriptDependencyContainer _dependencyContainer;
+		
+		public dotLessFilter(IHttpContext context, ScriptDependencyContainer dependencyContainer)
+		{
+			_context = context;
+			_dependencyContainer = dependencyContainer;
+			var factory = new dotless.Core.EngineFactory(new dotless.Core.configuration.DotlessConfiguration()
+			{
+				CacheEnabled = true
+			});
+			_lessEngine = factory.GetEngine();
+		}
+		public string ProcessScript(string scriptContents, Constants.ScriptType scriptType, Model.ScriptDependencyContainer container)
+		{
+			if (scriptType != Constants.ScriptType.CSS && scriptType != Constants.ScriptType.dotLess)
+			{
+				return scriptContents;
+			}
+
+			var processedContents = preProcessScriptContents(scriptContents);
+			return _lessEngine.TransformToCss(processedContents, null);
+		}
+
+		/// <summary>
+		/// Searches the script for an Import statement and attempts to resolve the 
+		/// location of the import file with one that is specified in the list of
+		/// dependencies otherwise the .less processor will barf when it cannot find the
+		/// import file.
+		/// </summary>
+		/// <param name="scriptContents"></param>
+		/// <returns></returns>
+		private string preProcessScriptContents(string scriptContents)
+		{
+			int importPosition = scriptContents.IndexOf("@import");
+			if (importPosition >= 0)
+			{
+				int startOfImportName = scriptContents.IndexOf("\"", importPosition)+1;
+				int endOfStatement = scriptContents.IndexOf("\";", importPosition);
+				var importName = scriptContents.Substring(startOfImportName, endOfStatement - startOfImportName);
+				var dependency = _dependencyContainer.FindDependency(importName);
+				if (dependency != null)
+				{
+					var resolvedPath = _context.ResolvePhysicalFilePathFromRelative(dependency.ScriptPath);
+					return scriptContents.Replace(string.Format("@import \"{0}\"", importName), string.Format("@import \"{0}\"", resolvedPath));
+				}
+				else
+				{
+					throw new ArgumentNullException(string.Format("Could not find Dependency [{0}] for .Less @import",importName));
+				}
+				//TODO: replace importname with dependency.scriptname
+			}
+
+			return scriptContents;
+		}
+
+		public static IScriptProcessingFilter GetDotLessProcessingFilter(IHttpContext context, ScriptDependencyContainer dependencyContainer)
+		{
+			if (_filter != null)
+			{
+				return _filter;
+			}
+
+			lock (_lockObject)
+			{
+				if (_filter != null)
+				{
+					return _filter;
+				}
+				_filter = new dotLessFilter(context, dependencyContainer);
+				return _filter;
+			}
+		}
+	}
+}

ScriptDependencyExtension/Handler/ScriptServeHandler.cs

 			}
 			if (string.IsNullOrWhiteSpace(scriptToRender))
 			{
-				var engine = new ScriptEngine(contextAdaptor, new ScriptDependencyLoader(contextAdaptor));
+				var engine = new ScriptEngine(contextAdaptor, scriptLoader);
 				var listOfFiles = dependencies.Select(d => d.ScriptPath).ToList();
 				var combiner = new FileCombiner(contextAdaptor, listOfFiles);
 				StringBuilder contents = new StringBuilder(combiner.CombineFiles());

ScriptDependencyExtension/ScriptDependencyExtension.csproj

     <Reference Include="AjaxMin">
       <HintPath>..\Dependencies\AjaxMin.dll</HintPath>
     </Reference>
+    <Reference Include="dotless.Core">
+      <HintPath>..\Dependencies\dotless.Core.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="System.Web" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Constants\ScriptHelperConstants.cs" />
+    <Compile Include="Filters\dotLessFilter.cs" />
     <Compile Include="Handler\ScriptServeHandler.cs" />
     <Compile Include="Filters\IScriptProcessingFilter.cs" />
     <Compile Include="Helpers\IUniqueHashValueGenerator.cs" />

ScriptDependencyExtension/ScriptDependencyLoader.cs

                 if (typeAttrib != null)
                 {
                     scriptDependency.TypeOfScript = ScriptType.Javascript;
-                    if (typeAttrib.Value.ToLowerInvariant() == XmlConstants.CSSTypeValue)
+					var normalisedTypeAttrib = typeAttrib.Value.ToLowerInvariant();
+                    if (normalisedTypeAttrib == XmlConstants.CSSTypeValue)
                     {
                         scriptDependency.TypeOfScript = ScriptType.CSS;
                     }
-                }
+					if (normalisedTypeAttrib == XmlConstants.DOTLessTypeValue)
+					{
+						scriptDependency.TypeOfScript = ScriptType.dotLess;
+					}
+				}
 
                 var file = dependency.Element(XmlConstants.ScriptFileElement);
                 if (file != null)

ScriptDependencyExtension/ScriptEngine.cs

 		
 		private void RegisterFilters()
 		{
+			_filters.Add(() => dotLessFilter.GetDotLessProcessingFilter(_httpContext,_scriptLoader.DependencyContainer));
 			_filters.Add(() => new ScriptMinifierFilter(_httpContext));
 		}
 

ScriptDependencyWebsite/Content/Home.css

-p.intro {
+@import "test.less";  
+
+p.intro {
     font-style:italic;
+}
+
+div {
+    border: 1px dotted @color-red;
+    .class1 {
+        color: red;
+        .class2 {
+            background-color: green;
+        }
+    }
 }

ScriptDependencyWebsite/Content/test.less

+@color-red: #990000;
+@color-attention: #FF0000;
+@color-white: #FFFFFF;
+@color-xl-grey: #EEEEEE;
+@color-xl-grey-2: #DFDFDF;
+
+.round(@radius: 3px) 
+{
+	border-radius: @radius; 
+	-moz-border-radius: @radius; 
+	-webkit-border-radius: @radius;
+}

ScriptDependencyWebsite/ScriptDependencies.xml

     </RequiredDependencies>
   </Dependency>
 
+  <Dependency Name="test.less" Type="less">
+    <ScriptFile>~/Content/test.less</ScriptFile>
+  </Dependency>
+
   <Dependency Name="Microsoft-Mvc-Validation" Type="js">
     <ScriptFile>~/Scripts/MicrosoftMvcValidation.js</ScriptFile>
     <RequiredDependencies>

ScriptDependencyWebsite/ScriptDependencyExtensionWebsite.csproj

     <Content Include="Views\_ViewStart.cshtml" />
     <Content Include="Views\Account\ChangePassword.cshtml" />
     <Content Include="Views\Account\ChangePasswordSuccess.cshtml" />
+    <None Include="Content\test.less" />
     <None Include="Views\Account\LogOn.cshtml" />
     <Content Include="Views\Account\Register.cshtml" />
     <Content Include="Views\Home\About.cshtml" />

ScriptDependencyWebsite/Web.config

     <add key="UnobtrusiveJavaScriptEnabled" value="true"/> 
   </appSettings>
   <system.web>
-    <compilation debug="false" targetFramework="4.0">
+    <compilation debug="true" targetFramework="4.0">
       <assemblies>
         <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
         <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.