Less.js: A função data-uri usa o caminho da chamada data-uri, não a string com o caminho para o arquivo em.

Criado em 8 jul. 2015  ·  37Comentários  ·  Fonte: less/less.js

de https://github.com/less/less.js/issues/2541, mas já vi isso em projetos

// mixins.less
.background(@image) {
    background-image: data-uri(@image);
}
// app/content/button.less
button {
  .background("images/btn.jpg");
}

Eu esperaria que a imagem fosse obtida em app/content/images/btn.jpg mas ela foi obtida em images/btn.jpg .

Agradeço o feedback sobre se esta é uma alteração significativa (garantindo uma grande melhoria) ou uma correção de bug.

bug

Comentários muito úteis

Hmm...

OK. Então, que tal termos uma função resolve-url(url, [base]) - com uma base opcional; padronizando para o diretório do arquivo no qual a chamada de função é escrita / declarada / definida. Então, tenha uma função declared-dir() que apenas puxa this.fileInfo e agarra o caminho, caso os autores queiram puxar o caminho explicitamente; deseja obter um caminho de um arquivo diferente; ou precisa usar isso como parte de algum outro recurso não relacionado à resolução de URL.

Ou seja, uma chamada de formulário completo (sem base implícita) seria algo como:

resolve-url("../foo", declared-dir())

... e seria equivalente a apenas fazer

resolve-url("../foo")

... que é o equivalente ansioso ao preguiçoso

url("../foo")

Nenhuma variável injetada 'automagicamente' necessária dessa maneira. Isso poderia ser implementado puramente como um conjunto de funções de plug-in, eu acho. E a única mecânica central de que precisamos é uma maneira de marcar URLs como 'já resolvidos', de modo que a lógica do resolvedor padrão possa ser bloqueada em URLs que saiam da função resolve-url .

A semântica deve ficar clara na documentação do curso. E essa documentação pode comparar explicitamente url com resolve-url para ilustrar a avaliação / resolução ansiosa e preguiçosa.

Todos 37 comentários

Agradeço o feedback sobre se esta é uma alteração significativa (garantindo uma grande melhoria) ou uma correção de bug.

Para sua informação: você pode implementar isso elegantemente sem quebrar a compatibilidade com versões anteriores de uma forma muito simples.

Atualmente, a função data-uri() aceita um nó de árvore Quoted contendo uma string como o caminho do arquivo e o resolve internamente como uma URL em relação à localização do arquivo que contém a chamada da função. Você pode sobrecarregar a função data-uri para também aceitar um nó de árvore Url contendo uma URL real. Dessa forma, a URL deve normalizar em relação ao local onde a função url() CSS é chamada e não deve mais ser normalizada internamente na função data-uri() Less.

Por exemplo

// app/content/button.less
button {
  .background(url("images/btn.jpg"));
}

@rjgotten

Para mim, parece mais uma solução alternativa do que uma correção. Em uma iteração posterior, eles _vai_ tentar evitar o detalhamento movendo url para o mixin e o problema surge novamente. O outro problema é que, considerando o caso de uso original de onde veio o problema (# 2541), ele costuma ser usado assim:

// mixins.less
.background(@image) {
    background-image: data-uri("@{image}.jpg");
}
// app/content/button.less
button {
  .background("images/btn");
}

(Por exemplo, para um mixin de fonte-face, normalmente são várias extensões woff , ttf , eot , eot?#iefix etc. anexadas ao mesmo nome de arquivo ) E desta forma o url também é impossível.

@ sete fases-máx.

Então eu realmente não acho que haja um período de solução adequado. Você precisa de alguma forma de determinar o contexto contra o qual um URL relativo deve ser resolvido. Ou você pega esse contexto das informações do arquivo do arquivo que definiu o valor da string indo para a função data-uri() , ou você torna o ponto de corte mais explícito por meio da função url() e Url nó de árvore.

Se você deseja oferecer suporte à substituição de variável de caminho, então as coisas rapidamente se tornam complicadas porque você precisa descobrir como as várias partes do caminho precisam ser normalizadas.

Por exemplo, como você normaliza algo como:

// mixins.less
.background(@image) {
    background-image: data-uri("../../@{image}.jpg");
}
// app/content/button.less
button {
  .background("../images/btn");
}

Como você combinaria a resolução de url relativa e a combinação orientada por substituição de token desses dois caminhos?

Suponho que uma opção seja resolver apenas o arquivo que define a string que está sendo substituída por um token substituto, quando o token substituto está no topo do valor do URL final indo para url() ou data-uri() função e ignora casos como o acima. Isso parece muito lógico.

Outra solução poderia ser introduzir algumas funções de manipulação de caminho para unir caminhos ou adicionar / remover / editar extensões de arquivo (semelhante a como a função unit() funciona para dimensões, talvez?) E tornar as coisas mais explícitas.

Por exemplo

// mixins.less
.background(@image) {
    background-image: data-uri(extension(<strong i="21">@image</strong>, "jpg"));
}
// app/content/button.less
button {
  .background(url("./images/btn"));
}

Para o seu primeiro exemplo, ele não precisa de nenhuma normalização especial, "../../@{image}.jpg" expande para "../../../images/btn.jpg" exatamente como está escrito (e então cabe até data-uri para lidar com o caminho).
E o segundo exemplo é apenas ... empilhar uma função para contornar uma função para contornar uma compatibilidade com versões anteriores com ... o que exatamente? Caminhos de arquivo _errados_ ao chamar um mixin definido em outro arquivo?

Afinal, se é para funcionar "conforme o esperado" _apenas_ com .background(url("images/btn.jpg")); , como é diferente de simplesmente escrever .background(data-uri("images/btn.jpg")); diretamente sem nenhuma alteração? :)


Em outras palavras, o que quero dizer é que se isso deve ser consertado, então deve ser consertado diretamente com data-uri não importa o quanto possa ser quebrado. (Honestamente, não sei qual seria a melhor estratégia: a. Aguarde mais relatórios / solicitações para isso e então (se houver o suficiente) altere ou b. Modifique antes para minimizar o possível impacto de interrupção).



Em outras palavras, assumindo que url e data-uri foram inicialmente projetados como intercambiáveis ​​(ou seja, data-uri é apenas uma versão especial de url (e compila para CSS url no final)), seria muito doloroso descrever por que exatamente ulr se comporta "assim" (com opções como essa), enquanto data-uri se comporta "assim" (com opções como esta), e para obter certos comportamentos, você precisará de data-uri(url()) combo, limitado a " data-uri dentro de um mixin e url _out_ dele com --relative-urls: on "<- Bhrrrr ... :)

Para mim, parece mais uma solução alternativa do que uma correção. Em uma iteração posterior, eles tentarão evitar o detalhamento movendo o url para o mixin e o problema ocorrerá novamente.

Sim, eu concordo.

Estou lentamente tentando colocar um pouco mais de esforço em menos para uma versão v3 e apenas consertar isso.

Eu estava pensando em trabalhar todos os caminhos de arquivo possíveis e experimentá-los um por um - embora seja um pouco desagradável se houver vários arquivos em locais diferentes ... Acho que concordo que não é possível corrigir isso completamente para sempre, mas pelo menos podemos consertar o caso normal.

Possivelmente, uma função resolve() pode ajudar a passar urls resolvidos para funções (mas, se não for um caminho completo, não funcionará e um caminho completo funcionará quando isso for corrigido ...)

Desculpe apenas anotar pensamentos rapidamente.

Esta é apenas uma nota rápida, mas teremos o mesmo problema com a importação usando interpolação de variáveis.

importação usando interpolação de variáveis.

Isso é interessante e inquietante.
É um caso de uso realmente suportado ou uma feliz coincidência?

@rjgotten É suportado, porém o suporte é um tanto limitado. Foi rastreado em # 410

Isso funciona:

<strong i="8">@variable</strong>: "path.less";
<strong i="9">@import</strong> "@{variable}";

Isso não:

.mixin(@variable) {
  <strong i="13">@import</strong> "@{variable}";
}

Editar: modificado para fazer o link funcionar.

Não tenho certeza se quero oferecer suporte à variável @import em mixins.
Toda a coisa de importação de variável é um pouco mágica e pode criar algum código contra-intuitivo ... então eu prefiro desencorajar isso.

Muita discussão sobre uma solução alternativa, talvez apenas consertar o problema real? Não está claro que o data-uri deve ser relativo ao arquivo que está sendo processado.

No momento eu tenho um arquivo que importa _uma referência_ de outro caminho, e ainda reclama do data-uri. Pelo menos isso deve ser um bug? Quer dizer, se eu importar por referência, não devo tentar reescrever os caminhos em relação ao arquivo atual.

@Ciantic

Quer dizer, se eu importar por referência, não devo tentar reescrever os caminhos em relação ao arquivo atual.

Com base no que exatamente? O que isso tem a ver com reference ?

No momento tenho arquivo que importa uma referência de outro caminho, e ainda reclama do data-uri.

Parece o oposto da questão acima. Você poderia fornecer mais detalhes? (por exemplo, caminhos dos arquivos de importação, importados e de dados, etc.).

styles.less

.something {
    background: data-uri("some.svg");
}

sub / test.less

<strong i="11">@import</strong> (reference) "../style.less";
.test {
    color: green;
}

Ele compila style.less, mas não test.less porque tenta usar data-uri em relação a test.less.

Por que o reference import tentaria reescrever os caminhos, não é necessário ao usar referências na minha opinião.

Eu esperaria que o data-uri siga as regras de reescrita de url nas opções. Claro .... difícil dizer o que isso realmente significa com data-uri.

Em geral, data-uri deve resolver em relação ao "arquivo .less de chamada". Portanto, no caso de um mixin, deve resolver em relação a onde o mixin é chamado, não em relação à localização do mixin. O mixin "mistura" essas afirmações e então as resolve. Então, @lukeapage , acho que sua interpretação está correta:

// app/content/button.less
button {
  .background("images/btn.jpg");
}

Deve procurar btn.jpg em app/content/images/ . Se não for, é um bug porque não é assim que os mixins deveriam funcionar. Não acho que seja uma "mudança significativa", mas uma correção de bug.

Dito isso ... não acho que haveria um problema de resolução como:

  1. Tentando resolver em relação ao chamador.
  2. Tentando resolver em relação ao mixin.

O Node.js tenta vários caminhos para resolução. Contanto que a ordem de resolução seja claramente documentada, você pode gerenciar as expectativas. E fazer assim significaria que se alguém tivesse baseado seu comportamento .less para fazer # 2, ainda funcionaria em quase todos, senão em todos os casos.

@ matthew-dean
Em geral, data-uri deve resolver em relação ao "arquivo .less de chamada". Portanto, no caso de um mixin, deve resolver em relação a onde o mixin é chamado, não em relação à localização do mixin. O mixin "mistura" essas afirmações e então as resolve.

Você nunca poderia fazer isso funcionar de uma maneira geralmente correta para todos os casos de uso.

Por exemplo, como você suportaria urls parametrizados, ou seja, urls com tokens de substituição, que devem ser resolvidos em alguma pasta de base conhecida com apenas os tokens de substituição preenchidos? Esse caso requer resolução em relação ao arquivo do chamado, não ao arquivo do chamador.

Tive uma pequena epifania sobre como consertar isso de forma transparente sem o uso explícito de url() no site da chamada, que @ seven-phase-max justamente chamou de uma má ideia (porque alguém eventualmente _will_ tentará refatorar na chamada do mixin e quebrar as coisas):

Quando Quoted nodes literais são criados, retém suas informações de arquivo. Propague essa informação por meio de atribuições de variáveis, chamadas de mixin, etc. Qualquer Quoted originado de um arquivo do chamador, quando tratado por url() ou data-uri() , seria resolvido contra esse arquivo do chamador . Mas um Quoted que faz parte de alguma lógica interna de um mixin ainda é resolvido contra o arquivo local do mixin.

Isso mantém tudo funcionando conforme o esperado, exceto para cenários com strings de substituição, como em:

// mixins.less
.background(@image) {
    background-image: data-uri("@{image}.jpg");
}
// app/content/button.less
button {
  .background("images/btn");
}

Há um truque que você pode usar para consertá-los também: ao preencher os tokens de substituição, se um token está bem no início da string de substituição, a string resultante deve herdar as informações do arquivo do token preenchido, em vez daquele da seqüência de substituição.

Se a intenção do próprio autor do mixin é fazer com que tais caminhos resolvam contra o arquivo mixin, ele ainda pode fazer esse trabalho usando, por exemplo, "./@{image}.jpg" como um padrão. Isso efetivamente transfere o fardo da responsabilidade para longe do chamador, que é o que você deseja.

// _mixins.less
.sprite(@image) {
    background: data-uri("../images/sprites/@{image}.png") no-repeat;
}
// main.less
div {
   .sprite('logo');
}

resultado:

div {
   background: url(data:image/png;base64,...) no-repeat;
}

Uau, este é muito antigo ... meus dois centavos, pois esse problema está me afetando também.

Que tal adicionar a opção de função url, ou criar uma nova função, que vai resolver o url como absoluto. Dessa forma, não importa onde o mixin esteja configurado, ele estará trabalhando com caminhos absolutos e sem espaço para erros.

Que tal adicionar a opção de função url, ou criar uma nova função, que vai resolver o url como absoluto. Dessa forma, não importa onde o mixin esteja configurado, ele estará trabalhando com caminhos absolutos e sem espaço para erros.

A questão aqui não era sobre caminhos absolutos versus caminhos relativos. Tratava-se de relativo ao site de chamada vs relativo ao site de declaração. Problema totalmente diferente.

Talvez o problema tenha evoluído, mas o comentário inicial e o título são sobre data-uri resolvendo o caminho em relação ao arquivo onde estava sendo chamado, o que quando combinado com um mixin colocado em outro lugar pode resolver o caminho incorretamente. Bem, esse é o problema que estou enfrentando.

Então, onde os caminhos absolutos influenciam isso como solução?

Assumindo que data-uri aceita caminhos absolutos e há uma função absolute-url hipotética, o código a seguir funcionaria independentemente de onde o mixin fosse colocado.

// mixins.less
.background(@image) {
    background-image: data-uri(@image);
}
// app/content/button.less
button {
  .background(absolute-url("images/btn.jpg"));
}

@ miljan-aleksic Por "absoluto" você quer dizer em relação ao arquivo na localização de data-uri ? Nesse caso, "absoluto" provavelmente é o termo errado.

No entanto, parece que um wrapper funcional para uma url para tornar qualquer url relativa a um arquivo é uma boa abordagem. Ou um argumento adicional para url ().

@ matthew-dean, por absoluto quero dizer o caminho completo para o arquivo, por exemplo: /users/myuser/projects/lessproject/icon.svg .

Não entendo sua abordagem, pois não vejo como url () poderia fazer um caminho relativo para o arquivo de localização uri de dados sem saber disso.

No entanto, parece que um wrapper funcional para uma url para tornar qualquer url relativa a um arquivo é uma boa abordagem. Ou um argumento adicional para url ().

Curiosamente; foi quase isso que sugeri alguns anos atrás. ;-)

por absoluto, quero dizer o caminho completo para o arquivo, por exemplo: /users/myuser/projects/lessproject/icon.svg.

Parece que com absoluto você quer dizer que absolute-url function _pré-resolve_ o caminho relativo passado a ele para um caminho de saída completo, com base na localização do arquivo em que a função absolute-url é chamada, em relação ao local onde o arquivo CSS de saída compilado irá.

Ou seja, vocês dois significam a mesma coisa. Assim como eu, na época.

para um caminho de saída completo, com base na localização do arquivo no qual a função absolute-url é chamada, em relação ao local onde o arquivo CSS de saída compilado irá.

Tipo, reescrever URLs ou rootpath ainda se aplicaria, mas com base no local de definição? Isso parece um pouco diferente do exemplo original de @lukeapage , que não estava falando sobre URLs em relação à saída, mas URLs _durante a compilação_; por exemplo, a localização data-uri() .

Portanto, esse problema é um pouco difícil de rastrear porque as pessoas postaram sobre problemas semelhantes, mas não exatamente idênticos. Ou seja, alterar uma _source_ relativa provavelmente exigiria uma solução muito diferente do que alterar a _saída_ relativa. Ou talvez não; depende da lógica do caminho; mas devemos deixar claro que data-uri não produz nenhum caminho como saída.

Talvez precisemos de algo como resolve() ? Não sei, apenas cuspindo, mas url(resolve(file(), "my/path")) ? Eu acho que é isso que @ miljan-aleksic quer dizer com absolute() , pois resolveria para uma URL absoluta. Mas ainda assim deve ser necessária uma entrada (como file() , para resolver contra). Caso contrário, você poderia fazer algo como file-resolve() para designar essa lógica em uma função, mas ter resolve() e file() como duas funções pode ser útil separadamente.

A parte complicada de tudo isso são todas as opções de reescrever URL, que agora existem mais a partir da fusão de PR que suporte a módulo adicionado. (https://github.com/less/less.js/pull/3248). Portanto, se ele retornar uma URL relativa ao arquivo, ainda poderá ser reescrito? Eu diria que sim, mas precisamos ser claros.

Sim, resolve () e file () definem exatamente o que eu estava tentando explicar. Espero que possamos ver isso implementado em um futuro próximo.

@ miljan-aleksic Ok, isso faz sentido então.

Na verdade, file() não está certo, já que isso retornaria o nome do arquivo que eu presumiria, seria mais parecido com dir() . E provavelmente deve ser uma constante variável por arquivo.

A respeito:

data-uri(resolve(<strong i="10">@DIR</strong>, "my/path"))

Portanto, duas coisas adicionadas: 1) uma função resolve () para combinar caminhos, 2) uma constante @DIR (e @FILE ?) Injetada em cada arquivo durante a avaliação. A única coisa complicada sobre isso seria testar se esses vars injetados não sobrescrevem ou são mesclados com outros vars na raiz, mas deve ser bem simples de testar. Ou deveriam estar em letras minúsculas, como @arguments ? Nesse caso, eu sugeriria @directory e @filename para evitar conflitos. Qual é a opção mais menos y?

Talvez precisemos de algo como resolve() ?

^ Bingo. Exatamente isso.

Qual é a opção mais menos y?

Eu escolheria a opção Node.js-y: __dirname
Já existe há muito tempo e é bem conhecido. Usar o mesmo nome para o mesmo conceito em Less pode ser uma boa ideia.

Eu escolheria a opção Node.js-y: __dirname

Err ... isso não corresponderia à palavra-chave Less / CSS, nem à variável Less, nem à semântica da função. Teríamos que fazer melhor do que isso. @__dirname talvez, mas os sublinhados ainda são um pouco estranhos para a linguagem. Não se encaixa de jeito nenhum.

os sublinhados ainda são um pouco estranhos para o idioma. Não se encaixa de jeito nenhum.

Um sublinhado duplo inicial é usado para indicar 'algo que o sistema fornece' em muitas linguagens, especialmente quando se trata de coisas como variáveis ​​intrínsecas. Portanto, o desfasamento é o ponto principal.

Claro; se você não gostar, pode sempre usar um derivado como @dirname ou @dir-name .

No entanto, tendo pensado mais sobre isso, por que nós _precisamos_ do caminho do arquivo atual exposto como uma variável? Não pode ser tecido na própria função conceitual resolve() ?

Não pode ser tecido na própria função conceitual resolve ()?

E voltamos à minha proposta, apenas com um nome de função diferente. Eu ainda gosto mais, ainda melhor como resolve ().

E voltamos à minha proposta, apenas com um nome de função diferente.

Tipo de.

O que estou querendo dizer é que afaik não há necessidade de passar a URL / caminho do arquivo que contém a chamada para uma função resolve() , já que essa localização deve ser _conhecida_ pela função.

this dentro de uma função refere-se a uma instância FunctionCaller , que tem uma propriedade currentFileInfo .
Essa propriedade é inicializada com as informações do arquivo do nó Call AST correspondente à chamada da função.
Ou seja

https://github.com/less/less.js/blob/4e903e8254cc20fec80fccd35794fb797949e653/lib/less/tree/call.js#L47

Se estou lendo o código corretamente, as informações do arquivo lá correspondem às informações do arquivo do local onde a chamada da função é _declarada_ - não ao arquivo onde a chamada da função é avaliada.

Isso significa que não há problema em usar essas informações de arquivo para um resolvedor de URL 'ansioso'. Ele funcionaria conforme o esperado, mesmo se uma chamada de função fosse colocada dentro de um mixin importado e avaliado dentro do contexto de outro arquivo. A saber: com a resolução fixada no arquivo onde o mixin está definido.

No entanto, depois de pensar um pouco mais sobre isso, por que precisamos que o caminho do arquivo atual seja exposto como uma variável? Não pode ser tecido na própria função conceitual resolve ()?

Se tivermos certeza de que ninguém precisa do arquivo atual ou de uma função de resolução geral ... talvez ... a coisa é uma var local explícita deixa claro que não é uma função genérica e que a função não será avaliada como qualquer outra função. Essa é minha preocupação real, é a semântica. Não importa o que você nomeie, se você não tiver um "marcador" especial para "arquivo atual", então não é como qualquer outra função que é resolvida da mesma forma com base nas entradas. Em outras palavras, é uma função que se resolve de acordo com entradas invisíveis, e isso me preocupa. No entanto, se a função for algo extremamente explícito como current-file-resolve() , talvez isso seja claro o suficiente. Caso contrário, você apenas vai confundir as pessoas porque uma chamada do mixin não resolveu specialfunction() acordo com o arquivo em que foi chamada, ao invés do arquivo em que o mixin foi definido.

Portanto, não, um var local é tecnicamente _necessário_, mas o significado / saída / comportamento precisa estar claro na semântica.

Hmm...

OK. Então, que tal termos uma função resolve-url(url, [base]) - com uma base opcional; padronizando para o diretório do arquivo no qual a chamada de função é escrita / declarada / definida. Então, tenha uma função declared-dir() que apenas puxa this.fileInfo e agarra o caminho, caso os autores queiram puxar o caminho explicitamente; deseja obter um caminho de um arquivo diferente; ou precisa usar isso como parte de algum outro recurso não relacionado à resolução de URL.

Ou seja, uma chamada de formulário completo (sem base implícita) seria algo como:

resolve-url("../foo", declared-dir())

... e seria equivalente a apenas fazer

resolve-url("../foo")

... que é o equivalente ansioso ao preguiçoso

url("../foo")

Nenhuma variável injetada 'automagicamente' necessária dessa maneira. Isso poderia ser implementado puramente como um conjunto de funções de plug-in, eu acho. E a única mecânica central de que precisamos é uma maneira de marcar URLs como 'já resolvidos', de modo que a lógica do resolvedor padrão possa ser bloqueada em URLs que saiam da função resolve-url .

A semântica deve ficar clara na documentação do curso. E essa documentação pode comparar explicitamente url com resolve-url para ilustrar a avaliação / resolução ansiosa e preguiçosa.

Para o caso de a ideia de "variáveis ​​injetadas" não funcionar (sem hacks adicionais) de qualquer maneira, porque os arquivos importados têm o mesmo escopo.

<strong i="7">@__dir</strong>: "whatever";
// *everywhere* it's the only <strong i="8">@__dir</strong> value = the path of "c"
<strong i="9">@import</strong> "a";
<strong i="10">@import</strong> "b";
<strong i="11">@import</strong> "c";

Por falar em implementação baseada em função, acho (mas não posso ter certeza) que ainda é possível obter o caminho do arquivo onde a função é invocada em algum lugar em seu this.context.? ou this.context.frames[?] ou então.

emmmm, então não temos uma maneira melhor de resolver isso?

@heynext
emmmm, então não temos uma maneira melhor de resolver isso?

A avaliação preguiçosa torna isso muito difícil de resolver corretamente, infelizmente.


@ sete fases-máx.
Por falar em implementação baseada em função, acho (mas não tenho certeza) que ainda é possível obter o caminho do arquivo onde a função é invocada em algum lugar em seu this.context.? ou this.context.frames[?] ou então.

Você deve ser capaz de encontrar o nó Call em sua hierarquia, sim.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

vecerek picture vecerek  ·  5Comentários

xblakestone picture xblakestone  ·  3Comentários

pknepper picture pknepper  ·  3Comentários

bassjobsen picture bassjobsen  ·  6Comentários

awebdev picture awebdev  ·  4Comentários