Less.js: La función data-uri usa la ruta de la llamada data-uri, no la cadena con la ruta al archivo en formato.

Creado en 8 jul. 2015  ·  37Comentarios  ·  Fuente: less/less.js

de https://github.com/less/less.js/issues/2541 pero he visto esto en proyectos

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

Esperaría que la imagen se obtenga de app/content/images/btn.jpg pero se obtenga de images/btn.jpg .

Agradezco cualquier comentario sobre si se trata de un cambio importante (que justifica una mejora importante) o una corrección de errores.

bug

Comentario más útil

mmm...

Está bien. Entonces, ¿qué tal si tenemos una función resolve-url(url, [base]) - con un base opcional; por defecto al directorio del archivo en el que se escribe / declara / define la llamada a la función. Luego tenga una función declared-dir() que simplemente extraiga this.fileInfo y tome la ruta, si los autores quieren extraer la ruta explícitamente; desea tomar una ruta de un archivo diferente; o necesita usar esto como parte de alguna otra característica no relacionada con la resolución de URL.

Es decir, una llamada de forma completa (sin base implícita) sería algo como:

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

... y equivaldría a simplemente hacer

resolve-url("../foo")

... que es el equivalente ansioso al perezoso

url("../foo")

No se necesitan variables inyectadas 'automágicamente' de esa manera. Creo que esto podría implementarse puramente como un conjunto de funciones de complemento. Y la única mecánica central que necesitamos es una forma de marcar las URL como 'ya resueltas', de modo que la lógica de resolución predeterminada pueda bloquearse para que no se ejecute en las URL que salen de la función resolve-url .

La semántica debe quedar clara en la documentación del curso. Y esa documentación puede comparar explícitamente url con resolve-url para ilustrar la evaluación / resolución ansiosa y perezosa.

Todos 37 comentarios

Agradezco cualquier comentario sobre si se trata de un cambio importante (que justifica una mejora importante) o una corrección de errores.

FYI: Puede implementar esto elegantemente sin romper la compatibilidad con versiones anteriores de una manera muy simple.

Actualmente, la función data-uri() acepta un nodo de árbol Quoted que contiene una cadena como ruta del archivo y la resuelve internamente como una URL en la ubicación del archivo que contiene la llamada a la función. Puede sobrecargar la función data-uri para aceptar también un nodo de árbol Url que contenga una URL real. De esta manera, la URL debería normalizarse frente a la ubicación donde se llama a la función url() CSS y ya no debería normalizarse internamente en la función data-uri() Less.

P.ej

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

@rjgotten

Para mí, parece más una solución alternativa que una solución. En una iteración posterior, _tratarán_ de evitar la verbosidad moviendo url al mixin y el problema vuelve a aparecer. El otro problema es que tomando el caso de uso original de donde vino este problema (# 2541), a menudo se usa así:

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

(Por ejemplo, para una mezcla de fuentes y caras como ejemplo, generalmente son múltiples extensiones woff , ttf , eot , eot?#iefix etc.agregadas al mismo nombre de archivo ). Y de esta manera el url también es imposible.

@ siete-fases-max

Entonces realmente no creo que haya un período de solución adecuado. Necesita alguna forma de determinar el contexto en el que debe resolverse una URL relativa. O toma ese contexto de la información del archivo del archivo que definió el valor de la cadena que va a la función data-uri() , o hace que el punto de corte sea más explícito a través de la función url() y la función Url nodo de árbol.

Si desea admitir la sustitución de variables de ruta, las cosas se complican rápidamente porque necesita averiguar cómo deben normalizarse las distintas partes de la ruta.

Por ejemplo, ¿cómo se normaliza algo como:

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

¿Cómo combinaría la resolución de URL relativa y la combinación impulsada por la sustitución de tokens de esas dos rutas?

Supongo que una opción es resolver solo contra el archivo que define la cadena que se sustituye por un token de reemplazo, cuando el token de reemplazo está a la cabeza del valor de URL final que va a url() o data-uri() función e ignorar casos como el anterior. Eso parece más lógico.

Otra solución podría ser introducir algunas funciones de manejo de rutas para unir rutas o agregar / eliminar / editar extensiones de archivo (similar a cómo funciona la función unit() para las dimensiones, ¿tal vez?) Y hacer las cosas más explícitas.

P.ej

// 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 su primer ejemplo, no necesita ninguna normalización especial, "../../@{image}.jpg" expande a "../../../images/btn.jpg" tal como está escrito (y luego depende de data-uri manejar la ruta).
Y el segundo ejemplo es simplemente ... apilar una función para solucionar una función para solucionar una compatibilidad con versiones anteriores con ... ¿qué exactamente? _Rutas de archivo incorrectas_ al llamar a un mixin definido en otro archivo?

Después de todo, si se supone que funcionará "como se esperaba" _sólo_ con .background(url("images/btn.jpg")); , ¿en qué se diferencia de simplemente escribir .background(data-uri("images/btn.jpg")); directamente sin ningún cambio? :)


En otras palabras, lo que quiero decir es que si esto se va a arreglar, entonces se debería arreglar con data-uri sin importar cuán roto pueda ser. (Honestamente, no sé cuál sería una mejor estrategia: a. Esperar más informes / solicitudes para esto y luego (si hay suficientes) cambiar o b. Modificarlo antes para minimizar el posible impacto de rotura).



En otras palabras, asumiendo que url y data-uri se diseñaron inicialmente como intercambiables (es decir, data-uri es solo una versión especial de url (y se compila en CSS url al final)), sería bastante doloroso describir por qué exactamente ulr comporta "así" (con opciones así) mientras que data-uri comporta "así" (con opciones como esta), y para obtener cierto comportamiento necesitará data-uri(url()) combo, limitado a " data-uri dentro de un mixin y url _out_ con --relative-urls: on "<- Bhrrrr ... :)

Para mí, parece más una solución alternativa que una solución. En una iteración posterior, intentarán evitar la verbosidad moviendo la URL al mixin y el problema vuelve a aparecer.

Si estoy de acuerdo.

Poco a poco estoy tratando de construir para poner un poco más de esfuerzo en menos para una versión v3 y simplemente arreglar esto.

Estaba pensando en resolver todas las posibles rutas de archivo y probarlas una por una, aunque eso es un poco desagradable si hay varios archivos en diferentes ubicaciones ... Creo que estoy de acuerdo en que no es posible arreglar esto por completo para siempre, pero al menos podemos arreglar el caso normal.

Posiblemente, una función resolve() podría ayudar a pasar las URL resueltas a las funciones (pero si no es una ruta completa, no funcionaría y una ruta completa funcionaría cuando esto se solucionara ...)

Perdón por anotar pensamientos rápidamente.

Esta es solo una nota rápida, pero tendremos el mismo problema con la importación usando interpolación de variables.

importar usando interpolación de variables.

Eso es interesante e inquietante.
¿Es un caso de uso realmente compatible o una feliz coincidencia?

@rjgotten Es compatible, sin embargo, el soporte es algo limitado. Se rastreó en el n. ° 410

Esto funciona:

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

Esto no lo hace:

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

Editar: modificado para que el enlace funcione.

No estoy seguro de querer admitir la variable @import en mixins.
Todo el asunto de la importación de variables es un poco mágico y puede crear un código contrario a la intuición ... así que prefiero desalentar eso.

Mucha discusión sobre la solución alternativa, ¿tal vez solo solucionar el problema real? ¿No está claro que data-uri debe ser relativo al archivo que se está procesando?

En este momento tengo un archivo que importa _una referencia_ de otra ruta, y todavía se queja de data-uri. ¿Al menos eso debe ser un error? Quiero decir, si importo por referencia, no debería intentar reescribir las rutas en relación con el archivo actual.

@Ciantic

Quiero decir, si importo por referencia, no debería intentar reescribir las rutas en relación con el archivo actual.

¿Basado en qué exactamente? ¿Qué tiene que ver con reference ?

En este momento tengo un archivo que importa una referencia de otra ruta y todavía se queja de data-uri.

Parece lo contrario al problema anterior. ¿Podría darnos más detalles? (por ejemplo, rutas de los archivos de importación, importados y de datos, etc.).

styles.less

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

sub / test.less

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

Compila style.less, pero no test.less porque intenta usar data-uri en relación con test.less.

¿Por qué la importación de referencias intentaría reescribir las rutas? En mi opinión, no es necesario cuando se usan referencias.

Esperaría que data-uri siga las reglas de reescritura de URL en las opciones. Por supuesto ... es difícil decir lo que eso significa realmente con data-uri.

En general, data-uri debería resolverse en relación con el "archivo de llamada .less". Entonces, en el caso de un mixin, debería resolverse en relación al lugar donde se llama el mixin, no en relación con la ubicación del mixin. El mixin "mezcla" esas declaraciones y luego las resuelve. Entonces @lukeapage creo que tu interpretación es correcta:

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

Debería buscar btn.jpg en app/content/images/ . Si no es así, es un error porque no es así como deberían funcionar los mixins. No creo que sea un "cambio radical", sino una corrección de errores.

Dicho esto ... no creo que haya un problema con la resolución como:

  1. Intentando resolver en relación con la persona que llama.
  2. Intentando resolver en relación con el mixin.

Node.js intenta varias rutas de resolución. Siempre que la orden de resolución esté claramente documentada, puede gestionar las expectativas. Y hacerlo así significaría que si alguien hubiera basado su comportamiento n. ° 2 de no hacer, aún funcionaría en casi todos, si no en todos los casos.

@ matthew-dean
En general, data-uri debería resolverse en relación con el "archivo de llamada .less". Entonces, en el caso de un mixin, debería resolverse en relación al lugar donde se llama el mixin, no en relación con la ubicación del mixin. El mixin "mezcla" esas declaraciones y luego las resuelve.

Nunca podría hacer que eso funcione de una manera que sea generalmente correcta para todos los casos de uso.

Por ejemplo, ¿cómo admitiría las URL parametrizadas, es decir, las URL con tokens de reemplazo, que deberían resolverse en alguna carpeta base conocida con solo los tokens de reemplazo llenos? Ese caso requiere una nueva resolución contra el archivo de la persona que llama, no el archivo de la persona que llama.

Tuve una especie de epifanía sobre cómo solucionar esto de forma transparente sin el uso explícito de url() en el sitio de la llamada, lo que @ seven-phase-max con razón señaló como una mala idea (porque alguien eventualmente _will_ intentará refactorizarlo en la llamada mixin y romper cosas):

Cuando se crean nodos literales Quoted , conserve la información de su archivo. Propagar esa información a través de asignaciones de variables, llamadas mixtas, etc. Cualquier Quoted que se origine en un archivo de llamada, cuando sea tratado por url() o data-uri() , se resolverá contra ese archivo de llamada . Pero un Quoted que es parte de alguna lógica interna de un mixin aún se resuelve contra el archivo local del mixin.

Eso mantiene todo funcionando como se esperaba, excepto para escenarios con cadenas de sustitución como en:

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

Hay un truco que puede usar para solucionarlos también: al completar los tokens de sustitución, si un token está al comienzo de la cadena de sustitución, entonces la cadena resultante debe heredar la información del archivo del token completado, en lugar de eso. de la cadena de sustitución.

Si la intención del propio autor del mixin es que tales rutas se resuelvan contra el archivo mixin, aún pueden hacer que eso funcione usando, por ejemplo, "./@{image}.jpg" como patrón. Eso efectivamente le quita la carga de responsabilidad a la persona que llama, que es lo que usted querría.

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

producción:

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

Vaya, este es uno muy antiguo ... mi granito de arena ya que este problema también me está afectando.

¿Qué hay de agregar la opción a la función url, o crear una nueva función, que resolverá la url como absoluta? De esa manera, no importa dónde se establezca el mixin, funcionará con rutas absolutas y sin espacio para errores.

¿Qué hay de agregar la opción a la función url, o crear una nueva función, que resolverá la url como absoluta? De esa manera, no importa dónde se establezca el mixin, funcionará con rutas absolutas y sin espacio para errores.

El problema aquí no se trataba de rutas absolutas frente a rutas relativas. Se trataba de relativo contra sitio de llamada versus relativo contra sitio de declaración. Problema totalmente diferente.

Quizás el problema haya evolucionado, pero el comentario inicial y el título tratan sobre data-uri resolviendo la ruta en relación con el archivo donde se estaba llamando, que cuando se combina con un mixin colocado en otro lugar puede resolver la ruta incorrectamente. Bueno, ese es el problema que estoy experimentando.

Entonces, ¿dónde influyen las rutas absolutas en eso como solución?

Suponiendo que data-uri acepta rutas absolutas y hay una función hipotética absolute-url , el siguiente código funcionaría sin importar dónde se coloque el mixin.

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

@ miljan-aleksic Por "absoluto", ¿te refieres al archivo en la ubicación de data-uri ? Si es así, "absoluto" probablemente sea el término incorrecto.

Sin embargo, parece que un contenedor funcional para que una URL haga que cualquier URL sea relativa a un archivo es un buen enfoque. O un argumento adicional para url ().

@ matthew-dean, por absoluto me refiero a la ruta completa al archivo, por ejemplo: /users/myuser/projects/lessproject/icon.svg .

No entiendo su enfoque porque no veo cómo url () podría hacer una ruta relativa al archivo de ubicación de datos-uri sin saber su ubicación.

Sin embargo, parece que un contenedor funcional para que una URL haga que cualquier URL sea relativa a un archivo es un buen enfoque. O un argumento adicional para url ().

Curiosamente; eso es casi lo que sugerí hace unos años. ;-)

por absoluta me refiero a la ruta completa al archivo, por ejemplo: /users/myuser/projects/lessproject/icon.svg.

Parece que con absoluto te refieres a esta función absolute-url _pre-resuelve_ la ruta relativa que se le pasa a una ruta de salida completa, según la ubicación del archivo en el que se llama a la función absolute-url , relativo a la ubicación donde irá el archivo CSS de salida compilado.

Es decir, ambos quieren decir lo mismo. Al igual que yo, en ese momento.

a una ruta de salida completa, basada en la ubicación del archivo en el que se llama a la función de url absoluta, en relación con la ubicación donde irá el archivo CSS de salida compilado.

Al igual que en, reescribir las URL o la ruta raíz se seguirían aplicando, pero ¿según la ubicación de la definición? Eso parece un poco diferente al ejemplo original de @lukeapage , que no hablaba de URL relativas a la salida, sino de URL durante la compilación; por ejemplo, la ubicación data-uri() .

Así que este problema es un poco difícil de rastrear porque la gente ha publicado sobre temas similares, pero no exactamente idénticos. Es decir, cambiar una _fuente_ relativa probablemente requeriría una solución muy diferente a la de cambiar la _salida_ relativa. O quizás no; depende de la lógica de la ruta; pero debemos tener claro que data-uri no produce ninguna ruta como salida.

¿Quizás necesitamos algo como resolve() ? No sé, solo escupir, pero url(resolve(file(), "my/path")) ? Supongo que eso es lo que @ miljan-aleksic quiso decir con absolute() , ya que se resolvería en una URL absoluta. Pero aún debería tomar una entrada (como file() , para resolver contra). De lo contrario, podría hacer algo como file-resolve() para designar esa lógica en una función, pero tener resolve() y file() como dos funciones podría ser útil por separado.

La parte complicada de todo esto son todas las opciones de reescritura de URL, de las cuales ahora hay más a partir de la fusión de relaciones públicas que agregó soporte de módulo. (https://github.com/less/less.js/pull/3248). Entonces, si devuelve una URL relativa a un archivo, ¿aún se puede reescribir? Asumiría que sí, pero tendríamos que ser claros.

Sí, resolve () y file () define exactamente lo que estaba tratando de explicar. Espero que podamos ver esto implementado en un futuro cercano.

@ miljan-aleksic Está bien, entonces tiene sentido.

En realidad, file() no es del todo correcto, ya que eso devolvería el nombre de archivo que supongo, sería más como dir() . Y probablemente debería ser una constante variable por archivo.

Qué pasa:

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

Entonces, se agregaron dos cosas: 1) una función resolve () para combinar rutas, 2) una constante @DIR (y @FILE ?) Inyectada en cada archivo durante la evaluación. Lo único complicado de eso sería probar que esas vars inyectadas no anulan ni se fusionan con otras vars en la raíz, pero eso debería ser bastante simple de probar. ¿O deberían estar en minúsculas, como @arguments ? En cuyo caso, sugeriría @directory y @filename para evitar conflictos. ¿Cuál es la opción más Less-y?

¿Quizás necesitamos algo como resolve() ?

^ Bingo. Exactamente eso.

¿Cuál es la opción más Less-y?

Yo elegiría la opción Node.js-y: __dirname
Ha existido durante mucho tiempo y es bien conocido. Usar el mismo nombre para el mismo concepto en Less puede ser una buena idea.

Yo elegiría la opción Node.js-y: __dirname

Err ... eso no coincidiría con la palabra clave Less / CSS ni con la semántica de la variable o función Less. Tendríamos que hacerlo mejor que eso. @__dirname tal vez, pero los guiones bajos todavía son un poco raros para el idioma. No encaja en absoluto.

los guiones bajos siguen siendo un poco extraños para el idioma. No encaja en absoluto.

Se utiliza un guión bajo doble para indicar "algo que proporciona el sistema" en muchos idiomas, especialmente cuando se trata de cosas como variables intrínsecas. Así que el fuera de lugar es el punto aquí.

Por supuesto; si no le gusta, siempre puede optar por un derivado como @dirname o @dir-name .

Sin embargo, habiendo pensado en esto un poco más, ¿por qué incluso _necesitamos_ la ruta del archivo actual expuesta como una variable? ¿No se puede integrar en la función conceptual resolve() sí misma?

¿No se puede entretejer con la función conceptual resolve () en sí misma?

Y volvimos a mi propuesta, solo que con un nombre de función diferente. Todavía me gusta más, incluso mejor que resolve ().

Y volvimos a mi propuesta, solo que con un nombre de función diferente.

Algo así como.

Lo que estoy diciendo es que afaik no es necesario pasar la URL / ruta del archivo que contiene la llamada a una función resolve() , ya que esa ubicación debería ser _conocida_ para la función.

this dentro de una función se refiere a una instancia FunctionCaller , que tiene una propiedad currentFileInfo .
Esa propiedad se inicializa en la información del archivo del nodo Call AST correspondiente a la llamada a la función.
Es decir

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

Si estoy leyendo el código correctamente, la información del archivo corresponde a la información del archivo del lugar donde se _declara_ la llamada a la función, no al archivo donde se evalúa la llamada a la función.

Eso significa que debería estar bien usar esta información de archivo para un solucionador de URL 'ansioso'. Actuaría como se esperaba, incluso si se coloca una llamada a función dentro de un mixin que se importa y evalúa dentro del contexto de otro archivo. A saber: con la resolución fijada al archivo donde se define el mixin.

Sin embargo, habiendo pensado en esto un poco más, ¿por qué necesitamos la ruta del archivo actual expuesta como una variable? ¿No se puede entretejer con la función conceptual resolve () en sí misma?

Si estamos seguros de que nadie necesita el archivo actual o una función de resolución general ... tal vez ... la cosa es que una var local explícita deja en claro que no es una función genérica y que la función no se evaluará como cualquier otra función. Esa es mi verdadera preocupación, es la semántica. No importa cómo lo nombre, si no tiene un "marcador" especial para el "archivo actual", entonces no es como cualquier otra función que se resuelva de la misma manera en función de las entradas. En otras palabras, es una función que se resuelve según entradas invisibles, y eso me preocupa. Sin embargo, si la función es algo extremadamente explícito como current-file-resolve() , tal vez eso sea lo suficientemente claro. De lo contrario, confundirá a la gente por qué una llamada mixin no resolvió specialfunction() acuerdo con el archivo en el que se llamó, en lugar del archivo en el que se definió el mixin.

Entonces, no, una var local técnicamente no es _necesaria_, pero el significado / salida / comportamiento debe quedar claro a partir de la semántica.

mmm...

Está bien. Entonces, ¿qué tal si tenemos una función resolve-url(url, [base]) - con un base opcional; por defecto al directorio del archivo en el que se escribe / declara / define la llamada a la función. Luego tenga una función declared-dir() que simplemente extraiga this.fileInfo y tome la ruta, si los autores quieren extraer la ruta explícitamente; desea tomar una ruta de un archivo diferente; o necesita usar esto como parte de alguna otra característica no relacionada con la resolución de URL.

Es decir, una llamada de forma completa (sin base implícita) sería algo como:

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

... y equivaldría a simplemente hacer

resolve-url("../foo")

... que es el equivalente ansioso al perezoso

url("../foo")

No se necesitan variables inyectadas 'automágicamente' de esa manera. Creo que esto podría implementarse puramente como un conjunto de funciones de complemento. Y la única mecánica central que necesitamos es una forma de marcar las URL como 'ya resueltas', de modo que la lógica de resolución predeterminada pueda bloquearse para que no se ejecute en las URL que salen de la función resolve-url .

La semántica debe quedar clara en la documentación del curso. Y esa documentación puede comparar explícitamente url con resolve-url para ilustrar la evaluación / resolución ansiosa y perezosa.

En caso de que la idea de "variables inyectadas" no funcione (sin trucos adicionales) de todos modos porque los archivos importados tienen el mismo alcance.

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

Hablando de la implementación basada en funciones, creo (pero no puedo estar seguro) que todavía es posible obtener la ruta del archivo donde se invoca la función en algún lugar de su this.context.? o this.context.frames[?] más o menos.

emmmm, ¿entonces no tenemos una mejor manera de resolverlo?

@heynext
emmmm, ¿entonces no tenemos una mejor manera de resolverlo?

La evaluación perezosa hace que esto sea muy difícil de resolver correctamente, me temo.


@ siete-fases-max
Hablando de la implementación basada en funciones, creo (pero no puedo estar seguro) que todavía es posible obtener la ruta del archivo donde se invoca la función en algún lugar de su this.context.? o this.context.frames[?] más o menos.

Debería poder encontrar el nodo Call en su jerarquía, sí.

¿Fue útil esta página
0 / 5 - 0 calificaciones