2016-09-09 8 views
3

В моем сайте Hakyll У меня есть таблица стилей связаны на страницу:Как релятивизировать URL-адреса в css-файлах в Hakyll?

<link rel="stylesheet" type="text/css" href="/css/my.css"> 

Этот CSS содержит @font-face директиву, связывающую к файлу шрифта:

@font-face { 
font-family: "Bla"; 
src: url("/data/bla.ttf") format("truetype"); 
} 

Проблема в том, что URL шрифта не получить релятивизацию relativizeUrls, даже если я переместю его в тег <script> внутри самой страницы. Как решить эту проблему?

ответ

1

Я прошел более легкий путь. Я использовал этот код, чтобы получить корневой путь относительно текущего элемента:

rootPath :: Compiler String 
rootPath = (toSiteRoot . fromJust) <$> (getUnderlying >>= getRoute) 

А потом создал Context с постоянным полем:

fontCtx = do 
    root <- rootPath 
    return $ constField "fontRoot" root 

Наконец, я переехал @font-face пункт из файла CSS в HTML один и использовал мое поле там:

<style type="text/css"> 
     @font-face { 
      ... 
      src: url("$fontRoot$/data/bla.ttf") format("truetype"); 
     } 
    </style> 

Этот контекст поля оказался весьма полезным и в других местах, например, пути строк в JavaScript коде, whic h Я также использую.

+0

О да, это определенно хорошее решение. –

1

Hakyll's relativizeURLs использует TagSoup для синтаксического анализа и красивой печати HTML, поэтому он может работать только на URL-адресах, найденных внутри атрибутов HTML. Я не знаю каких-либо существующих функций, чтобы расширить это на CSS, а не только на атрибуты HTML.

Соответствующий код проходит через все тег разобран TagSoup и применяет функцию к атрибутам он распознает, как URL-адрес:

-- | Apply a function to each URL on a webpage 
withUrls :: (String -> String) -> String -> String 
withUrls f = withTags tag 
    where 
    tag (TS.TagOpen s a) = TS.TagOpen s $ map attr a 
    tag x    = x 
    attr (k, v)   = (k, if isUrlAttribute k then f v else v) 

(От Hakyll.Web.HTML)

Там нет никакого способа изменить этот обход логику от предоставил relativizeURLs компилятор, поэтому вам, вероятно, придется написать свой собственный. К счастью, это довольно просто: он получает корень сайта (с toSiteRoot), затем использует withURLs, чтобы применить функцию к каждому URL-адресу, который превращает абсолютные пути в относительные.

relativizeUrls item = do 
    route <- getRoute $ itemIdentifier item 
    return $ case route of 
     Nothing -> item 
     Just r -> fmap (relativizeUrlsWith $ toSiteRoot r) item 

relativizeUrlsWith root = withUrls rel 
    where 
    isRel x = "/" `isPrefixOf` x && not ("//" `isPrefixOf` x) 
    rel x = if isRel x then root ++ x else x 

(Выдержки из Hakyll.Web.RelativizeURLs).

Вам нужно будет объединить этот процесс с небольшим парсером CSS. Это будет выглядеть примерно так (в псевдокоде):

relativizeCssUrls root = renderCSS . fmap relativize . parseCSS 
    where relativize (URL url) 
      | isRel url = URL (root <> url) 
      | otherwise = URL url 
     relativize other = other 

Я не использовал какие-либо библиотеки CSS синтаксический/печать, так что я не могу дать вам хорошее предложение здесь, но css-text кажется достойной отправной точкой ,

2

tl; dr - Вы можете использовать пакет Beerend Lauwers's hakyll-extra (похоже, на hackage), который обеспечивает макрос relativizeUrl. Или выполните свою собственную работу следующим образом:

Если у вас слишком много ссылок и не нужно вводить парсер CSS только для этого, вы можете просто создать поле функции - эффективно, макрос - который позволяет вам звонить, напримерrelativize("/some/url") изнутри. (Я столкнулся с подобной проблемой, потому что мне хотелось релятивизировать ссылки на таблицу стилей для использования только старыми версиями Internet Explorer, а до TagSoup ссылки выглядели так, как если бы они были в комментариях, поэтому они не обрабатывали их.)

Во-первых, нам нужно написать версию relativizeUrls которая просто работает на одном URL:

import Data.List as L 

-- | Relativize URL. Same logic as "relativizeUrlsWith" in 
-- Hakyll.Web.Html.RelativizeUrls, but for just one url. 
relativizeUrl :: String --^Path to the site root 
        -> String --^link to relativize 
        -> String --^Resulting link 
relativizeUrl root = rel 
    where 
    isRel :: String -> Bool 
    isRel x = "/" `L.isPrefixOf` x && not ("//" `L.isPrefixOf` x) 
    rel x = if isRel x then root ++ x else x 

Затем мы определяем «поле функции», который может быть добавлен в контекстах.

import Data.Maybe (maybe) 

-- ugh. ugly name. 
relativizeFuncField :: Context a 
relativizeFuncField = functionField "relativize" relativize 
    where 
    relativize :: [String] -> Item a -> Compiler String 
    relativize args item = do 
     siteRoot <- getRoot <$> (getRoute $ itemIdentifier item) 
     arg <- case args of 
       [arg] -> return arg 
       _  -> error "relativize: expected only 1 arg" 
     return $ relativizeUrl siteRoot arg 

    getRoot :: Maybe String -> String 
    getRoot = maybe (error "relativize: couldn't get route") toSiteRoot 

Затем, в любом месте вы хотите использовать этот макрос, вместо того, чтобы использовать, скажем, defaultContext, используйте relativizeFuncField <> defaultContext. например .:

import Data.Monoid((<>)) 

main = 
    -- ... 

    match (fromList ["about.rst", "contact.markdown"]) $ do 
     route $ setExtension "html" 
     compile $ pandocCompiler 
      >>= loadAndApplyTemplate "templates/default.html" (relativizeFuncField <> defaultContext) 
      >>= relativizeUrls 

Таким образом, в конце концов, это означает, что в файле, вы можете написать $relativize("/path/to/file")$ в любом месте, где TagSoup еще не релятивизации ссылки.

Надеется, что это использование :)
(Это было с помощью Hakyll 4.9.0.0, но я предполагаю, что другие версии 4.X являются почти таким же.)

редактируемых: пса, многие благодаря Beerend Lauwers, который объяснил функциональные поля Hakyll в своем посте here

отредактировал еще раз: d'oh. Я не видел, что Beerend на самом деле уже положил функцию relativizeUrl в свой пакет hakyll-extra.