@brandlab-digital/eslint-plugin
Proprietary software © Brandlab Digital Marketing. All rights reserved. Published for Brandlab client sites.
Fast, syntax-level safeguards for Brandlab Next.js consumers. The plugin gives immediate editor and CI feedback for violations that can be established from a single source file.
It deliberately does not implement release support, migration policy, project health, rendered-output validation or portfolio scoring. Those decisions belong to @brandlab-digital/conformance.
Client preset
Every Brandlab Next.js client uses the complete preset as its whole eslint.config.mjs:
import brandlab from '@brandlab-digital/eslint-plugin'
export default brandlab.configs.next
configs.next is Next's core-web-vitals and typescript configs, then configs.recommended, then react/no-unescaped-entities off (apostrophes are ordinary copy) and the standard ignores (.next/, out/, build/, coverage/, playwright-report/, test-results/, .vercel/, public/, next-env.d.ts). It loads eslint-config-next when first used, so install eslint-config-next at the exact version of next.
Recommended flat config
For a non-Next consumer, or to compose a custom array:
import brandlab from '@brandlab-digital/eslint-plugin'
export default [brandlab.configs.recommended]
The recommended config enables every rule in the table below as an error except the two marked "site-system config", which the next section covers.
Site System config
configs['site-system'] is configs.recommended plus the two Site System rules (no-preferred-region, no-long-class-in-list), all errors. A site adds it when it declares a siteSystem tier in brandlab.config.ts, so fleet bumps and security patches stay green until a site adopts the shared shell:
import brandlab from '@brandlab-digital/eslint-plugin'
export default [...brandlab.configs.next, brandlab.configs['site-system']]
The conformance engineering check accepts exactly two eslint.config.mjs default exports: export default brandlab.configs.next, and the form above (brandlab.configs["site-system"] with double quotes is also accepted, and so is a trailing comma or the array split over lines). Any other array fails the tool-preset check.
For a non-Next consumer, [brandlab.configs['site-system']] alone enables all nine rules. SITE_SYSTEM_RULE_IDS lists the two IDs.
Stable rule IDs
| Rule | Purpose | Preferred implementation |
|---|---|---|
brandlab/no-retired-analytics-events | Rejects retired event names such as contact_form_submission and phone_call. | Use contact_form for accepted forms and phone_link_click for telephone-link diagnostics. |
brandlab/no-raw-json-ld | Rejects raw JSON-LD <script> and createElement output in consumers. | Render typed graphs through @brandlab-digital/site-next/schema. |
brandlab/no-unsafe-rich-text-sinks | Rejects every project-owned raw HTML sink (dangerouslySetInnerHTML in JSX, props objects or assignments), whatever sanitiser it names. The conformance checker applies the same policy. | Render JSON-LD with SchemaScript and rich text with SafeRichText from @brandlab-digital/site-next. |
brandlab/no-protected-platform-reimplementation | Rejects local declarations and direct calls that bypass protected contact, SEO or analytics surfaces. | Import the shared site-core or site-next API. |
brandlab/no-analytics-pii | Rejects statically provable lead PII, message and attachment data in known analytics sinks. | Send only shared allowlisted event parameters. |
brandlab/no-unsafe-external-links | Rejects script-bearing link schemes and external new-context links with opener access when the URL is static. | Use safe schemes and rel="noopener"; keep runtime URL validation at the trust boundary. |
brandlab/no-raw-image-elements | Rejects raw website <img> and createElement('img') output. | Use next/image; the documented getImageProps art-direction pattern and email renderer files remain exempt. |
brandlab/no-preferred-region (site-system config, 3.13.0) | Rejects an exported preferredRegion (a variable, function or export { x as preferredRegion }). It is deprecated, and on Vercel accepts only auto, global or home. | Leave the region to vercel.json regions, recorded in brandlab.config.ts hosting.originRegion (rule origin-region-v1). |
brandlab/no-long-class-in-list (site-system config, 3.14.0) | Rejects a className (or class) string over 80 characters inside a .map() or .flatMap() callback: a literal, template text, or a string inside cx(), a ternary, && or an array. Every item repeats it in the HTML and the RSC payload. Tests and installed packages are exempt. | Style the repeated item once with a named class in the site stylesheet, or keep the class at 80 characters or fewer. |
RULE_IDS (all twelve) is exported for tools that need the canonical IDs without duplicating strings. The 3.0 release removed brandlab/no-allow-indexing-true together with the ALLOW_INDEXING path it guarded; indexing is decided by the deployment environment.
Deliberate boundaries
- The protected-surface rule checks declarations with reserved platform API names and direct transport or analytics calls. It does not compare implementation behaviour or package versions.
- The analytics rule inspects known event sinks and source-visible payloads. It does not guess the contents of opaque dynamic objects; runtime allowlisting and conformance remain mandatory.
- The link rule acts only on statically resolvable URLs. Dynamic destinations require runtime validation and behavioural verification.
- The image rule applies to website UI. Raw image markup remains available to non-Next renderers when the file is clearly located or named as an email renderer.
- Shared implementations in
site-core,site-nextand installed@brandlab-digitalpackages are not treated as consumer reimplementations. - The telephone-link rule reads the
hrefattribute where it is written. A link built elsewhere (atelHrefvariable, a config object,createElement) is out of reach; conformance reads the rendered links. - The hover-timer rule follows handlers declared in the same file: inline, named, wrapped in a call such as
useCallback, or reached through local helper functions. A timer in an imported hook, or in an effect driven by hover state, is not found. It ignores files outsidesrc/and files whose path and exports do not name a header, nav, menu or drawer. - The deep-import rule covers
@brandlab-digital/site-nextonly.
These constraints keep linting deterministic and editor-fast while the conformance engine owns deeper AST, behavioural and rendered-output evidence.