@webority/repo-checks
v0.156.0
Published
Repository checks for Webority React web portals, React Native apps and Razor sites: banned and required dependencies, pnpm only, exact pins, the shared Biome preset, and token use in style, markup and component code. Run before a push; existing findings
Readme
@webority/repo-checks
Repository checks for Webority React web portals and React Native apps. One command reads every
app package.json in the git repository and reports what the house rules say it must not have,
must have, and how it must be pinned and installed.
It also counts raw measures, raw colours, root font sizes and palette reads in every tracked style, markup and script file of the repository (bootstrap-ui.md → Tokens).
Use
pnpm add -D --save-exact @webority/repo-checks
pnpm exec webority-repo-checks # every app in the repository
pnpm exec webority-repo-checks apps/portal # one app folder
pnpm exec webority-repo-checks --offline # no advisory audit, so no network
pnpm exec webority-repo-checks --build # quick rules only, for a build script
pnpm exec webority-repo-checks --skip-lint # leave out the Biome run, for a gate that already lintsA product's build script runs --build before its bundler, for example "build": "webority-repo-checks --build && vite build".
An app is a package.json whose dependencies hold react-native (mobile) or react-dom (web). A react-dom folder that also has react-native-web is a react-native-web demo: it gets the pnpm and Biome checks, not the portal rules. Biome and @webority/biome-config are read from the package.json beside the biome.json in effect (a nested config with "root": false defers to the one above), so a sub-folder linted by the root config does not repeat them. A workspace:, file: or link: reference is local and never counts as a lockstep version.
A package with agentation in its dependencies is a build tool, not an app. It and anything else (tooling roots, libraries) get only the lockfile check.
A mobile app with an android/ folder gets the Android checks, and a web app's src/ the import checks.
The missing-export:<file> check runs the Biome installed beside the app or in a folder above it, so it needs @biomejs/biome installed; without one it reports nothing, and the required:@biomejs/biome finding fails the app.
| Finding | What it means |
|---|---|
| banned:<name> | A dependency the rules retire or forbid (ESLint, Prettier, lint-staged, react-toastify, moment and the rest; the mobile list is the retired-libraries table) |
| required:<name> | A dependency every app carries (Biome and the shared preset, the core and UI packages, Bootstrap and react-bootstrap on web, Crashlytics on mobile) |
| not-exact:<name> | A ^ or ~ range. Web pins @webority/* and bootstrap exactly; a React Native app pins every dependency exactly |
| lockstep:<set> | Packages that publish together at one version are pinned at different versions |
| pnpm:* | A committed package-lock.json or yarn.lock, no pnpm-lock.yaml, no devEngines.packageManager of { "name": "pnpm", "version": "<x.y.z>", "onFail": "error" } (a plain npm install fails, and pnpm refuses any other version), a leftover packageManager field (pnpm ignores it beside devEngines), or a pnpm-workspace.yaml without engineStrict: true and strictPeerDependencies: true, or a pinned pnpm version that differs from the pnpm version in the machine prerequisites list (pnpm:version; skipped where the machine prerequisites list does not exist, and WEBORITY_PREREQUISITES points the check at another list) |
| biome:* | No Biome config, a config that does not extend @webority/biome-config/biome.jsonc, a formatter.lineEnding or root linter.rules, or (mobile) no __DEV__ global, either top-level or in an override whose includes cover the app folder |
| catalogue-copy:<file> | A .jsx or .tsx file anywhere under src/ of a web app, or of a workspace package that depends on @webority/ui-react, named like a @webority/ui-react export |
| android-release-signing | android/app/build.gradle has no guard that throws a GradleException when a requested task name contains release and a signing setting or the upload key is missing |
| android-edge-to-edge | android/gradle.properties sets edgeToEdgeEnabled=false |
| rn-mainactivity-oncreate-bundle:<file> | A MainActivity.kt or .java passes a bundle to super.onCreate; pass null |
| metro-bridge:<file> | A React Native app's entry file (package.json main, default index.js), root App.js or src/App.js nor any other source file of the app (outside node_modules, ios, android and build output) imports createMetroBridge from @webority/mobile-core/metro and assigns globalThis.__METRO_BRIDGE__ from a createMetroBridge(...) call inside if (__DEV__) in the same file; react-native-procedures.md → Debugging: the development bridge |
| endpoint-name:<name> | Two RTK Query endpoints in an app's src share a name (a name: build.query(, build.mutation( or build.infiniteQuery( key in a file that calls injectEndpoints or createApi); RTK Query keeps one, so the other screen calls the wrong URL. Warns until a release enforces it (REPO_CHECKS_ENFORCE=endpoint-name fails it now) |
| mobile-production-stamps:<file> | A mobile app's Android manifest or iOS Expo.plist points over-the-air updates at the Staging or Development feed, or android/gradle.properties carries a .staging or .development app id, left by mobile-tools prebuild, or a committed manifest or Expo.plist still points at the retired whole-number address ota/manifest/<n>/<n>/. Set the OTA public-id settings, then run mobile-tools prebuild --env Production before committing |
| spa-host-immutable-assets:<file> | A web app's web.config, staticwebapp.config.json, _headers or spa-host*.json sets Cache-Control with immutable |
| build-typecheck:<script> | A web portal's build or build:* script runs no tsc --noEmit and no typecheck script, itself or through the build scripts it calls; react.md → Language. Warns until a release enforces it |
| library-copy:<file>:<name> | A web portal's source file under src/ declares or exports, itself, a name @webority/web-core exports (its logger, error helpers, store set-up, reload helpers, refresh lock, JWT helpers, India validators); a re-export from the package passes. Warns until a release enforces it |
| mobile-library-pin:<name> | A React Native app pins a @webority/mobile-* package below this checker's own version, which is the current phone library version. Warns until a release enforces it |
| root-file:<name> | A web portal has no .nvmrc in its folder or above, lacks spa-host.json or a spa-host.<Environment>.json, has a JavaScript Vite config, has no codeSplitting groups, or sets build.target; react.md, as the starter does. Warns until a release enforces it |
| tracking-plan:<missing\|invalid\|extends> | A product repo (it has .github/workflows/production.yml) has no .webority/tracking-plan.json, one that is not a JSON object, or one whose extends is not @webority/analytics/fleet; product-analytics.md. Warns until a release enforces it |
| web-folder:<folder> | A web portal has src/components/base/ or a flat src/components/features/; react.md → Folder layout. Warns until a release enforces it |
| stale-theme-name:<file> | A web style or source file reads a pre-0.89.0 theme name as a whole token (var(--radius), --color-primary-ink). No alias ships for these. Run webority upgrade 0.89.0 --path <folder> once, then webority brand. --color-primary-text is not flagged. Never recorded in the baseline |
| lint:<file> | Biome's check, with warnings as errors, over the app folder: any error or warning. Never recorded in the baseline; fix the finding; --skip-lint leaves it out |
| web-import-axios:<file> | A web source file under src/ imports axios; HTTP goes through @webority/web-core |
| web-import-axios-types:<file> | A web source file imports types from axios with import type or export type; import AxiosError, AxiosInstance, AxiosRequestConfig and AxiosResponse from @webority/web-core. Warns until a release enforces it |
| web-import-router:<file> | A web source file imports BrowserRouter or Routes from react-router or react-router-dom; routes are objects for createBrowserRouter |
| web-import-env-branch:<file> | A web source file reads import.meta.env.DEV or import.meta.env.PROD; branch on AppSettings.ENVIRONMENT |
| missing-export:<file> | A file imports a name its module does not export; Biome's React false alarms, unresolved aliases and a name a relative module re-exports from a package are not reported |
| raw-measure:<file> | A px, rem or em literal, or a bare number for a weight or a leading, in padding, margin, gap, inset, a corner, font size, font weight or line height. Counted per file. |
| raw-colour:<file> | A hex, an rgb() or hsl() with numbers, or a CSS named colour in a colour property or style key, where a colour meaning belongs. Counted per file. |
| root-font-size:<file> | A font-size or font declared on html or :root. The type scale assumes the browser's root size. Counted per file. |
| palette-read:<file> | A read of a raw palette step (var(--wui-<step>), or a phone palette. read) or of a Bootstrap theme colour (var(--bs-primary), var(--bs-primary-rgb) or Sass $primary, for the eight theme colours), where a colour meaning belongs. The message names the meaning to read. Counted per file. |
| raw-motion:<file> | A non-zero time (150ms, .2s) or an easing (ease, linear, cubic-bezier(...), steps(...)) in transition, transition-duration, transition-timing-function, animation, animation-duration or animation-timing-function, where a motion token belongs. A var() read, a zero and a delay are not counted. Counted per file. |
| dark-read:<file> | A read of the private dark tier (var(--wui-dark-primary) and the other --wui-dark-* steps), which stays dark in light mode. The message names the --color-* meaning to read. It replaces the palette-read count for those reads. Counted per file. |
| missing-canvas-key:<file> | A list item with no data-canvas key on web or testID on a phone, and no spread: the first JSX tag (past a Fragment) that an expression the .map( callback itself returns holds outside any object literal, in an expression body or any return of a block body. An object holding an element is data, a nested function's returns are its own, a block with no returned element draws no row, a React Navigation .Screen or .Group configures a navigator, and a component whose root sets the key (in the same file or imported) is keyed by itself. A map given a named function has its whole argument read. Counted per file. |
| repeated-canvas-key:<file> | A quoted data-canvas or testID value that a second tag in the same module repeats. Counted per file. |
| raw-bootstrap:<file> | A .jsx, .tsx or .cshtml file writes a raw Bootstrap component class (btn, card, modal, table, badge, alert, form-control, form-select, form-check-input, pagination, nav-tabs, dropdown-menu, offcanvas, breadcrumb, progress, spinner-border, accordion); the message names the library component (badge names AppBadge / <app-badge>, and AppStatusBadge / <app-status-badge> for a badge that shows a status). A modifier such as btn-primary alone is not counted. Counted per file. |
| library-class-restyle:<file> | A product .scss or .css selector names a class the library theme defines (a wui-* component class, an app-* shell, navigation or tab class, or a Bootstrap component class the theme skins: btn, card, modal, table, form-control, form-select, form-check, form-label, form-text, badge, alert, dropdown, nav-tabs, nav-pills, nav-link, pagination, page-item, page-link, offcanvas, accordion, breadcrumb, progress, spinner-border, list-group, input-group, each also with a -suffix), including as an ancestor (.my-page .wui-card-body, .app-shell.collapsed .app-sidebar .brand-logo); use a library prop or token, or a product class. A token file that only sets custom properties names no class and is not counted. Counted per file. |
| shadowed-library-name:<file> | A .jsx or .tsx file imports a catalogue component from @webority/ui-react under another name and declares a component under the catalogue name (import { AppButton as LibraryAppButton } with function AppButton), so its call sites read as the library component. Use the library component and its props, or give the product component its own name. Counted per file. |
| rename-wrapper:<file> | A re-export of @webority/ui-react, a component whose body only returns one catalogue component, or a _Name.cshtml partial that is one <app-*> element: import the library name. Counted per file. |
| single-use-component:<file> | A file under src/components/<feature>/ (not layout/), or a shared partial under Pages/Shared/ or a *.Web.Common project, that fewer than two files use; move it next to the page that uses it. A layout is skipped: a _…Layout.cshtml file, or one that a _ViewStart.cshtml of the same project names with Layout = "<name>", because areas find it by that name. A Razor file whose first line is @* layering-allow single-use-component: <reason> *@ is skipped too; a mark with no reason is a finding. Counted per file. |
| error-summary-title:<file> | A .cshtml <app-error-summary> sets a title with the old generic wording ("Please fix the following", "There were errors"); drop it, the library writes the standard heading. A computed title or one naming the failed action is not counted. Counted per file. |
| route-internal-id:<file> | A route binds a record's internal number: a .cshtml @page or a C# [Route], [Http*] or Map* template whose id or ...Id parameter is constrained int or long, or an asp-route-* link that passes a record's .Id. Route by the public id ({publicId:guid}) instead. An untyped {id} is not counted. Counted per file. |
| razor-page-handler:<file> | A Razor page names a handler its page model does not declare: a literal asp-page-handler, a ?handler= in a literal href, action, formaction or inline script string, or the handler argument of Url.Page, RedirectToPage or RedirectToPagePermanent (read like RedirectToPage). A handler X matches On<Verb>X or On<Verb>XAsync on the model or a base class in the repo. A ?handler= is checked only in an address with no path before the ?; one naming another path is counted as not checked. A page whose model derives from a package class is not checked. Counted per file. |
| razor-page-link:<file> | A literal asp-page, or the page argument of Url.Page, RedirectToPage or RedirectToPagePermanent, names no page in the project, the projects it references, or a host project that references it, resolved as ASP.NET Core does (absolute within the area, relative to the linking page's folder, asp-area="" for the root). A link into an area no folder in the repo holds is a package's page and is not counted. Counted per file. What the check cannot read (a dynamic asp-page, a page name in a variable, a constant C# member lookup cannot pin to one value, a model outside the repo) is not a finding; the run prints how many links it skipped and why. |
| web-route-literal:<file> | A router link or navigate call in a portal with a literal path instead of href(...) from the route table, so a link to a route that does not exist fails the type check. A template that starts with a substitution is not counted. Counted per file. |
| api-thunk:<file> | A call of createAsyncThunk, or of a name the file binds to createAsyncThunk.withTypes, in an app's source; an API call moves to an RTK Query endpoint in services/api (react.md: API data). An app's session module, src/redux/action/accountAction on a phone or web head, is not counted: its sign-in, token refresh, session restore and sign-out thunks are session flows, not API reads (react.md, react-native.md). Counted per file. |
| screen-service-import:<file> | A file under src/screens/ imports from its head's services/ folder other than the RTK Query api module (services/api or services/api/*); a screen reads data through that module. Counted per file. |
| api-call:<file> | A hand-written URL in an app's src: a .get(, .post(, .put(, .patch( or .delete( call whose first argument is a string or template starting with / or api/, or a function in a services folder declared to return Promise<unknown>. The reply type and the URL come from the generated contract: call the operation through api("<OperationId>", request) from src/services/api/api.ts. Counted per file. Warns until a release enforces it (REPO_CHECKS_ENFORCE=api-call fails it now). |
| oversize:<file> | A .jsx, .tsx or .cshtml file over 600 lines (over 800 says to split it along its concerns). A warning only: it never fails, even outside the baseline. Counted per file. |
| hook:drift | The root .husky/pre-commit is missing or differs from the shared hook this package ships (CRLF is ignored) |
| tracked-case:<path> | A tracked file or folder whose name on disk differs only by letter case, a rename done by hand on a Mac that git never saw; a Linux checkout cannot find the import. Run the git mv the message gives. A folder is reported once. Always fails. |
| advisory:<package>:<id> | pnpm audit lists a known advisory for the lockfile that governs an app. A high or critical advisory fails; any other severity warns. Reported on the folder of that pnpm-lock.yaml |
| advisory:unreachable | pnpm audit could not reach the advisory service. It fails: check the network and run again, or pass --offline |
| advisory:unreadable | pnpm audit returned a report the check cannot read. It fails |
Comment lines (//, /*, *) and .test. or .spec. files are not read by the import checks.
Where each rule runs
| Rule | What it checks | --build (product build) | Full run (push) |
|---|---|---|---|
| banned, required, not-exact, lockstep | Retired or forbidden packages, required packages, exact pins and lockstep versions | yes | yes |
| pnpm:* | The lockfile, manifest and workspace rules, including a committed npm or yarn lockfile | yes | yes |
| biome:* | The Biome config: the shared preset, line endings, root rules and the __DEV__ global | yes | yes |
| hook:drift | The root pre-commit hook matches the shared one | yes | yes |
| tracked-case | A tracked name that differs only by letter case | yes | yes |
| catalogue-copy | A web source file named like a @webority/ui-react export | yes | yes |
| android-release-signing, android-edge-to-edge | Android Gradle release signing and edge-to-edge settings | yes | yes |
| rn-mainactivity-oncreate-bundle, mobile-production-stamps, spa-host-immutable-assets | Native entry points, production update stamps and immutable host caching | yes | yes |
| folder-layout, cross-feature-import, import-into-screens, screen-imports-screen, shared-single-importer, library-wrapper, screen-length, mobile-pins-lockstep, local-theme, width-scaling, raw-primitive | Phone app layering: folder layout, import direction, screen size and library use | yes | yes |
| metro-bridge | The development bridge is wired in a React Native app | yes | yes |
| web-import-axios, web-import-axios-types, web-import-router, web-import-env-branch | Web imports that bypass the shared packages or the app settings | yes | yes |
| endpoint-name | Two RTK Query endpoints share a name | yes | yes |
| build-typecheck, library-copy, mobile-library-pin, root-file, web-folder | Portal build script, library copies, phone pins, root files and folder layout (warn first) | yes | yes |
| tracking-plan | A product repo keeps a tracking plan that extends the shared plan (warn first) | yes | yes |
| lint, missing-export | Biome over every file: lint findings, and imports a module does not export | no | yes |
| stale-theme-name | Pre-0.89.0 theme names read as a whole token | no | yes |
| advisory | Known advisories from pnpm audit (needs the network) | no | yes |
| raw-measure, palette-read, raw-colour, root-font-size, raw-motion, dark-read | Counted style rules: raw sizes, colours and motion, palette reads and the root font size | no | yes |
| missing-canvas-key, repeated-canvas-key | List items without a key, and repeated keys | no | yes |
| raw-bootstrap, library-class-restyle, rename-wrapper, shadowed-library-name, single-use-component | Raw Bootstrap classes, product CSS restyling the library, wrappers, shadowed names and single-use components | no | yes |
| error-summary-title, route-internal-id, oversize | Error summary titles, internal ids in routes and file size | no | yes |
| api-thunk, screen-service-import, api-call | Hand-written API thunks, screens that import services other than the RTK Query api module, and hand-written API URLs | no | yes |
| razor-page-handler, razor-page-link, web-route-literal | Razor handlers and page links that name nothing, and portal links written as literal paths | no | yes |
--build skips the rules in BUILD_SKIPPED_RULES (exported from src/check.ts) because they start Biome, read every file's content or call the network. It prints no stale or fallen baseline lines, and it combines with --json, --offline and folder arguments, but not with the --baseline-* flags or --sync-hook.
Build mode
A portal's build script runs pnpm exec webority-repo-checks --build as its first step, so every build fails on a new finding (exit 1). Build mode skips these 26 rules: lint, missing-export, stale-theme-name, advisory, raw-measure, palette-read, raw-colour, root-font-size, raw-motion, dark-read, missing-canvas-key, repeated-canvas-key, raw-bootstrap, library-class-restyle, rename-wrapper, shadowed-library-name, single-use-component, error-summary-title, route-internal-id, razor-page-handler, razor-page-link, web-route-literal, api-thunk, screen-service-import, api-call and oversize. Those rules run only in a full run (pnpm exec webority-repo-checks with no --build), which the push check still runs. Any rule not named here runs in both.
--skip-lint skips only the lint rule; it combines with --json, --offline and folder arguments, and with --build, but not with the --baseline-* flags or --sync-hook.
--build cannot be combined with --baseline-init, --baseline-shrink, --baseline-accept, --baseline-adopt or --sync-hook; with one of them it prints a usage message and exits 2. The exit codes are 0 for nothing new, 1 for a new finding and 2 for a usage or setup error.
Token rules: counted per file
The four rules read every tracked .scss, .css, .cshtml, .jsx and .tsx file, and each .js or
.ts file that imports react-native. They skip a path with a node_modules, dist, generated,
vendor, __tests__ or __mocks__ segment, an email template project folder (<Product>.Email or
<Product>.Emails: email clients read no CSS variables, so inline pixels and colours are required there;
a plain Email folder of admin screens stays checked), a path containing wwwroot/lib/, a .generated. name,
a .min.css name, a .test. or .spec. script, and any path the repo lists under generated[].paths in
.webority/map.json (a trailing / names a folder; * matches within one segment). List a compiled
stylesheet there: it bundles the shared theme, so its library rules are not the product's code, and the
rules still read the SCSS it is built from. They also skip a path the repo's root .gitattributes marks linguist-generated (for example Webority.Canvas.Kits/** linguist-generated; a later -linguist-generated line for a narrower path takes it back, as in Git; nested .gitattributes files are not read). They also skip any path the repo lists under fixtures in the
same map, with the same pattern syntax ("fixtures": ["corpus/"]): a test fixture that imitates captured
device output holds literal colours and sizes on purpose.
raw-measure counts a non-zero px, rem or em literal, or a bare number for a weight or a leading, in
padding, margin, gap, inset, a corner, font size, font weight or line height: one finding per
declaration or style key. A zero, a keyword (auto, bold, normal), a token
(var(--wui-space-md)), a percentage or another unit, and a custom property or Sass variable
declaration are not counted.
raw-colour counts a hex, an rgb(), rgba(), hsl() or hsla() with numeric arguments, or a CSS
named colour, in a colour property or style key (including a fallback inside var(--color-x, #fff)).
transparent, currentColor, a meaning read, a custom property or Sass variable declaration, and
text inside a url() or a quoted string are not counted.
root-font-size counts every font-size or font declaration in a rule whose selector is html or
:root (with or without an attribute, class or pseudo-class), whatever its value.
palette-read counts each var(--wui-<step>) and each phone palette. read, plus each
var(--bs-<colour>), var(--bs-<colour>-rgb) and Sass $<colour> for Bootstrap's eight theme
colours: one finding per read, and a declaration is not a read.
raw-motion counts a non-zero time or an easing in the six properties transition, transition-duration,
transition-timing-function, animation, animation-duration and animation-timing-function. Motion comes
from the tokens --wui-duration-fast, --wui-duration-default, --wui-duration-slow and --wui-ease-out,
--wui-ease-in-out, --wui-ease-ui, --wui-ease-elastic and --wui-ease-linear; a delay and a zero are not counted.
dark-read counts each read of the private dark tier (var(--wui-dark-*)). A dark band is a scope with
data-theme="dark", never a direct read, so a read of the dark tier stays dark in light mode.
missing-canvas-key and repeated-canvas-key read every script module that imports react-native,
@webority/mobile-ui or @webority/ui-react. The attribute is testID in a module that imports
react-native or @webority/mobile-ui, and data-canvas in any other. A spread on the item counts as
keyed, and a repeat adds one finding per tag after the first.
src/assets/styles/_home.scss:12: color: var(--wui-primary);
(read the meaning: var(--color-primary))New rules start as warnings
lint, rn-mainactivity-oncreate-bundle, spa-host-immutable-assets, oversize, advisory, endpoint-name, api-call, api-thunk, screen-service-import, build-typecheck, library-copy, mobile-library-pin, root-file, web-folder, web-import-axios-types and tracking-plan print as warnings and do not fail until
REPO_CHECKS_ENFORCE names them: REPO_CHECKS_ENFORCE=lint,rn-mainactivity-oncreate-bundle, or all. A release
removes a rule from the warning list when no repository has a hit. --json carries the warnings under warnings.
For advisory the severity decides: a high or critical advisory fails, and any other severity warns, whatever REPO_CHECKS_ENFORCE names.
The advisory audit
Each run audits every distinct pnpm-lock.yaml that governs a checked app, once per lockfile, with pnpm audit --json from that lockfile's folder. The audit has a 60-second timeout and needs the network. An advisory accepted by the owner is recorded with --baseline-accept <lockfile folder> --reason <text>, like any other finding. --offline skips the audit and prints repo-checks: advisory rule skipped (--offline). to stderr, for a machine without the network.
Existing findings: a baseline that only shrinks
A repository adopting the checks records what it has today, once:
pnpm exec webority-repo-checks --baseline-initThat writes .webority/baselines/repo-checks-baseline.json, creating the folder: the dependency findings as
"<app folder> | <id>", and the code rules counted per file:
{
"version": 2,
"findings": ["apps/portal | banned:@webority/ui-elements", "samples/react-sandbox | required:@webority/web-core"],
"counts": { "palette-read:packages/theme/scss/wui/_buttons.scss": 3, "raw-measure:src/assets/styles/_home.scss": 14 }
}From then on a finding in the baseline is reported as known and does not fail; any other finding
fails (exit 1). A count may only fall: a file above its recorded count fails and prints its lines,
and a file missing from the baseline is at 0, so a new or renamed file starts at 0. After fixing old
findings or lowering counts, --baseline-shrink drops the fixed findings and lowers each count to
its current value, deleting one that reaches 0; it never adds or raises one.
--baseline-accept <folder> --reason <text> records debt the owner has accepted: every current finding and count under that folder is added, and a count is raised to its current value, never lowered. Use it only for debt the owner has accepted, and give a reason that names the decision; the reason is kept in accepted in the baseline file, and --baseline-shrink keeps that entry.
--baseline-adopt <rule,...> records today's findings and counts for rules the baseline has never recorded, such as a rule a new release adds. It refuses a rule the baseline already records, so it never raises an existing entry, and names how many findings and counts it adopted per rule. A release that adds raw-motion and dark-read is adopted once with --baseline-adopt raw-motion,dark-read; then --baseline-shrink lowers the palette-read counts the dark reads leave.
--baseline-init runs once: it refuses an existing file and folder arguments. When the file is the
earlier shape (no version), delete it and run --baseline-init once from the git root, then review
the diff before committing.
--json prints { root, new, known, stale } for tools; a count above its record appears in new
with its lines.
The shared pre-commit hook
Every library and tool repository runs the pre-commit hook this package ships: the ones whose
.webority/settings.json sets kind to library or tool, or that have no settings file. Any other
kind (a product, a website, a mobile app) carries no hook, and hook:drift stays quiet there. Adopt it
once, from the git root:
pnpm exec webority-repo-checks --sync-hookThat writes .husky/pre-commit, creating .husky when it is missing, with LF line endings. A
multi-app repository installs it once, at the root. husky stays the installer, through the
prepare script. Remove lint-staged (pnpm remove lint-staged, and delete its config key): the
shared hook runs Biome itself. Put repo-only steps in .husky/pre-commit.local; the shared hook
runs that file after Biome, and --sync-hook never touches it.
hook:drift keeps the root copy equal to the shipped one.
Versioning
Published from the webority-ui monorepo in lockstep with the other @webority/* packages. Pin
an exact version; a new rule arrives when you bump it.
