Compartilhamento de versão de recursos Laravel Blade, Mix e SASS

8

No meu projeto, uso alguns recursos (principalmente imagens) no SASS e no Blade. Além disso, tenho alguns recursos usados ​​apenas no SASS e outros usados ​​apenas no Blade.

Por exemplo, eu poderia usar mix('images/logo.png')nos arquivos Blade e background: url('../images/logo.png')nos arquivos SASS.

Quanto à minha estrutura de diretórios, fiz o seguinte:

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

Para compilar meus recursos e colocá-los na publicpasta, eu uso o seguinte 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, recebo esse URL no app.css:

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

Enquanto eu recebo outro em HTML renderizado:

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

Eles são considerados dois recursos diferentes, não era o que eu esperava ...

Solução potencial

Eu descobri que os arquivos CSS gerados pelo uso SASS um Versioned URL mesmo se eu não especificar version()no webpack.mix.js. Então eu estava pensando que talvez eu pudesse usar algum truque, 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
    });

E use-o no SASS da seguinte forma:

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

Mas esta solução tem muitas desvantagens, sou obrigado a usar a versionedfunção todas as vezes, meu código-fonte não funcionará facilmente em outros projetos sem a webpack.mix.jsfunção e tenho que editar todos os arquivos que utilizo na minha pasta de recursos para usar a função.

Outra solução?

Acho que a origem do meu problema pode vir da maneira como estruturei meus arquivos. Tenho uma resources/imagespasta que contém imagens usadas pelo SASS, mas também usadas pelo Blade.
As imagens usadas no SASS serão copiadas public/imagesporque é assim que o SASS trabalha com o webpack, e essas imagens também serão copiadas uma segunda vez porque eu as usei mix.copy()(porque eu preciso que os outros arquivos estejam dentro da pasta pública para estarem acessíveis no Blade / HTML).

Tenho certeza de que estou enganando em algum lugar, procurei na Internet uma maneira adequada de trabalhar com os recursos SASS e Blade no Laravel, mas não encontrei nada relevante.
Talvez eu deva considerar outra estrutura de arquivos? Mas qual deles ?

Marc
fonte
1
Quero dar meus próprios pensamentos enquanto também enfrento o mesmo problema. O Laravel Mix lida com arquivos .blade e CSS / JS de maneira diferente. Você não obterá o mesmo hash para sua imagem de logotipo e para os recursos .blade que ele usa mix.version()e para arquivos CSS, há seu próprio carregador de arquivos com sua própria função de hash. Não tem nada em comum com a estruturação de arquivos. Sua solução proposta parece ser uma boa opção, não acho que exista uma solução nativa para esse problema.
Svyat 15/04

Respostas:

2

Descobri que os arquivos CSS gerados pelo SASS usam uma URL com versão, mesmo que eu não especifique version () no webpack.mix.js.

A reescrita url()nas folhas de estilo é um recurso do webpack , que anexa o hash MD5 calculado do arquivo ao URL. mix.version()por outro lado, gera um hash diferente, graças a essas linhas:

/**
 * 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);
}

O Laravel Mix lê o arquivo como uma string (não como um buffer), faz o hash e extrai apenas os 20 primeiros caracteres. Não consigo descobrir uma maneira simples de substituir esse comportamento, uma solução rápida e suja é a redefinição da hashfunção:

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

Uma maneira melhor é estender o Laravel Mix e definir seu próprio versionMD5()método, você pode copiar algum código dessa extensão .

Navalha
fonte