Uso compartido de versiones de recursos Laravel Blade, Mix y SASS

8

En mi proyecto, uso algunos recursos (principalmente imágenes) tanto en SASS como en Blade. Además, tengo algunos recursos que solo se usan en SASS, y otros que solo se usan en Blade.

Por ejemplo, podría usar mix('images/logo.png')en archivos Blade y background: url('../images/logo.png')en archivos SASS.

En cuanto a la estructura de mi directorio, hice lo siguiente:

- resources
    - js
    - sass
    - images  // All images used by Blade, Sass, or both
    - fonts

Para compilar mis recursos y colocarlos en la publiccarpeta, utilizo lo siguiente webpack.mix.js:

mix.copy('resources/images/**/*.*', 'public/images');
mix.copy('resources/fonts/**/*.*', 'public/fonts');
mix.version('public/images/**/*.*');
mix.version('public/fonts/**/*.*');

mix.js('resources/js/app.js', 'public/js')
    .js('resources/js/vendor.js', 'public/js')
    .scripts([ // Old not ES6 JS
        'resources/js/tpl/core.min.js'
    ], 'public/js/core.min.js')
    .sass('resources/sass/app.scss', 'public/css')
    .sourceMaps()
    .version();

Como resultado, obtengo esa URL en app.css:

background: url(/images/logo.png?0e567ce87146d0353fe7f19f17b18aca);

Mientras obtengo otro en HTML renderizado:

src="/images/logo.png?id=4d4e33eae039c367c8e9"

Se consideran 2 recursos diferentes, eso no es lo que esperaba ...

Posible solución

Descubrí que los archivos CSS generados por el uso SASS un versionado URL incluso si no especifico version()en webpack.mix.js. Entonces me preguntaba si tal vez podría usar algún truco, como este:

const sass = require('sass');

// Custom SASS function to get versioned file name
// Uses Mix version md5 hash
const functions = {
    'versioned($uri)': function(uri, done) {
        uri = uri && uri.getValue() || uri;
        const version = File.find(path.join(Config.publicPath, uri)).version();
        done(new sass.types.String(`${uri}?id=${version}`));
    }
};

mix.sass('resources/sass/all.scss', 'public/css', { 
        sassOptions: {
            functions
        }
    })
    .options({ // Do not process URLs anymore
        processCssUrls: false
    });

Y úsalo en SASS así:

background-image: url(versioned('/images/logo.png'));

Pero esta solución tiene muchos inconvenientes, estoy obligado a usar la versionedfunción cada vez, mi código fuente no funcionará fácilmente en otros proyectos sin la webpack.mix.jsfunción, y tengo que editar todos los archivos que uso en mi carpeta de recursos para usar la función.

Otra solución?

Creo que la fuente de mi problema podría provenir de la forma en que estructuré mis archivos, tengo una resources/imagescarpeta que contiene imágenes utilizadas por SASS pero también utilizadas por Blade.
Las imágenes utilizadas en SASS se copiarán public/imagesporque esa es la forma en que SASS funciona con el paquete web, y estas imágenes también se copiarán una segunda vez porque lo usé mix.copy()(porque necesito que los otros archivos estén dentro de la carpeta pública para poder acceder a ellos en Blade / HTML).

Estoy bastante seguro de que me estoy equivocando en alguna parte, busqué en Internet una forma adecuada de trabajar con los recursos de SASS y Blade en Laravel, pero no encontré nada relevante.
¿Quizás debería considerar otra estructura de archivos? Pero cual ?

Bagazo
fuente
1
Quiero dar mis propios pensamientos mientras también enfrento el mismo problema. Laravel Mix maneja archivos .blade y CSS / JS de manera diferente. No obtendrá el mismo hash para la imagen de su logotipo que para los activos .blade que usa mix.version()y para los archivos CSS, hay su propio cargador de archivos con su propia función de hash. No tiene nada en común con la estructuración de archivos. Su solución propuesta parece ser una buena opción, no creo que haya una solución nativa para este problema.
Svyat

Respuestas:

2

Descubrí que los archivos CSS generados por SASS usan una URL versionada incluso si no especifico la versión () en webpack.mix.js.

La reescritura url()dentro de las hojas de estilo es una característica del paquete web , agrega el hash MD5 calculado del archivo a la URL. mix.version()por otro lado genera un hash diferente gracias a esas líneas:

/**
 * Read the file's contents.
 */
read() {
    return fs.readFileSync(this.path(), 'utf8');
}

/**
 * Calculate the proper version hash for the file.
 */
version() {
    return md5(this.read()).substr(0, 20);
}

Laravel Mix lee el archivo como una cadena (no como un búfer), lo codifica y extrae solo los primeros 20 caracteres. No puedo encontrar una manera simple de anular este comportamiento, una solución rápida y sucia es redefinir la hashfunción:

const mix = require('laravel-mix');
let md5 = require('md5');
let fs = require('fs-extra');

Mix.manifest.hash = function (file) {
    let f = new File(path.join(Config.publicPath, file));

    let hash = md5(fs.readFileSync(f.path()));

    let filePath = this.normalizePath(file);

    this.manifest[filePath] = filePath + '?' + hash;

    return this;
}

Una mejor manera es extender Laravel Mix y definir su propio versionMD5()método, puede copiar algún código de esta extensión .

Maquinilla de afeitar
fuente