Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/Butil/Bit.Butil.Build/ButilScriptBundler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,42 @@ public static SortedSet<string> ResolveNames(IEnumerable<string> names, ButilScr
return modules;
}

/// <summary>
/// The names in a consumer's list that name one module while a Bit.Butil class of the same name
/// needs more: <c>crypto</c> keeps the hashing module alone, and <c>Crypto</c> the class also calls
/// four others. Each result carries the class and the modules naming it would add.
/// </summary>
/// <remarks>
/// A module name meaning that one module is deliberate - it is the finer control a split family
/// offers - and a consumer who wrote <c>crypto</c> before the family was split is exactly who would
/// not know the name stopped covering it. The publish reports these so that consumer finds out in
/// the build output rather than in a browser.
/// </remarks>
/// <param name="names">What the consumer wrote.</param>
/// <param name="manifest">The module manifest.</param>
/// <param name="types">The type map; without it nothing can be said, and the result is empty.</param>
public static IReadOnlyList<(string Name, string ClassName, IReadOnlyList<string> Beyond)> NarrowerThanClass(
IEnumerable<string> names, ButilScriptManifest manifest, ButilTypeModules? types)
{
var hints = new List<(string, string, IReadOnlyList<string>)>();
if (types is null) return hints;

foreach (var raw in names)
{
var name = (raw ?? string.Empty).Trim();
if (manifest.Dependencies.ContainsKey(name) is false) continue;

var type = types.FullTypeNames.FirstOrDefault(candidate =>
string.Equals(candidate.Substring(candidate.LastIndexOf('.') + 1), name, StringComparison.OrdinalIgnoreCase));
if (type is null) continue;

var beyond = types.ForFullName(type).Where(module => module != name).OrderBy(module => module, StringComparer.Ordinal).ToArray();
if (beyond.Length > 0) hints.Add((name, type.Substring(type.LastIndexOf('.') + 1), beyond));
}

return hints;
}

/// <summary>
/// Concatenates the chunks of the given modules, in the given order, into a bundle. Chunk files are
/// <c>&lt;chunksDirectory&gt;/&lt;module&gt;.js</c>.
Expand Down
13 changes: 12 additions & 1 deletion src/Butil/Bit.Butil.Build/TrimButilScripts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,24 @@ public override bool Execute()
// way none of the above can see - through reflection, or from JavaScript of its own.
if (explicitNames.Length > 0)
{
var chosen = ButilScriptBundler.ResolveNames(explicitNames, manifest, Types(), out var unresolved);
var typeMap = Types();
var chosen = ButilScriptBundler.ResolveNames(explicitNames, manifest, typeMap, out var unresolved);
if (unresolved.Count > 0)
{
Log.LogError($"Bit.Butil: <BitButilScriptModule> names {string.Join(", ", unresolved.Select(name => $"'{name}'"))}, which is neither a JavaScript module nor a Bit.Butil class. The modules are: {string.Join(", ", manifest.Order)}.");
return false;
}

// A module name keeps that module alone, by design - but a name that used to cover a whole
// family before it was split (crypto, css, window) now quietly keeps a fraction of what the
// class of the same name calls. Said at normal verbosity, so the consumer who meant the
// class learns it from the build rather than from a "BitButil.cryptoSign is undefined".
foreach (var (name, className, beyond) in ButilScriptBundler.NarrowerThanClass(explicitNames, manifest, typeMap))
{
Log.LogMessage(MessageImportance.High,
$"Bit.Butil: <BitButilScriptModule Include=\"{name}\" /> keeps only that module; the {className} class also calls {string.Join(", ", beyond)}. Name the class instead if the app reaches those too.");
}

referenced.UnionWith(chosen);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Butil/Bit.Butil.Demo/Client/Docs/DocsNav.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static class DocsNav
new("Navigation", "navigation", "The modern successor to History - read the entry list, and finally know whether you can go back.", typeof(NavigationPage)),
new("Location", "location", "Read and mutate the current URL, reload or navigate.", typeof(LocationPage)),
new("Navigator", "navigator", "Browser identity, languages, share, vibrate, badges, protocol handlers and more.", typeof(NavigatorPage), ApiSupport.Partial, ApiNeeds.SecureContext | ApiNeeds.UserGesture),
new("UserAgent", "user-agent", "Parsed user-agent brands, platform and mobile-ness.", typeof(UserAgentPage), ApiSupport.Partial),
new("UserAgent", "user-agent", "Parse any user-agent string - browser, engine, OS, device - plus the Client Hints brands and mobile-ness.", typeof(UserAgentPage), ApiSupport.Partial),
new("TextFragment", "text-fragment", "Scroll-to-text URLs: deep-link to a phrase rather than to an anchor.", typeof(TextFragmentPage), ApiSupport.Partial),
new("Url", "url", "Parse and edit URLs the way the browser does - and match routes with URLPattern.", typeof(UrlPage), ApiSupport.Partial),
new("Speculation", "speculation", "Prefetch and prerender rules, and knowing whether anyone is looking at this page yet.", typeof(SpeculationPage), ApiSupport.Chromium),
Expand Down
14 changes: 10 additions & 4 deletions src/Butil/Bit.Butil.Demo/Client/Layout/AppHeader.razor
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
@inject ThemeService theme
@inject ThemeService theme

@* Fluent 2's command bar: one 56px band, an identity block at the start, the utilities at the end,
and everything in it a 32px control on the 4px grid. The bar is the only chrome that is on every
page - the nav panel is not on the home page - so anything a reader must be able to reach from
anywhere lives here: search, the two outbound links, and the theme. *@
anywhere lives here: search, the two outbound links, and the theme. Search is the one thing the
home page takes back, because its hero shows the same box larger. *@

<header class="header">
<div class="container@(IsHomePage ? " home-page" : "")">
Expand Down Expand Up @@ -45,8 +46,13 @@
}

@* In the bar rather than inside the nav panel, which only exists on documentation
pages: search has to work from the home page too. *@
<SearchBox />
pages: search has to work from every page that has one. The home page is the
exception - it puts the same box in its hero, where it is the thing being offered
rather than a utility, so the bar would only be showing it twice. *@
@if (IsHomePage is false)
{
<SearchBox />
}

<a class="icon-btn" target="_blank" rel="noopener noreferrer"
title="Bit.Butil on NuGet" aria-label="Bit.Butil on NuGet (opens in a new tab)"
Expand Down
Loading
Loading