In my project, I use some resources (mainly images) in both SASS and Blade. Also, I have some resources only used in SASS, and some only used in Blade.
For example, I could use mix('images/logo.png')
in Blade files, and background: url('../images/logo.png')
in SASS files.
As for my directory structure, I did the following :
- resources
- js
- sass
- images // All images used by Blade, Sass, or both
- fonts
In order to compile my resources and place them in the public
folder, I use to following 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();
In result, I get that URL in app.css :
background: url(/images/logo.png?0e567ce87146d0353fe7f19f17b18aca);
While I get another in rendered HTML :
src="/images/logo.png?id=4d4e33eae039c367c8e9"
They are considered as 2 different resources, that's not what I expected...
Potential workaround
I discovered that CSS files generated by SASS use a versioned URL even if I don't specify version()
in webpack.mix.js
. So I was wondering maybe I could use some trick, like this one :
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
});
And use it in SASS like so :
background-image: url(versioned('/images/logo.png'));
But this solution have a lot of drawbacks, I am obliged to use the versioned
function every time, my source code won't work easily in other projects without the webpack.mix.js
function, and I have to edit every files that I use in my resources folder to use the function.
Other solution?
I think the source of my problem could come from the way I structured my files, I have a resources/images
folder which contains images used by SASS but also used by Blade.
Images used in SASS will be copied to public/images
because that's the way SASS works with webpack, and these images will also be copied a second time because I used mix.copy()
(because I need the other files to be inside the public folder in order to be accessible in Blade/HTML).
I'm pretty sure I'm mistaking somewhere, I looked over the internet for a proper way to work with SASS and Blade resources in Laravel but I didn't find anything relevant.
Maybe I should consider another file structure ? But which one ?
I discovered that CSS files generated by SASS use a versioned URL even if I don't specify version() in webpack.mix.js.
Rewriting url()
within stylesheets is a webpack feature, it appends the computed MD5 hash of the file to the URL. mix.version()
on the other hand generates a different hash thanks to those lines:
/**
* 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 reads the file as a string (not as a buffer), hashes it and extracts only the first 20 characters. I can't figure out a simple way to override this behavior, a quick and dirty workaround is the redefine the hash
function:
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;
}
A better way is to extend Laravel Mix and define your own versionMD5()
method, you might copy some code from this extension.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With