Open zbecknell opened 6 years ago
This workaround works okay, except you can't use variables from _partial.scss in site.scss defeating the purpose of the entire project if I have to put everything in one class or mirror variables across files.
Sure, I can modify the main file every time I change something in a partial file but that's a huge pain in the ass and a good way to get me pulling my hair out when I forget about it and can't figure out why a div's style won't update.
I think this lib is unusable without this feature.
and i try to use enableCaching to false for solve this.; but it don't recompile each time. (so it use cache....)
It appears that setting enableCaching to false does not add any additional header information. Only when enableCaching is set to true, does it inject headers to support caching the output.
I was successful compiling multiple source files and having it reload after a change to any of them by controlling the cache mechanism on my own, which I often do anyway, using either tag helpers or NWebsec.
I applied the asp-append-version tag helper to my stylesheet reference in HTML layout.
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
And then included the bundle files as follows:
services.AddWebOptimizer(pipeline =>
{
pipeline.AddScssBundle("/css/site.css", "/Styles/*.scss").UseContentRoot();
});
Changing any of the partial files and doing a refresh of the page renders a new CSS file.
I missed this at first in the documentation. If you import the WebOptimization.Core TagHelpers, then CSS links are automatically versioned. The addition of the asp-append-version tag is irrelevant.
Include this in your _ViewImports.cshtml file.
@addTagHelper *, WebOptimizer.Core
@jbeanky Do you have your partials in the /Styles/
folder? If so, they are probably being bundled into site.css
and therefor are being referenced twice. In this case I've found that they will indeed cause the cache be busted, but that's only because the partials are being explicitly included (in a glob in your case).
Is there any update to resolve this issue?
I've found an alternative workaround to this, which is basically to use custom WebOptimizer bundling logic instead of SCSS' own @import
functionality.
Pros:
Cons:
enableTagHelperBundling=false
in your WebOptimizer settings, because that causes each file in the bundle to be sent separately to the browser, so essentially the imports won't happen.@import
in your SCSS and you have to define your imports when configuring WebOptimizer, so you can't change which files import which other files without redeploying the web app (at least, not unless you define all your SCSS file relationships in the appsettings.json
and parse and implement them with your own logic on startup; then you could get away with modifying appsettings.json
and just restarting the app).services.AddWebOptimizer(pipeline =>
{
pipeline
.AddBundle("/scss-bundle.css", "text/css; charset=UTF-8",
"/scss/base-file.scss",
"/scss/partials/_partial-1.scss",
"/scss/partials/_partial-2.scss")
.AdjustRelativePaths()
.Concatenate()
.CompileScss()
.InlineImages()
.FingerprintUrls()
.MinifyCss();
}
The main trick is just adding the files in the order you want them appended to your base SCSS file, and then calling .Concatenate()
before calling .CompileScss()
. Also, if you currently do an @import
in the middle of an SCSS file, you'll need to break that file into two files, one for the content before the @import
and one for the content after. Then define your bundle with them in that order: the 'before import' file, the file to import, then the 'after import' file.
This obviously isn't ideal, but after a quick look at the WebOptimizer code I didn't see an obvious first-class fix for the problem, and this works well enough for my scenario (basically just setting different SCSS variable values for different clients in a multi-tenant application).
To mitigate debugging headaches caused by not being able to disable bundling in dev, in my actual implementation for this, I check IHostingEnvironment.IsDevelopment()
and only append the .MinifyCss()
call on my bundles when the app is not running in dev. That way, while the files are bundled, they're at least readable when I'm trying to debug stuff. (If you want to do that, keep in mind that you can't inject an IHostingEnvironment
object into Startup.ConfigureServices()
directly; you have to inject it into the Startup
constructor and store a reference to it on the Startup
class object, then reference that.)
I guess this issue cannot be resolved unless the Core project is updated as well.
WebOptimizer.Core/Taghelpers/BaseTagHelper.cs contains this method:
protected void AddToCache(string cacheKey, string value, IFileProvider fileProvider, params string[] files)
{
var cacheOptions = new MemoryCacheEntryOptions();
foreach (string file in files)
{
cacheOptions.AddExpirationToken(fileProvider.Watch(file));
}
Cache.Set(cacheKey, value, cacheOptions);
}
Because this method files parameter is only the entry scss this results in the only file watched is the "css/site.scss"-file.
The only part of this WebOptimizer.Sass project aware of the containing scss files is the compiler (which is a separate library). However the Compiler.cs could call ConvertToCss using GenerateSourceMap = true (which it is not). Then the sourcemap could be used to retrieve the underlying files and push the list of files all the way down to the AddToCache method (I haven't figured that part out, but maybe someone here could).
Currently, including a
.scss
file with a partial file will compile correctly but will not re-compile with changes only to the partial. So the following will only recompile when changes tosite.scss
are made but not to_partial.scss
which it is referencing:A workaround is to include the partial in the bundle list:
This does bust the cache when changes to the partial are made and causes the full
site.scss
to recompile as well, but results in both files being served in the bundle and duplicate css appearing.Less than ideal solution:
Manually include the partials, as in the workaround, and use them only for purposes of cache-busting but do not duplicate them in the bundle.
More ideal solution:
Automatically include partials for purposes of cache-busting and do not duplicate them in the bundle. We can find the partials quite easily by generating the source map during conversion, but after that it would be a matter of associating them appropriately and evaluating when they've been changed or not.
I'm not familiar enough with the code to know whether this would require major architectural changes or not, but it seems like this should be the long term solution.