net mvc bundleconfig asp import asp.net-mvc-4 bundle less asp.net-optimization

import - mvc - bundles asp net webforms



MVC4 Less Bundle @import Directorio (11)

Estoy intentando usar la agrupación MVC4 para agrupar algunos de mis archivos menos, pero parece que la ruta de importación que estoy usando está desactivada. Mi estructura de directorio es:

static/ less/ mixins.less admin/ user.less

En user.less, estoy intentando importar mixins.less usando esto:

@import "../mixins.less";

Esto solía funcionar para mí antes cuando usaba chirpy con dotless, pero ahora noté que ELMAH se estaba enojando conmigo, diciendo esto:

System.IO.FileNotFoundException: You are importing a file ending in .less that cannot be found. File name: ''../mixins.less''

¿Se supone que debo usar una @import diferente con MVC4?

Alguna información adicional

Aquí está el código de menos clase y global.asax.cs que estoy usando para intentar esto:

LessMinify.cs

... public class LessMinify : CssMinify { public LessMinify() {} public override void Process(BundleContext context, BundleResponse response) { response.Content = Less.Parse(response.Content); base.Process(context, response); } } ...

Global.asax.cs

... DynamicFolderBundle lessFB = new DynamicFolderBundle("less", new LessMinify(), "*.less"); BundleTable.Bundles.Add(lessFB); Bundle AdminLess = new Bundle("~/AdminLessBundle", new LessMinify()); ... AdminLess.AddFile("~/static/less/admin/user.less"); BundleTable.Bundles.Add(AdminLess); ...


Adición a la respuesta de Ben Cull:

Sé que esto "debería ser un comentario a la publicación de Ben Cull", pero agrega un poco más que sería imposible agregar en un comentario. Así que vótame si es necesario. O cierra mi

La publicación de blog de Ben lo hace todo, excepto que no especifica minificación.

Por lo tanto, instale el paquete BundleTransformer.Less como sugiere Ben y luego, si desea la minimización de su CSS, haga lo siguiente (en ~ / App_Start / BundleConfig.cs):

var cssTransformer = new CssTransformer(); var jsTransformer = new JsTransformer(); var nullOrderer = new NullOrderer(); var css = new Bundle("~/bundles/css") .Include("~/Content/site.less"); css.Transforms.Add(cssTransformer); css.Transforms.Add(new CssMinify()); css.Orderer = nullOrderer; bundles.Add(css);

La línea agregada es:

css.Transforms.Add(new CssMinify());

Donde CssMinify está en System.Web.Optimizations

Me siento tan aliviado de evitar el problema @import y el archivo resultante con la extensión .less no encontró que no me importa quién me vota.

Si, por el contrario, siente el impulso de votar a favor de esta respuesta, dele su voto a Ben.

Por lo tanto, allí.


A continuación de RockResolve a continuación, para utilizar el miniactor MicrosoftAjax, hágalo referencia como el minificador de CSS predeterminado en web.config en lugar de pasarlo como un argumento.

Desde https://bundletransformer.codeplex.com/wikipage/?title=Bundle%20Transformer%201.7.0%20Beta%201#BundleTransformerMicrosoftAjax_Chapter

Para hacer que MicrosoftAjaxCssMinifier sea el minificador de CSS predeterminado y MicrosoftAjaxJsMinifier el minificador de JS predeterminado, debe realizar cambios en el archivo Web.config. En el atributo defaultMinifier del elemento / configuration / bundleTransformer / core / css se debe establecer un valor igual a MicrosoftAjaxCssMinifier , y en el mismo atributo del elemento / configuration / bundleTransformer / core / js - MicrosoftAjaxJsMinifier.


A partir de febrero de 2013: la gran solución de Michael Baird fue superada por la respuesta del "Paquete BundleTransformer.Less Nuget" a la que se hace referencia en la publicación de Ben Cull. Respuesta similar en: http://blog.cdeutsch.com/2012/08/using-less-and-twitter-bootstrap-in.html

El blog de Cdeutsch y la publicación de awrigley que agrega minificación son buenos, pero aparentemente ahora no son el enfoque correcto.

Alguien más con la misma solución obtuvo algunas respuestas de un autor de BundleTransformer: geekswithblogs.net/ToStringTheory/archive/2012/11/30/… . Ver los comentarios en la parte inferior.

En resumen, use BundleTransformer.MicrosoftAjax en lugar de los minitorres integrados incorporados. por ejemplo, css.Transforms.Add (new CssMinify ()); reemplazado por css.Transforms.Add (nuevo BundleTransformer.MicrosoftAjax ());


Aquí está la versión más simple del código para manejar esto que podría surgir:

public class LessTransform : IBundleTransform { public void Process(BundleContext context, BundleResponse bundle) { var pathResolver = new ImportedFilePathResolver(context.HttpContext.Server); var lessParser = new Parser(); var lessEngine = new LessEngine(lessParser); (lessParser.Importer as Importer).FileReader = new FileReader(pathResolver); var content = new StringBuilder(bundle.Content.Length); foreach (var bundleFile in bundle.Files) { pathResolver.SetCurrentDirectory(bundleFile.IncludedVirtualPath); content.Append(lessEngine.TransformToCss((new StreamReader(bundleFile.VirtualFile.Open())).ReadToEnd(), bundleFile.IncludedVirtualPath)); content.AppendLine(); } bundle.ContentType = "text/css"; bundle.Content = content.ToString(); } } public class ImportedFilePathResolver : IPathResolver { private HttpServerUtilityBase server { get; set; } private string currentDirectory { get; set; } public ImportedFilePathResolver(HttpServerUtilityBase server) { this.server = server; } public void SetCurrentDirectory(string fileLocation) { currentDirectory = Path.GetDirectoryName(fileLocation); } public string GetFullPath(string filePath) { var baseDirectory = server.MapPath(currentDirectory); return Path.GetFullPath(Path.Combine(baseDirectory, filePath)); } }


El problema es que DynamicFolderBundle lee todos los contenidos de los archivos y pasa los contenidos combinados a LessMinify.

Como tal, cualquier @import no tiene ninguna referencia a la ubicación de donde vino el archivo.

Para resolver esto tuve que colocar todos los archivos "menos" en una ubicación.

Entonces debes entender que el orden de los archivos se vuelve importante. Como tal, comencé a cambiar el nombre del archivo con un número (por ejemplo: "0 CONSTANTS.less", "1 MIXIN.less", lo que significa que se cargan en la parte superior de la salida combinada antes de entrar en el LessMinify.

si depura su LessMinify y ve la respuesta. ¡Contenido verá la salida combinada menos!

Espero que esto ayude


Esto es lo que hice:

Se agregó el módulo Twitter Bootstrap Nuget.

Agregué esto a mi archivo _Layout.cshtml:

<link href="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Content/twitterbootstrap/less")" rel="stylesheet" type="text/css" />

Tenga en cuenta que cambié el nombre de mi carpeta "less" a twitterbootstrap solo para demostrar que podía

Se movieron menos archivos a una subcarpeta llamada "importaciones", excepto bootstrap.less y (para el diseño receptivo ) responsive.less .

~/Content/twitterbootstrap/imports

Se agregó una configuración en web.config:

<add key="TwitterBootstrapLessImportsFolder" value="imports" />

Creó dos clases (ligera modificación de la clase anterior):

using System.Configuration; using System.IO; using System.Web.Optimization; using dotless.Core; using dotless.Core.configuration; using dotless.Core.Input; namespace TwitterBootstrapLessMinify { public class TwitterBootstrapLessMinify : CssMinify { public static string BundlePath { get; private set; } public override void Process(BundleContext context, BundleResponse response) { setBasePath(context); var config = new DotlessConfiguration(dotless.Core.configuration.DotlessConfiguration.GetDefault()); config.LessSource = typeof(TwitterBootstrapLessMinifyBundleFileReader); response.Content = Less.Parse(response.Content, config); base.Process(context, response); } private void setBasePath(BundleContext context) { var importsFolder = ConfigurationManager.AppSettings["TwitterBootstrapLessImportsFolder"] ?? "imports"; var path = context.BundleVirtualPath; path = path.Remove(path.LastIndexOf("/") + 1); BundlePath = context.HttpContext.Server.MapPath(path + importsFolder + "/"); } } public class TwitterBootstrapLessMinifyBundleFileReader : IFileReader { public IPathResolver PathResolver { get; set; } private string basePath; public TwitterBootstrapLessMinifyBundleFileReader() : this(new RelativePathResolver()) { } public TwitterBootstrapLessMinifyBundleFileReader(IPathResolver pathResolver) { PathResolver = pathResolver; basePath = TwitterBootstrapLessMinify.BundlePath; } public bool DoesFileExist(string fileName) { fileName = PathResolver.GetFullPath(basePath + fileName); return File.Exists(fileName); } public string GetFileContents(string fileName) { fileName = PathResolver.GetFullPath(basePath + fileName); return File.ReadAllText(fileName); } } }

Mi implementación de IFileReader examina el miembro estático BundlePath de la clase TwitterBootstrapLessMinify. Esto nos permite inyectar una ruta base para las importaciones a usar. Me hubiera gustado tener un enfoque diferente (proporcionando una instancia de mi clase, pero no pude).

Finalmente, agregué las siguientes líneas a Global.asax:

BundleTable.Bundles.EnableDefaultBundles(); var lessFB = new DynamicFolderBundle("less", new TwitterBootstrapLessMinify(), "*.less", false); BundleTable.Bundles.Add(lessFB);

Esto resuelve efectivamente el problema de las importaciones que no saben de dónde importar.


Hay un código publicado en GitHub Gist que funciona bien con @import y dotLess: https://gist.github.com/2002958

Lo probé con Twitter Bootstrap y funciona bien.

ImportedFilePathResolver.cs

public class ImportedFilePathResolver : IPathResolver { private string currentFileDirectory; private string currentFilePath; /// <summary> /// Initializes a new instance of the <see cref="ImportedFilePathResolver"/> class. /// </summary> /// <param name="currentFilePath">The path to the currently processed file.</param> public ImportedFilePathResolver(string currentFilePath) { CurrentFilePath = currentFilePath; } /// <summary> /// Gets or sets the path to the currently processed file. /// </summary> public string CurrentFilePath { get { return currentFilePath; } set { currentFilePath = value; currentFileDirectory = Path.GetDirectoryName(value); } } /// <summary> /// Returns the absolute path for the specified improted file path. /// </summary> /// <param name="filePath">The imported file path.</param> public string GetFullPath(string filePath) { filePath = filePath.Replace(''//', ''/'').Trim(); if(filePath.StartsWith("~")) { filePath = VirtualPathUtility.ToAbsolute(filePath); } if(filePath.StartsWith("/")) { filePath = HostingEnvironment.MapPath(filePath); } else if(!Path.IsPathRooted(filePath)) { filePath = Path.Combine(currentFileDirectory, filePath); } return filePath; } }

LessMinify.cs

public class LessMinify : IBundleTransform { /// <summary> /// Processes the specified bundle of LESS files. /// </summary> /// <param name="bundle">The LESS bundle.</param> public void Process(BundleContext context, BundleResponse bundle) { if(bundle == null) { throw new ArgumentNullException("bundle"); } context.HttpContext.Response.Cache.SetLastModifiedFromFileDependencies(); var lessParser = new Parser(); ILessEngine lessEngine = CreateLessEngine(lessParser); var content = new StringBuilder(bundle.Content.Length); foreach(FileInfo file in bundle.Files) { SetCurrentFilePath(lessParser, file.FullName); string source = File.ReadAllText(file.FullName); content.Append(lessEngine.TransformToCss(source, file.FullName)); content.AppendLine(); AddFileDependencies(lessParser); } bundle.Content = content.ToString(); bundle.ContentType = "text/css"; //base.Process(context, bundle); } /// <summary> /// Creates an instance of LESS engine. /// </summary> /// <param name="lessParser">The LESS parser.</param> private ILessEngine CreateLessEngine(Parser lessParser) { var logger = new AspNetTraceLogger(LogLevel.Debug, new Http()); return new LessEngine(lessParser, logger, false); } /// <summary> /// Adds imported files to the collection of files on which the current response is dependent. /// </summary> /// <param name="lessParser">The LESS parser.</param> private void AddFileDependencies(Parser lessParser) { IPathResolver pathResolver = GetPathResolver(lessParser); foreach(string importedFilePath in lessParser.Importer.Imports) { string fullPath = pathResolver.GetFullPath(importedFilePath); HttpContext.Current.Response.AddFileDependency(fullPath); } lessParser.Importer.Imports.Clear(); } /// <summary> /// Returns an <see cref="IPathResolver"/> instance used by the specified LESS lessParser. /// </summary> /// <param name="lessParser">The LESS prser.</param> private IPathResolver GetPathResolver(Parser lessParser) { var importer = lessParser.Importer as Importer; if(importer != null) { var fileReader = importer.FileReader as FileReader; if(fileReader != null) { return fileReader.PathResolver; } } return null; } /// <summary> /// Informs the LESS parser about the path to the currently processed file. /// This is done by using custom <see cref="IPathResolver"/> implementation. /// </summary> /// <param name="lessParser">The LESS parser.</param> /// <param name="currentFilePath">The path to the currently processed file.</param> private void SetCurrentFilePath(Parser lessParser, string currentFilePath) { var importer = lessParser.Importer as Importer; if(importer != null) { var fileReader = importer.FileReader as FileReader; if(fileReader == null) { importer.FileReader = fileReader = new FileReader(); } var pathResolver = fileReader.PathResolver as ImportedFilePathResolver; if(pathResolver != null) { pathResolver.CurrentFilePath = currentFilePath; } else { fileReader.PathResolver = new ImportedFilePathResolver(currentFilePath); } } else { throw new InvalidOperationException("Unexpected importer type on dotless parser"); } } }


He pasado por el mismo problema, al ver el mismo mensaje de error. Buscar una solución en internet me trajo aquí. Mi problema fue el siguiente:

Dentro de un archivo menos tuve en algún momento un estilo incorrecto que me estaba dando una advertencia. El archivo menos no pudo ser analizado. Me deshice del mensaje de error eliminando la línea incorrecta.

Espero que esto ayude a alguien.


Un trabajo que encontré que fue realmente útil fue establecer el directorio antes de ejecutar Less.Parse dentro de LessMinify.Process (). Así es como lo hice:

public class LessTransform : IBundleTransform { private string _path; public LessTransform(string path) { _path = path; } public void Process(BundleContext context, BundleResponse response) { Directory.SetCurrentDirectory(_path); response.Content = Less.Parse(response.Content); response.ContentType = "text/css"; } }

Luego, pasa el camino al crear el objeto menos transformado como sigue:

lessBundle.Transforms.Add( new LessTransform(HttpRuntime.AppDomainAppPath + "/Content/Less") );

Espero que esto ayude.