Last active
December 22, 2015 10:03
-
-
Save Crisfole/39fab8875764f6a0187b to your computer and use it in GitHub Desktop.
CoffeeScript and Less
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using MsieJavaScriptEngine; | |
public class CoffeeScriptCompiler : ContentCompiler { | |
public CoffeeScriptCompiler() { | |
Javascript.Engine.RegisterCompiler("CoffeeScript", LoadCoffeeScript, MakeCompiler); | |
} | |
private static void LoadCoffeeScript(MsieJsEngine engine) { | |
engine.Execute(Properties.Resources.Coffeescript); | |
} | |
private static Func<string, string> MakeCompiler(MsieJsEngine engine) { | |
return input => { | |
engine.SetVariableValue("coffeeInput", input); | |
engine.Execute(@"try { | |
coffeeOutput = CoffeeScript.compile(coffeeInput); | |
} | |
catch (_error) { | |
error = _error; | |
}"); | |
if (engine.HasVariable("error")) { | |
var startLine = engine.Evaluate<int>("error.location.first_line") + 1; | |
var endLine = engine.Evaluate<int>("error.location.last_line") + 1; | |
var startColumn = engine.Evaluate<int>("error.location.first_column") + 1; | |
var endColumn = engine.Evaluate<int>("error.location.last_column") + 1; | |
var stack = engine.Evaluate<string>("error.stack"); | |
throw new ResourceCompilerError(startLine, endLine, startColumn, endColumn, stack); | |
} | |
return engine.GetVariableValue<string>("coffeeOutput"); | |
}; | |
} | |
public override string[] InputExtensions { | |
get { return new[] {"coffee"}; } | |
} | |
public override string[] OutputExtensions { | |
get { return new[] {"js"}; } | |
} | |
public override IEnumerable<string> Dependencies(string input) { | |
yield return input; | |
} | |
public override string GetSourceFilename(string virtualPath) { | |
var toReturn = Path.ChangeExtension(virtualPath, "coffee"); | |
return !File.Exists(toReturn) ? null : toReturn; | |
} | |
public override void Compile(string sourceFile, string destFile, bool throwErrors = false) { | |
string result; | |
try { | |
result = Javascript.Engine.Compile("CoffeeScript", File.ReadAllText(sourceFile)); | |
} | |
catch (Exception error) { | |
result = error.Message; | |
if (throwErrors) { | |
throw; | |
} | |
} | |
File.WriteAllText(destFile, result); | |
} | |
public override string GetCacheFilename(string virtualPath) { | |
return Path.ChangeExtension(virtualPath, ".js"); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public abstract class ContentCompiler { | |
public abstract string GetSourceFilename(string virtualPath); | |
public abstract void Compile(string sourceFile, string destFile, bool throwErrors = false); | |
public abstract string GetCacheFilename(string virtualPath); | |
public bool NeedsRecompilation(string input, string output) { | |
var lastCompiled = File.GetLastWriteTimeUtc(output); | |
return !File.Exists(output) || Dependencies(input).Any(dep => File.GetLastWriteTimeUtc(dep) > lastCompiled); | |
} | |
public abstract string[] InputExtensions { get; } | |
public abstract string[] OutputExtensions { get; } | |
public abstract IEnumerable<string> Dependencies(string input); | |
public virtual bool CanHandle(string requestedPath) { | |
return !this.OutputExtensions.Any() || this.OutputExtensions.Any(requestedPath.EndsWith); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using MsieJavaScriptEngine; | |
internal class Javascript { | |
private readonly MsieJsEngine _engine = new MsieJsEngine(); | |
private readonly Dictionary<string, object> _locks = new Dictionary<string, object>(); | |
private readonly Dictionary<string, Func<string, string>> _compilers = new Dictionary<string, Func<string, string>>(); | |
private Javascript() { } | |
public static readonly Javascript Engine = new Javascript(); | |
internal void RegisterCompiler(string name, Action<MsieJsEngine> setup, | |
Func<MsieJsEngine, Func<string, string>> makeCompileFunc) { | |
if (this._locks.ContainsKey(name)) { | |
return; | |
} | |
this._locks[name] = new object(); | |
setup(this._engine); | |
this._compilers[name] = makeCompileFunc(this._engine); | |
} | |
internal string Compile(string compiler, string source) { | |
lock (this._locks[compiler]) { | |
return this._compilers[compiler](source); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Text.RegularExpressions; | |
using dotless.Core; | |
using dotless.Core.configuration; | |
using dotless.Core.Loggers; | |
using Nancy; | |
public class LessCompiler : ContentCompiler | |
{ | |
public override string[] OutputExtensions { | |
get { return new[] { "css" }; } | |
} | |
public override IEnumerable<string> Dependencies(string input) { | |
var deps = new List<string> { | |
input, | |
}; | |
using ((Pushd) Path.GetDirectoryName(input)) { | |
var engine = LessEngine; | |
engine.TransformToCss(input); | |
deps.AddRange(engine.GetImports().Select(Path.GetFullPath)); | |
} | |
return deps; | |
} | |
public override string GetSourceFilename(string virtualPath) { | |
var toReturn = Path.ChangeExtension(virtualPath, "less"); | |
return !File.Exists(toReturn) ? null : toReturn; | |
} | |
public override string GetCacheFilename(string virtualPath) { | |
return Path.ChangeExtension(virtualPath, "css"); | |
} | |
public override string[] InputExtensions | |
{ | |
get { return new[] {"less"}; } | |
} | |
public override void Compile(string sourceFilename, string destFilename, bool throwErrors = false) { | |
using ((Pushd)Path.GetDirectoryName(sourceFilename)) { | |
var engine = LessEngine; | |
var css = engine.TransformToCss(sourceFilename); | |
if (engine.LastTransformationSuccessful) { | |
File.WriteAllText(destFilename, css); | |
} | |
else if (throwErrors) { | |
var lines = engine.LogContents.Split('\n').Skip(1).ToList(); | |
var details = Regex.Split(lines.First(), @" on line | in file "); | |
var msg = details.First(); | |
var lineNo = int.Parse(details.Skip(1).First()); | |
var colNo = lines.First(l => l.StartsWith(" ") && l.Contains("^")).Count(c => c == '-') + 1; | |
throw new ResourceCompilerError(lineNo, lineNo, colNo, colNo, msg); | |
} | |
else if (StaticConfiguration.IsRunningDebug) { | |
File.WriteAllText(destFilename, engine.LogContents); | |
} | |
} | |
} | |
private static LoggingLessEngine LessEngine { | |
get { | |
var conf = DotlessConfiguration.GetDefault(); | |
conf.KeepFirstSpecialComment = true; | |
conf.Debug = StaticConfiguration.IsRunningDebug; | |
conf.MinifyOutput = !StaticConfiguration.IsRunningDebug; | |
conf.InlineCssFiles = true; | |
conf.Logger = typeof (StringLogger); | |
return new LoggingLessEngine(new EngineFactory(conf).GetEngine()); | |
} | |
} | |
private class StringLogger : Logger { | |
private readonly List<string> messages = new List<string>(); | |
public StringLogger(LogLevel level) : base(level) { | |
} | |
protected override void Log(string message) { | |
this.messages.Add(message); | |
} | |
public string LogContents { | |
get { return string.Join("\n", this.messages); } | |
} | |
} | |
// Directly from https://github.com/dotless/dotless/blob/master/src/dotless.Compiler/FixImportPathDecorator.cs | |
// Renamed to better reflect its purpose | |
private class LoggingLessEngine : ILessEngine { | |
public LoggingLessEngine(ILessEngine underlying) { | |
this.Underlying = underlying; | |
} | |
public string TransformToCss(string source, string fileName) { | |
return this.Underlying.TransformToCss(source, fileName); | |
} | |
public string TransformToCss(string inFile) { | |
return TransformToCss(File.ReadAllText(inFile), inFile); | |
} | |
public void ResetImports() { | |
this.Underlying.ResetImports(); | |
} | |
public IEnumerable<string> GetImports() { | |
return this.Underlying.GetImports().Select(import => import.Replace("/", "\\")); | |
} | |
public bool LastTransformationSuccessful { | |
get { return this.Underlying.LastTransformationSuccessful; } | |
} | |
public string LogContents { | |
get { return this.Logger.LogContents; } | |
} | |
private StringLogger Logger { | |
get { | |
var parameterDecorator = this.Underlying as ParameterDecorator; | |
if (parameterDecorator != null) { | |
var cacheDecorator = parameterDecorator.Underlying as CacheDecorator; | |
if (cacheDecorator != null) { | |
return cacheDecorator.Logger as StringLogger; | |
} | |
} | |
return null; | |
} | |
} | |
public ILessEngine Underlying { get; private set; } | |
} | |
private class Pushd : IDisposable { | |
private readonly string _currDir; | |
private Pushd(string dir) { | |
this._currDir = Directory.GetCurrentDirectory(); | |
Directory.SetCurrentDirectory(dir); | |
} | |
public static implicit operator Pushd(string d) { | |
return new Pushd(d); | |
} | |
public void Dispose() { | |
Directory.SetCurrentDirectory(this._currDir); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
public class ResourceCompilerError : Exception { | |
public ResourceCompilerError(int startLine, int endLine, int startColumn, int endColumn, string details) { | |
this.StartLine = startLine; | |
this.EndLine = endLine; | |
this.StartColumn = startColumn; | |
this.EndColumn = endColumn; | |
this.Details = details; | |
this.LineNumbers = true; | |
} | |
public ResourceCompilerError(string details) { | |
Details = details; | |
this.LineNumbers = false; | |
} | |
public bool LineNumbers { get; set; } | |
public string Details { get; set; } | |
public int StartLine { get; set; } | |
public int EndLine { get; set; } | |
public int StartColumn { get; set; } | |
public int EndColumn { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment