Подтвердить что ты не робот

Приложение ASP.NET MVC4 не может скомпилировать Bootstrap.LESS при производстве, пока оно работает на dev

Я чувствую себя немного застрявшим прямо сейчас. Сначала я использовал nuget для

install-package Bootstrap.less

а также

install-package dotless

Затем, как показано в Rick Andersons Blogpost о связывании и минимизации в asp.net mvc, я создал класс LessTransform. Я установил 2 почти пустых файла без изменений и создал новый пакет, упаковывающий их...

    var lessBundle = new Bundle("~/MyLess").IncludeDirectory("~/Content/MyLess", "*.less", true);
    lessBundle.Transforms.Add(new LessTransformer());
    lessBundle.Transforms.Add(new CssMinify());
    bundles.Add(lessBundle);

Это сработало хорошо. Затем я добавил новый StyleBundle к основному файлу bootstrap.less(который в основном использует @import для включения всех других .less файлов, которые загружаются в bootstrap.less)...

    bundles.Add(new StyleBundle("~/Bootstrap").Include("~/Content/Bootstrap/less/bootstrap.less"));

и ScriptBundle для начальной загрузки JavaScripts...

    bundles.Add(new ScriptBundle("~/bundles/Bootstrap").Include("~/Scripts/bootstrap/js/bootstrap-*"));

включить все загруженные файлы начальной загрузки - *. js и TADAA, все работает нормально. CSS был скомпилирован, включая все импортированные файлы JavaScript, которые были правильно загружены.

Но... все, что только работало в режиме разработки с

    <compilation debug="true" targetFramework="4.5"/>

Как только я отключу отладку, чтобы проверить, работает ли сборка в один файл, а минимизация работает, я сталкиваюсь с проблемой.

Процесс объединения, похоже, не позволяет импортировать все эти .less файлы, импортированные в bootstrap.less

/* Minification failed. Returning unminified contents.
(11,1): run-time error CSS1019: Unexpected token, found '/'
(11,2): run-time error CSS1019: Unexpected token, found '/'
(12,1): run-time error CSS1031: Expected selector, found '@import'
(12,1): run-time error CSS1025: Expected comma or open brace, found '@import'
(12,27): run-time error CSS1019: Unexpected token, found '/'
(12,28): run-time error CSS1019: Unexpected token, found '/'

 ... here go many many lines like these 

(60,25): run-time error CSS1019: Unexpected token, found ';'
(62,1): run-time error CSS1019: Unexpected token, found '/'
(62,2): run-time error CSS1019: Unexpected token, found '/'
(63,1): run-time error CSS1031: Expected selector, found '@import'
(63,1): run-time error CSS1025: Expected comma or open brace, found '@import'
(63,27): run-time error CSS1019: Unexpected token, found '/'
(63,28): run-time error CSS1019: Unexpected token, found '/'
: run-time error CSS1067: Unexpected end of file encountered
 */
/*!
 * Bootstrap v2.3.1 
 *
 * Copyright 2012 Twitter, Inc
 * Licensed under the Apache License v2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Designed and built with all the love in the world @twitter by @mdo and @fat.
 */

// Core variables and mixins
@import "variables.less"; // Modify this for custom colors, font-sizes, etc
@import "mixins.less";

... and the rest of the original bootstrap.less... no style definitions

смотря на миниатюрный пакет bootstrap.javascript, также поражает меня. в dev не было проблем после загрузки страницы, теперь после того, как bootstrap.javascript был включен и проинструктирован в Google в состояния консоли JavaScript

Uncaught TypeError: Cannot read property 'Constructor' of undefined

Я рассмотрел несколько тем, которые, похоже, были тесно связаны с моей Проблемой, и я пробовал несколько вещей, но до сих пор без успеха.

Большое спасибо заранее всем, кто может пролить свет на мою ситуацию, и кто будет указывать на то, чего я не вижу или делаю неправильно. С наилучшими пожеланиями, Ingo

4b9b3361

Ответ 1

Если вы хотите использовать bootstrap как файлы меньшего размера и, кроме того, хотите перестать беспокоиться о связывании и минимизации на вашей машине разработки, а также на вашей производственной машине, вы можете использовать следующий подход.

Примечание: вам не нужно все это, если вы играете только с меньшими файлами, в то время как DEBUGging включен; Но как только вы захотите, чтобы ваше приложение вышло в эфир на производственном сервере, таком как Windows Azure, и все еще хотите просто изменить меньшее количество файлов, не заботясь о процедурах группировки и минимизации... ну... тогда этот подход будет работа

Итак, чтобы решить проблему, я немного застрял, мне пришлось подойти к проблеме по-другому и пришлось модифицировать (см. Модификацию 2 дальше по почте) "BundleSource", который, как я думал, мне бы хотелось.

ТАК НЕ БУДЕТ ЗАПРЕЩАЕТСЯ ПРОЧИТАТЬ 2-ю модификацию/предупреждение рядом с этим ответом!


МОДИФИКАЦИЯ 1)

Таким образом, первая и большая часть задания - это получение пакета без загрузочных файлов. Чтобы сделать это, я взял на себя смелость разбить кусок кода, который я нашел в Интернете, который (если вам нужен только один пакет с меньшим количеством файлов) сам решает мою проблему... если вы не захотите использовать или быть в состоянии использовать несколько меньших пакетов с несколькими базовыми каталогами... Так вот где я действительно нашел подход, который мне очень помог...

... за что я благодарю Кристофа Класа за его блог-запись "Использование ASP.NET для комплектации и минимизации с файлами LESS", которые я случайно и с радостью споткнулся.

Как и я, он попытался использовать LessMinify.cs, который Scott Hanselman показывал в своих речах для работы с 1 LESS файлом вместо простого связывания каждого файла в 1 каталоге, полном LESS файлов.

Но он должен был продлить всю процедуру связывания, как он показывает в своем блоге. Таким образом, решение, которое он предлагает, может связывать 1 меньше файла, который использует импорт для загрузки других меньших файлов. Но поскольку он статически реализует путь, который добавляется в исходный каталог, в котором можно найти меньше файлов... в зависимости от того, какой меньше связок вы определяете, нужно выбрать меньше файла в том же каталоге...

Вот где я взял на себя смелость расширить свое решение немного дальше. Я создал файл LessBundling.cs со следующим содержимым:

using dotless.Core.configuration;
using dotless.Core.Input;
using MvcApplication2.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Web;
using System.Web.Hosting;
using System.Web.Optimization;

namespace MvcApplication2.Extensions
{
    // create Less-Minifier (use Type to define source directory of less files [see below at BootstrapFileReader])
    public class LessMinify<TFileReader> : CssMinify
        where TFileReader : IFileReader
    {
        public LessMinify() {}

        public override void Process(BundleContext context, BundleResponse response)
        {
            var config = new DotlessConfiguration()
            { 
                MinifyOutput = true,
                ImportAllFilesAsLess = true,
                CacheEnabled = false,
                LessSource = typeof(TFileReader)
            };

            response.Content = dotless.Core.Less.Parse(response.Content, config);
            base.Process(context, response);
        }
    }

    // create a LessStyleBundler to allow initializing LessBundle with a single less file that uses imports
    public class LessStyleBundle<TFileReader> : Bundle
        where TFileReader : IFileReader
    {
        public LessStyleBundle(string virtualPath)
            : base(virtualPath, new LessMinify<TFileReader>()) {}

        public LessStyleBundle(string virtualPath, string cdnPath)
            : base(virtualPath, cdnPath, new LessMinify<TFileReader>()) { }
    }

    // create abstract VirtualFileReader from dotless-IFileReader as a Base for localized 
    internal abstract class VirtualFileReader : IFileReader
    {
        public byte[] GetBinaryFileContents(string fileName)
        {
            fileName = GetFullPath(fileName);
            return File.ReadAllBytes(fileName);
        }

        public string GetFileContents(string fileName)
        {
            fileName = GetFullPath(fileName);
            return File.ReadAllText(fileName);
        }

        public bool DoesFileExist(string fileName)
        {
            fileName = GetFullPath(fileName);
            return File.Exists(fileName);
        }


        public string GetFullPath(string path)
        {
            return  HostingEnvironment.MapPath(SourceDirectory + path);
        }

        public abstract string SourceDirectory {get;}
        // implement to return Path to location of less files
        // e. g. return "~/Content/bootstrap/less/";
    }

    // create BootstrapFileReader overwriting the Path where to find the Bootstrap-Less-Files
    internal sealed class BootstrapFileReader : VirtualFileReader
    {
        public override string SourceDirectory 
        {
            get { return "~/Content/bootstrap/less/"; }
        }
    }

}

Итак, что это на самом деле делает?

  • LessMinify расширяет класс CssMinify и поэтому приносит все необходимое для минимизации файлов css
    • Важным отличием от "обычного" набора является то, что вы создаете новую Dotless-Configuration с помощью LessSource, определенного как typeof (TFileReader)...
    • Используя <TFileReader> вы можете определить класс, который будет содержать исходный каталог, в котором поставщик / minifier будет искать меньше файлов, которые будут учитываться
  • LessStyleBundle расширяет Bundle и поэтому приносит все необходимое для объединения файлов
    • В этом классе я снова использую TFileReader, так как здесь будет создан экземпляр LessMinify (er)
  • VirtualFileReader реализует IFileReader, который представляет собой бесцельный интерфейс, определяющий все методы, необходимые для анализа меньшего количества файлов и предоставления информации о том, где искать файлы для импорта
    • Чтобы распространить решение Kristof на проблему, я добавил абстрактное свойство SourceDirectory... требуя, чтобы я также создал абстрактный класс VirtualFileReader

Теперь с помощью этой настройки вы можете создать столько LessFileReaders, сколько хотите. Вам просто нужно расширить абстрактный VirtualFileReader, как это видно в

  • BootstrapFileReader расширяет VirtualFileReader
    • Единственная цель BootstrapFileReader состоит в том, чтобы иметь свойство-getter для SourceDirectory, в котором поставщик / minifier найдет меньше файлов, которые необходимо импортировать

В моем случае Bootstraps Less-Files находится в ~/Content/bootstrap/less, который должен быть по умолчанию, если вы устанавливаете "twitter.bootstrap.less" -nugget.

Если у вас будет другой каталог в вашем приложении, в котором будет меньше файла, который снова будет иметь несколько импорта, вы просто создадите новый класс, расширяющий VirtualFileReader, и определите свойство-getter для SourceDirectory, чтобы вернуть соответствующий путь

Если вы хотите использовать этот метод Bundling для фактического связывания и минимизации меньших файлов в производственной среде, вы просто добавляете InstantStyleBundle-instantion к BundlerConfig.cs:

bundles.Add(new LessStyleBundle<BootstrapFileReader>("~/bundles/BootstrapCSS")
    .Include("~/Content/bootstrap/less/bootstrap.less"));

и, конечно, ваш _Layout.cshtml должен также знать о готовом комплекте

@Styles.Render("~/bundles/BootstrapCSS")

МОДИФИКАЦИЯ 2)

теперь незначительная модификация, которую я также должен был добавить, чтобы получить эту работу

В моей первой попытке связать bootstrap.less я использовал этот

bundles.Add(new LessStyleBundle<BootstrapFileReader>("~/Content/BootstrapCSS")
    .Include("~/Content/bootstrap/less/bootstrap.less"));

Я думал, что буду использовать Content в маршрутах для CSS/Less и Bundles в маршрутах для Javascript.

Но это не работает из коробки. ASP.net не позволяет создавать пакет, который начинается с ~/Content. Вы получите отказ авторизации 403. Поэтому самым простым решением для этого является использование ~/bundles:

bundles.Add(new LessStyleBundle<BootstrapFileReader>("~/bundles/BootstrapCSS")
    .Include("~/Content/bootstrap/less/bootstrap.less"));

Поскольку для этой проблемы не так много реальных решений, я надеюсь, что это поможет хотя бы некоторым из вас, если вы планируете интегрировать twitter bootstrap в ваше приложение asp.net mvc4.

С наилучшими пожеланиями, Инго

Ответ 2

Я изменил обходное решение Ingo, чтобы избавиться от пользовательских классов для каждого каталога. Кроме того, я добавил правильный вывод исключения (потому что в противном случае все исключения были тихими, и вы просто получили пустой файл в случае ошибки).

public class LessTransform : IItemTransform
{
    [ThreadStatic]
    internal static string CurrentParsedFileDirectory;


    public string Process (string includedVirtualPath, string input)
    {
        CurrentParsedFileDirectory = Path.GetDirectoryName (includedVirtualPath);

        var config = new DotlessConfiguration
        {
            MinifyOutput = false,
            CacheEnabled = false,
            MapPathsToWeb = true,
            ImportAllFilesAsLess = true,
            LessSource = typeof (VirtualFileReader),
            Logger = typeof (ThrowExceptionLogger)
        };

        return Less.Parse (input, config);
    }
}


internal class VirtualFileReader : IFileReader
{
    public bool UseCacheDependencies
    {
        get { return false; }
    }


    public byte[] GetBinaryFileContents (string fileName)
    {
        return File.ReadAllBytes (GetFullPath (fileName));
    }


    public string GetFileContents (string fileName)
    {
        return File.ReadAllText (GetFullPath (fileName));
    }


    public bool DoesFileExist (string fileName)
    {
        return File.Exists (GetFullPath (fileName));
    }


    public string GetFullPath (string path)
    {
        if (string.IsNullOrEmpty (path))
            return string.Empty;

        return HostingEnvironment.MapPath (path[0] != '~' && path[0] != '/'
                                               ? Path.Combine (LessTransform.CurrentParsedFileDirectory, path)
                                               : path);
    }
}


public class ThrowExceptionLogger : Logger
{
    public ThrowExceptionLogger (LogLevel level) : base (level)
    {
    }


    protected override void Log (string message)
    {
        if (string.IsNullOrEmpty (message))
            return;

        if (message.Length > 100)
            message = message.Substring (0, 100) + "...";

        throw new LessTransformException (message);
    }
}


[Serializable]
public sealed class LessTransformException : Exception
{
    public LessTransformException (string message) : base (message)
    {
    }
}

Использование:

bundles.Add (new StyleBundle ("~/styles-bundle/common")
    .Include ("~/content/bootstrap/bootstrap.less", new LessTransform ()));

Ответ 3

У меня была такая же проблема сегодня, я нашел работу, но мне тоже хотелось бы найти лучшее решение. Я также пытался использовать бесчисленные и настраиваемые преобразования, как то, что у вас есть.

Обход проблемы:

Событие предварительной сборки:

"$(SolutionDir)packages\dotless.1.3.1.0\tool\dotless.compiler.exe" "$(ProjectDir)Content\less\bootstrap.less"

Это создаст файл bootstrap.css, который затем можно включить как обычный CSS вместо LESS.

Это решение не является идеальным, так как вам придется обновлять событие сборки каждый раз, когда вы обновляете без ограничений, а также иметь ручку связки, она также более чистая.

Ответ 4

Я действительно рекомендую вместо этого установить WebEssentials 2012.

Он будет генерировать css файл и мини-css файл из вашего .less, и вы можете ссылаться на css. Он автоматически обновляет css каждый раз, когда вы вносите изменения в ваш .less, поэтому нет необходимости помнить какие-либо шаги предварительной сборки или что-то еще...

При установке WebEssentials вы также получите другие слабые функции, такие как предварительный просмотр CoffeeScript, TypeScript и LESS. JSHint, автоматическая минификация и многое-многое другое "лакомства"!