2011-01-25 15 views
12

He escrito un método de acción ASP.NET MVC que recibe un nombre de archivo .less, lo procesa a través de Less.Parse(<filename>) y emite el archivo css procesado.¿Cómo puedo generar errores al utilizar .less mediante programación?

Esto funciona bien siempre que el código .less sea válido, pero si hay un error, dotLess simplemente devuelve una cadena vacía. Entonces, si hay un error al procesar el archivo, mi método de acción devuelve un archivo css vacío.

¿Cómo puedo generar un mensaje de error con una descripción más cercana del error de sintaxis?

+0

¿Has visto SquishIt? http://www.codethinked.com/post/2010/05/26/SquishIt-The-Friendly-ASPNET-JavaScript-and-CSS-Squisher.aspx –

+0

@qstarin: Gracias, pero ya estoy usando combres (http: //combres.codeplex.com/) para fines de producción y los combres pueden hacer todo lo que hace squishit (más un poco más). Pero todavía necesito la respuesta a mi pregunta anterior para fines de desarrollo. La razón es que no me gusta trabajar con una descarga combinada incluida de todos mis archivos CSS, prefiero archivos separados. Y todo funciona bien, a excepción de los mensajes de error ... –

+0

Tengo curiosidad, ¿qué características útiles ofrece combges que SquishIt no ofrece? Al revisar la documentación de Combres, parece que hacen las mismas tareas. Squish Sin embargo, incluye dotLess en su procesamiento. Es por eso que lo mencioné, porque no necesitaría una acción separada para realizar la compilación de dotLess. –

Respuesta

14

El analizador de puntos atrapa las excepciones y las envía a un registrador. El fragmento de la fuente de dotless que realiza esta es LessEngine.TransformToCss:

public string TransformToCss(string source, string fileName) 
{ 
    try 
    { 
     Ruleset ruleset = this.Parser.Parse(source, fileName); 
     Env env = new Env(); 
     env.Compress = this.Compress; 
     Env env2 = env; 
     return ruleset.ToCSS(env2); 
    } 
    catch (ParserException exception) 
    { 
     this.Logger.Error(exception.Message); 
    } 
    return ""; 
} 

Less.Parse tiene una sobrecarga que toma un objeto DotlessConfiguration, que proporciona varias propiedades que se pueden utilizar:

public class DotlessConfiguration 
{ 
    // Properties 
    public bool CacheEnabled { get; set; } 
    public Type LessSource { get; set; } 
    public Type Logger { get; set; } 
    public LogLevel LogLevel { get; set; } 
    public bool MinifyOutput { get; set; } 
    public int Optimization { get; set; } 
    public bool Web { get; set; } 
} 

Usted se dará cuenta de que el Logger la propiedad es del tipo Type. Cualquiera que sea el tipo que proporcione debe implementar dotless.Core.Loggers.ILogger:

public interface ILogger 
{ 
    // Methods 
    void Debug(string message); 
    void Error(string message); 
    void Info(string message); 
    void Log(LogLevel level, string message); 
    void Warn(string message); 
} 

Como vimos en el primer fragmento, el método Error en el registrador se llamará cuando se detecta un error durante el análisis.

Ahora, el único punto pegajoso de todo esto es cómo se instancia exactamente una instancia del tipo que implementa ILogger. Internamente, dotLess usa un contenedor IoC que se procesa en la DLL. Después de las llamadas al método, parece que eventualmente llamará al Activator.CreateInstance para crear una instancia de su ILogger.

Espero que esto sea al menos algo útil.

+0

Excelente. ¡Muchas gracias! :-) –

+1

Realmente debería volver a trabajar la API Logger un poco ... En el momento de escribir esto tenía sentido con solo los usos internos en mente ... pero para los que llaman desde el exterior esto es bastante malo. – Tigraine

+0

@Tigraine: Espero al menos Expliqué correctamente cómo atrapar los eventos de registro, estaba un poco inseguro de haberlo descifrado todo, particularmente cuáles eran las ramificaciones de la especificación de un objeto DotlessConfiguration con solo la clase de registro completada. Además, si está reelaborando esto, , Apuntaría a SimpleLoggingFramework o Common.Logging como dos posibles fachadas para los sistemas de registro, aunque honestamente puede llegar a ser un desastre con todas las dependencias ... buena suerte. heh –

5

Acabo de enfrentar esto hoy en mi proyecto RequestReduce. Me estaba quedando en blanco menos -> css transforma porque había errores de análisis que parecían ir al éter. Gracias a la respuesta de qes pude encontrar una solución para escribir los errores en el flujo de respuesta. Aquí está mi dotless.Core.Loggers.ILogger:

public class LessLogger : ILogger 
{ 
    public void Log(LogLevel level, string message) 
    { 
    } 

    public void Info(string message) 
    { 
    } 

    public void Debug(string message) 
    { 
    } 

    public void Warn(string message) 
    { 
    } 

    public void Error(string message) 
    { 
     Response.Write(message); 
    } 

    public HttpResponseBase Response { get; set; } 
} 

me pase esto en la configuración enviada a la EngineFactory:

  var engine = new EngineFactory(new DotlessConfiguration 
               { 
                CacheEnabled = false, 
                Logger = typeof (LessLogger) 
               } 
       ).GetEngine(); 

Para realizar pruebas unitarias que quería pasar en mi HttpResponseBase que escribir el error. Aquí es donde me sentí cosas poniendo feo con un poco de fundición desagradable para obtener una referencia a mi registrador:

  ((LessLogger)((LessEngine)((ParameterDecorator)engine).Underlying).Logger).Response = response; 

Espero que esto ayude a cabo y si alguien sabe de una forma más elegante para obtener una referencia al registrador, por favor Házmelo saber.

+0

¿Dónde colocas la clase 'LessLogger'? – guanome

5

Puede hacerlo fácilmente con web.config. En su sección de configuración sin punto, agregue lo siguiente: logger="dotless.Core.Loggers.AspResponseLogger". Esto hará que los errores sin salida sean en lugar de css en blanco.

He incluido lo siguiente como ejemplo. ("..." representa material existente en su web.config). En mi ejemplo a continuación, el caché está configurado en falso. Esto es útil para propósitos de depuración.Probablemente debería establecerse en verdadero en circunstancias normales.

<configuration>  
    <configSections> 
      ... 
      <section name="dotless" type="dotless.Core.configuration.DotlessConfigurationSectionHandler,dotless.Core" /> 
     </configSections> 

     <dotless minifyCss="false" cache="false" 
      logger="dotless.Core.Loggers.AspResponseLogger" /> 
     ...  
</configuration>  
+3

He agregado la línea que se ve de la siguiente manera ¿No hay nada para mí? – Jacques

+0

¿No estás seguro de por qué esto está marcado como respuesta? –

+1

+1 Esto funciona: si utilizas algo como SquishIt, asegúrate de hacer referencia a los menos archivos directamente en tu navegador (o lo que sea) para ver los errores – davidsleeps

0

Para el beneficio de los demás, la solución de @ tony722 funciona si simplemente hace referencia a los archivos .less de sus páginas.

Pero si se llama a Less.Parse directamente, este método va a escribir cualquier error en Response:

var lessConfig = new DotlessConfiguration { Logger = typeof(AspResponseLogger) }; 
string css = Less.Parse(someInput, lessConfig); 
0

Esto registra a la ventana de salida en VS:

var config = dotless.Core.configuration.DotlessConfiguration.GetDefault(); 
config.Logger = new dotless.Core.Loggers.DiagnosticsLogger(dotless.Core.Loggers.LogLevel.Debug).GetType(); 
config.MinifyOutput = minified; 
css= Less.Parse(css, config); 
+0

es esta solución por sí misma. ¿Requiere que se implemente un registrador como los anteriores? – petersmm

1

estoy usando una clase contenedora alrededor sin punto , de la siguiente manera:

public class LessParser : IStylizer 
{ 
    public string ErrorFileName { get; private set; } 
    public int ErrorLineNumber { get; private set; } 
    public int ErrorPosition { get; private set; } 
    public string ErrorMessage { get; private set; } 

    string IStylizer.Stylize(Zone zone) 
    { 
     ErrorFileName = zone.FileName; 
     ErrorLineNumber = zone.LineNumber; 
     ErrorPosition = zone.Position; 
     ErrorMessage = zone.Message; 

     return String.Empty; 
    } 

    public string Compile(string lessContent, string lessPath) 
    { 
     var lessEngine = new EngineFactory(new DotlessConfiguration 
     { 
      CacheEnabled = false, 
      DisableParameters = true, 
      LogLevel = LogLevel.Error, 
      MinifyOutput = true 
     }).GetEngine(); 

     lessEngine.CurrentDirectory = lessPath; 

     /* uncomment if DisableParameters is false 
     if (lessEngine is ParameterDecorator) 
      lessEngine = ((ParameterDecorator)lessEngine).Underlying; 
     */ 

     /* uncomment if CacheEnabled is true 
     if (lessEngine is CacheDecorator) 
      lessEngine = ((CacheDecorator)lessEngine).Underlying; 
     */ 

     ((LessEngine)lessEngine).Parser.Stylizer = this; 

     return lessEngine.TransformToCss(lessContent, null); 
    } 

    public FileInfo SyncCss(FileInfo lessFile) 
    { 
     var cssFile = new FileInfo(
      lessFile.FullName.Substring(0, lessFile.FullName.Length - lessFile.Extension.Length) + ".css"); 

     if (!cssFile.Exists || cssFile.LastWriteTimeUtc < lessFile.LastWriteTimeUtc) 
     { 
      string cssContent = Compile(ReadFileContent(lessFile), lessFile.DirectoryName); 

      if (String.IsNullOrEmpty(cssContent)) 
       return null; 

      using (var stream = cssFile.Open(FileMode.Create)) 
      using (var writer = new StreamWriter(stream, Encoding.UTF8)) 
      { 
       writer.Write(cssContent); 
      } 
     } 

     return cssFile; 
    } 

    public string ReadFileContent(FileInfo file) 
    { 
     using (var reader = file.OpenText()) 
     { 
      return reader.ReadToEnd(); 
     } 
    } 
} 

El truco es utilizar la propia implementación ntation de la interfaz IStylizer a la que se recurre al encontrar un error de análisis para formatear el mensaje de error resultante. Esto nos permite capturar piezas discretas del error, a diferencia de la implementación de la interfaz ILogger donde el error ya es un texto formateado.

var parser = new LessParser(); 
var lessFile = new FileInfo("C:\\temp\\sample.less")); 
var cssFile = parser.SyncCss(lessFile); 

if (cssFile != null) 
    Console.WriteLine(parser.ReadFileContent(cssFile)); 
else 
    Console.WriteLine("Error '{3}' in {0}, line {1}, position {2}", 
     parser.ErrorFileName, parser.ErrorLineNumber, parser.ErrorPosition, parser.ErrorMessage); 
Cuestiones relacionadas