52

MVC4 バンドルを使用していくつかの少ないファイルをグループ化しようとしていますが、使用しているインポート パスがオフになっているようです。私のディレクトリ構造は次のとおりです。

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

user.less では、これを使用して mixins.less をインポートしようとしています:

@import "../mixins.less";

これは、以前はドットなしでチャーピーを使用していたときに機能していましたが、ELMAH が次のように言って怒っていることに気付きました。

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

@importMVC4で別のものを使用することになっていますか?

いくつかの追加情報

これを試みるために使用しているクラスとglobal.asax.csのコードは次のとおりです。

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);
...
4

10 に答える 10

41

Using LESS CSS With MVC4 Web Optimizationに関する簡単なブログ投稿を書きました。

基本的には、BundleTransformer.Less Nuget パッケージを使用し、BundleConfig.csを変更することになります。

ブートストラップでテスト済み。

編集:私がこれを言う理由を言及する必要があります.@importディレクトリ構造の問題にも遭遇し、このライブラリはそれを正しく処理します.

于 2012-10-02T01:02:10.590 に答える
26

@import と dotLess でうまく機能する GitHub Gist に投稿されたコードがあります: https://gist.github.com/2002958

Twitter Bootstrapでテストしたところ、問題なく動作しました。

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");
        }


    }
}
于 2012-08-06T19:02:43.763 に答える
21

Ben Cullの回答への補遺:

これが「Ben Cull の投稿へのコメントであるべき」ということはわかっていますが、コメントに追加するのが不可能な少し余分な情報が追加されています。必要に応じて投票してください。または私を閉じます。

ベンのブログ投稿は、縮小化を指定していないことを除いて、すべてを行っています。

したがって、Ben が提案するように BundleTransformer.Less パッケージをインストールしてから、CSS を縮小したい場合は、次の手順を実行します (~/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);

追加された行は次のとおりです。

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

どこCssMinifyにあるSystem.Web.Optimizations

@import の問題を回避し、.less 拡張子の付いた結果のファイルが見つからなかったので、誰が私に反対票を投じてもかまわないので、とても安心しています。

反対に、この回答に投票したいという衝動を感じた場合は、Ben に投票してください。

そこで。

于 2012-10-31T17:38:21.090 に答える
17

私が見つけた非常に役立つ回避策は、LessMinify.Process() 内で Less.Parse を実行する前にディレクトリを設定することでした。これが私がやった方法です:

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";
        }
    }

次に、次のように less 変換オブジェクトを作成するときにパスを渡します。

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

お役に立てれば。

于 2013-10-02T22:55:44.823 に答える
4

問題は、DynamicFolderBundle がファイルのすべてのコンテンツを読み取り、結合されたコンテンツを LessMinify に渡すことです。

そのため、@import はファイルの元の場所を参照しません。

これを解決するには、すべての「少ない」ファイルを 1 つの場所に配置する必要がありました。

次に、ファイルの順序が重要になることを理解する必要があります。そのため、ファイルの名前を数字で変更し始めました(例:「0 CONSTANTS.less」、「1 MIXIN.less」は、LessMinify.

LessMinify をデバッグして response.Content を表示すると、結合された less 出力が表示されます。

お役に立てれば

于 2012-03-15T09:56:52.013 に答える
2

これが私がしたことです:

Twitter Bootstrap Nuget モジュールを追加しました。

これを私の_Layout.cshtmlファイルに追加しました:

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

「less」フォルダーの名前をtwitterbootstrapに変更したことに注意してください。

すべての less ファイルを「imports」というサブフォルダーに移動しました。ただし、 bootstrap.lessと (レスポンシブ デザインの場合) Respond.lessを除きます。

~/Content/twitterbootstrap/imports

web.config に次の構成を追加しました。

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

2 つのクラスを作成しました (上記のクラスをわずかに変更):

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);
        }
    }
}

私の IFileReader の実装は、TwitterBootstrapLessMinify クラスの静的メンバー BundlePath を調べます。これにより、インポートで使用するベース パスを挿入できます。別のアプローチを取りたかったのですが (クラスのインスタンスを提供することでしたが、できませんでした)。

最後に、次の行を Global.asax に追加しました。

BundleTable.Bundles.EnableDefaultBundles();

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

これにより、どこからインポートすればよいかわからないインポートの問題が効果的に解決されます。

于 2012-05-25T04:48:10.440 に答える
1

2013年2月の時点で、Michael Bairdの優れたソリューションは、BenCullの投稿で言及されている「BundleTransformer.LessNugetPackage」の回答に取って代わられました。同様の回答:http: //blog.cdeutsch.com/2012/08/using-less-and-twitter-bootstrap-in.html

Cdeutschのブログとawrigleyの投稿でミニファイを追加するのは良いことですが、今では明らかに正しいアプローチではありません。

同じソリューションを使用している他の誰かが、BundleTransformerの作成者からいくつかの回答を得ました:http: //geekswithblogs.net/ToStringTheory/archive/2012/11/30/who-could-ask-for-more-with-less-css-part- 2.aspx。下部のコメントを参照してください。

要約すると、組み込みのミニファイアの代わりにBundleTransformer.MicrosoftAjaxを使用します。例:css.Transforms.Add(new CssMinify()); css.Transforms.Add(new BundleTransformer.MicrosoftAjax());に置き換えられました。

于 2013-02-08T00:20:05.673 に答える
1

以下の RockResolve に続いて、MicrosoftAjax ミニファイアを使用するには、引数として渡すのではなく、web.config でデフォルトの CSS ミニファイアとして参照します。

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

MicrosoftAjaxCssMinifier を既定の CSS ミニファイアに、MicrosoftAjaxJsMinifier を既定の JS ミニファイアにするには、Web.config ファイルを変更する必要があります。\configuration\bundleTransformer\core\css 要素のdefaultMinifier属性では、 MicrosoftAjaxCssMinifierと同じ値を設定する必要があり、\configuration\bundleTransformer\core\js 要素の同じ属性 - MicrosoftAjaxJsMinifier に設定する必要があります。

于 2013-04-16T20:05:17.940 に答える
-1

同じエラーメッセージが表示され、同じ問題を経験しました。インターネットで解決策を探していたところ、ここにたどり着きました。私の問題は次のとおりです。

より少ないファイル内で、ある時点で警告を発していた不適切なスタイルがありました。less ファイルを解析できませんでした。間違った行を削除して、エラーメッセージを取り除きました。

これが誰かに役立つことを願っています。

于 2013-12-09T13:26:29.197 に答える