-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add support for updating scoped css files #16507
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,12 +55,15 @@ setTimeout(function () { | |
} | ||
} | ||
|
||
function updateCssByPath(path) { | ||
async function updateCssByPath(path) { | ||
const styleElement = document.querySelector(`link[href^="${path}"]`) || | ||
document.querySelector(`link[href^="${document.baseURI}${path}"]`); | ||
|
||
// Receive a Clear-site-data header. | ||
await fetch('/_framework/clear-browser-cache'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Scoped css files can contain links to other css files. We'll try clearing out the http caches to force the browser to re-download. |
||
|
||
if (!styleElement || !styleElement.parentNode) { | ||
console.debug('Unable to find a stylesheet to update. Updating all css.'); | ||
console.debug('Unable to find a stylesheet to update. Updating all local css files.'); | ||
updateAllLocalCss(); | ||
} | ||
|
||
|
@@ -74,7 +77,7 @@ setTimeout(function () { | |
} | ||
|
||
function updateCssElement(styleElement) { | ||
if (styleElement.loading) { | ||
if (!styleElement || styleElement.loading) { | ||
// A file change notification may be triggered for the same file before the browser | ||
// finishes processing a previous update. In this case, it's easiest to ignore later updates | ||
return; | ||
|
@@ -94,12 +97,6 @@ setTimeout(function () { | |
styleElement.parentNode.insertBefore(newElement, styleElement.nextSibling); | ||
} | ||
|
||
function updateScopedCss() { | ||
pranavkm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
[...document.querySelectorAll('link')] | ||
.filter(l => l.baseURI === document.baseURI && l.href && l.href.indexOf('.styles.css') !== -1) | ||
.forEach(e => updateCssElement(e)); | ||
} | ||
|
||
function applyBlazorDeltas(deltas) { | ||
deltas.forEach(d => window.Blazor._internal.applyHotReload(d.moduleId, d.metadataDelta, d.ilDelta)); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.IO; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.DotNet.Cli.Utils; | ||
using Microsoft.DotNet.Watcher.Internal; | ||
|
||
namespace Microsoft.DotNet.Watcher.Tools | ||
{ | ||
internal sealed class ScopedCssFileHandler | ||
{ | ||
private static readonly string _muxerPath = new Muxer().MuxerPath; | ||
private readonly ProcessRunner _processRunner; | ||
private readonly Extensions.Tools.Internal.IReporter _reporter; | ||
|
||
public ScopedCssFileHandler(ProcessRunner processRunner, Extensions.Tools.Internal.IReporter reporter) | ||
{ | ||
_processRunner = processRunner; | ||
_reporter = reporter; | ||
} | ||
|
||
public async ValueTask<bool> TryHandleFileChange(DotNetWatchContext context, FileItem file, CancellationToken cancellationToken) | ||
{ | ||
if (!file.FilePath.EndsWith(".razor.css", StringComparison.Ordinal) && | ||
!file.FilePath.EndsWith(".cshtml.css", StringComparison.Ordinal)) | ||
{ | ||
return default; | ||
} | ||
|
||
_reporter.Verbose($"Handling file change event for scoped css file {file.FilePath}."); | ||
if (!await RebuildScopedCss(file.ProjectPath, cancellationToken)) | ||
{ | ||
return false; | ||
} | ||
await HandleBrowserRefresh(context.BrowserRefreshServer, file, cancellationToken); | ||
return true; | ||
} | ||
|
||
private async ValueTask<bool> RebuildScopedCss(string projectPath, CancellationToken cancellationToken) | ||
{ | ||
var build = new ProcessSpec | ||
{ | ||
Executable = _muxerPath, | ||
Arguments = new[] { "msbuild", "/nologo", "/t:_PrepareForScopedCss", projectPath, } | ||
}; | ||
|
||
var result = await _processRunner.RunAsync(build, cancellationToken); | ||
return result == 0; | ||
} | ||
|
||
private static async Task HandleBrowserRefresh(BrowserRefreshServer browserRefreshServer, FileItem fileItem, CancellationToken cancellationToken) | ||
{ | ||
// We'd like an accurate scoped css path, but this needs a lot of work to wire-up now. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does moving the CSS generation to source generators make things any easier? I'm not super familiar with the EnC/source generators side of the house but wonder if that helps here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would remove the need to run msbuild and make it faster. But we would still need to know what the path of the css file would be in markup. It's a bit tricky because it's relative to the wwwroot of the project being served. For instance, in a hosted app, the file turns out to be |
||
// We'll handle this as part of https://github.com/dotnet/aspnetcore/issues/31217. | ||
// For now, we'll make it look like some css file which would cause JS to update a | ||
// single file if it's from the current project, or all locally hosted css files if it's a file from | ||
// referenced project. | ||
var cssFilePath = Path.GetFileNameWithoutExtension(fileItem.ProjectPath) + ".css"; | ||
var message = new UpdateStaticFileMessage { Path = cssFilePath }; | ||
await browserRefreshServer.SendJsonSerlialized(message, cancellationToken); | ||
} | ||
|
||
private readonly struct UpdateStaticFileMessage | ||
{ | ||
public string Type => "UpdateStaticFile"; | ||
|
||
public string Path { get; init; } | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume this behavior will be sticking around for a while. If so, might help to add a comment on why we do this here (AKA move the comment below into the code).