House rules: numbers through one formatter, no made-up server errors, and the rules fail the build
Build / Build (push) Successful in 33s
Build / Build (push) Successful in 33s
- Every number shown on a page goes through NumbersExtensions: counts, percentages, the nerd readouts and the values written into a style. - A failure the client builds itself (offline, timeout, retries run out, an answer it can't read) is a 400 that names its kind; only a server's own 5XX passes through. - Login, sign-up, recovery and password confirmations leave autocomplete to the browser, so it can offer the saved login. - The Daisy components start with empty lists and attributes, and a decorative image still renders alt="". - Arasaka.HouseRules.Analyzers checks all of this in every build, together with PrivaPub.ClientModels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W4YtEJrgxG12Pc39Fai8Bd
This commit is contained in:
1 parent
697b2c8897
commit
3827d903a3
37 files changed
+165
-111
No files matched your search
@@ -849,22 +849,24 @@ in both builds, kept out of the precache; see **Web Push**): `push` shows the no
|
||||
the app on that persona's notifications, `pushsubscriptionchange` subscribes again with the same key. They control no
|
||||
page and have no fetch handler.
|
||||
|
||||
## Development rules
|
||||
## House rules
|
||||
|
||||
These rules are adapted from the owner's b2b.next project. They are enforced by review: the build doesn't check them,
|
||||
so follow them in every change.
|
||||
The owner's rules for every .NET project (`~/RiderProjects/HouseRules/RULES.md`, repo `Arasaka/HouseRules`) apply
|
||||
here and win over anything else in this file. `Arasaka.HouseRules.Analyzers` (`Directory.Build.props`) reports the
|
||||
ones with an HR id as errors in every build and in Rider, here and in `PrivaPub.ClientModels`; the rest are reviewed.
|
||||
|
||||
| Area | Rule |
|
||||
|---|---|
|
||||
| All | `var` everywhere; uninitialised locals are `var x = default(T);` |
|
||||
| All | Primary constructors for DI (`class Foo(IBar bar)`), not constructor + fields |
|
||||
| All | Every async method takes a `CancellationToken` as its last parameter |
|
||||
| All | `var` everywhere; uninitialised locals are `var x = default(T);` (HR0001) |
|
||||
| All | Primary constructors for DI (`class Foo(IBar bar)`), not constructor + fields (HR0004) |
|
||||
| All | Every async method takes a `CancellationToken` as its last parameter (HR0003) |
|
||||
| All | Early return on error: guard, handle, `return`; never if/else for success vs failure |
|
||||
| Models / state | Lists and arrays `= []`, never null; no `= string.Empty` / `""` defaults; explicit enum defaults |
|
||||
| Components | No `@{ }` blocks in markup: locals become `@code` members |
|
||||
| Components | `[Inject]` in `@code`, never `@inject` |
|
||||
| Components | Disable controls with `IsDefaultDisabled` (plus local conditions), never bare offline state |
|
||||
| Components | Every text/number input declares `autocomplete` |
|
||||
| Models / state | Lists and arrays `= []`, never null (HR0006); no `= string.Empty` / `""` defaults (HR0005); explicit enum defaults |
|
||||
| Components | No `@{ }` blocks in markup: locals become `@code` members (HR0102) |
|
||||
| Components | `[Inject]` in `@code`, never `@inject` (HR0101) |
|
||||
| Components | Disable controls with `IsDefaultDisabled` (plus local conditions), never bare offline state (HR0104) |
|
||||
| Components | Every text/number input declares `autocomplete`, except credentials (HR0103, HR0105) |
|
||||
| Services | A failure the client builds itself is a 400 that names its kind in `ErrorCode` (`FailureCodes`), never a 5XX; only a server's own 5XX passes through (HR0007) |
|
||||
| Services | `Task<WebResult>` for mutations, `Task<List<T>>` (`[]` on error) for lists, `Task<T>` with a default for values |
|
||||
| Localization | Every user-facing string through `Localizer`, its key in **both** `AllStrings.resx` and `AllStrings.it.resx` |
|
||||
|
||||
@@ -881,7 +883,7 @@ List<FeedPost> posts = []; // DO
|
||||
@(posts?.Count ?? 0) // DON'T
|
||||
```
|
||||
|
||||
`= ""` is allowed only as a documented exception, with the reason in a comment (e.g. a decorative image's `Alt`).
|
||||
A string that must render as `""` coalesces in the markup instead: a decorative image is `alt="@(Alt ?? "")"`.
|
||||
|
||||
**Pages that load data.** Pages fetch in `LoadData` and keep lifecycle concerns in the lifecycle method:
|
||||
|
||||
@@ -930,22 +932,27 @@ join a list into one string with `<br/>` or `\n`.
|
||||
|
||||
**Autocomplete values:**
|
||||
- `autocomplete="off"` for search, filter and free text;
|
||||
- `new-password` when setting a password;
|
||||
- `new-password` when setting a password; `off` on a password field is wrong, since it doesn't stop saved passwords
|
||||
(HR0105);
|
||||
- `one-time-code` for OTP (DOtp sets it);
|
||||
- `username` and `current-password` on the login form, so password managers fill it;
|
||||
- omitted on login credentials, contact e-mails and password confirmations, so the browser can help: the files are
|
||||
listed in `HouseRulesAutocompleteOmit` (Login, Join, Setup, PasswordRecovery, AccountEditor, TotpEditor, Groups,
|
||||
ActDialog, BackupsPanel);
|
||||
- inside `Components/Daisy`, attributes pass through from the caller.
|
||||
|
||||
**Localization:**
|
||||
- Keys are English source strings. Grep both resx files for an existing key before adding one; match keys exactly.
|
||||
- Never add a key that differs from an existing one only by case ("users" next to "Users"): resource lookups collide
|
||||
on them and one of the two silently shows the other's text. Reuse the existing key.
|
||||
- Use `Localizer[key, args]`, never `string.Format(Localizer[key], …)`. Don't write `.Value`.
|
||||
- Use `Localizer[key, args]`, never `string.Format(Localizer[key], …)`. Don't write `.Value` (HR0009).
|
||||
- Enum members shown through `Localizer[value.ToString()]` are keys too: when you add a member, add its key.
|
||||
- Language names in the language picker stay in their own language.
|
||||
|
||||
**Formatting.** Date formats come from `SUtility.dateTimeFormat` / `timeFormat` / `dateTimeFormatExt`. Relative times
|
||||
and sizes come from `GetPassedTime` / `GetFileSize` (`Extensions/GenericExtensions.cs`). No inline format strings for
|
||||
display.
|
||||
**Formatting.** Numbers go through `NumbersExtensions` (`Extensions/NumbersExtensions.cs`, HR0011): `ToDefaultCount`,
|
||||
`Compact` (999, 1.2k), `ToDefaultPercent`, `ToWholePercent`, `ToSignedPercent`, `ToReadout` (nerd tips), `ToFixed`,
|
||||
and `ToCssNumber` for values written into a style. Date formats come from `SUtility.dateTimeFormat` / `timeFormat` /
|
||||
`dateTimeFormatExt`. Relative times and sizes come from `GetPassedTime` / `GetFileSize`
|
||||
(`Extensions/GenericExtensions.cs`). No inline format strings for display.
|
||||
|
||||
**JSON.** Register every `/clientapi` or localStorage type in `ClientJsonContext.cs` (camelCase: `theme.js` reads
|
||||
`pageSettings` straight from localStorage), every Mastodon API type in `MastodonJsonContext.cs` (snake_case).
|
||||
|
||||
Reference in new issue
Block a user