eslint-plugin-gameface
v1.2.0
Published
Gameface-specific ESLint rules (HTML data binding, CSS) — stubs for incremental port from HTMLHint and Stylelint.
Maintainers
Readme
eslint-plugin-gameface
Check the full documentation here.
ESLint plugin for Gameface. It ships a flat preset that runs @html-eslint on *.html, @eslint/css on *.css, and data-driven gameface/* rules backed by JSON under gameface-features/ (regenerated by your feature extractor).
Install
npm install --save-dev eslint eslint-plugin-gamefaceRequires ESLint >= 9.15 (flat config). HTML/CSS parsers and the TypeScript parser used by the preset are dependencies of this package—no separate install. Node.js LTS is recommended; see @eslint/css engines if you hit environment issues.
Use the preset
// eslint.config.js
import gameface from "eslint-plugin-gameface";
export default [
{ ignores: ["**/node_modules/**"] },
...gameface.configs["flat/recommended"],
];Settings
The preset sets settings.gameface.modelsDir to "Gameface-models" (resolved relative to the ESLint cwd) for gameface/html-databind-model-properties and other tooling.
settings.gameface.customDataBindAttributes(optionalstring[]): extra alloweddata-bind-*attribute names forgameface/html-databind-spelling(same idea as.cohtmllintrccustomDataBindAttributesin HTMLLint).settings.gameface.version(optional string): which Gameface feature catalog to use. Defaults to"latest"(JSON shipped undergameface-features/next tocss/,html/, etc.). If you add a directorygameface-features/versions/<version>/with the same layout (css/,html/,selectors/,js/), setversionto that<version>string and rules load those files instead. Values containing..,/, or\are ignored (falls back to latest). Setsettings.gameface.versionon ESLintfilesglobs that run Gameface rules (HTML, CSS, JSX) when you use a versioned catalog.
The JSON-driven rules pick up version automatically. Collector false positives are corrected internally in src/data/catalog-whitelists.js (not configurable in ESLint).
gameface-features/ data
| Path | Used by |
| --- | --- |
| gameface-features/css/{supported,partial,unsupported}.json | gameface/css-no-unsupported-properties, gameface/css-partial-property-values, HTML embedded/inline CSS rules |
| gameface-features/functions/{supported,unsupported}.json | gameface/css-no-unsupported-functions, HTML embedded/inline and JSX inline CSS function rules (missing rows only; unknown rows such as url are excluded) |
| gameface-features/html/{supported,partial,unsupported}.json | gameface/html-parsed-no-impl, gameface/html-partial-features |
| gameface-features/selectors/{supported,partial,unsupported}.json | gameface/css-no-unsupported-selectors, gameface/css-partial-selectors |
| gameface-features/js/*.json | gameface/js-no-unsupported-globals, gameface/js-partial-member-access |
summary.json files are informational only; rules read the array catalogs.
Versioned catalogs live under gameface-features/versions/<version>/ (same subpaths as above). See settings.gameface.version in Settings.
Publishing: package.json "files" includes gameface-features so npm tarballs ship the JSON.
Rules
| Rule id | Default in flat/recommended | Description |
| --- | --- | --- |
| gameface/css-no-unsupported-properties | error (CSS) | Property in css/unsupported.json (missing/unsupported). |
| gameface/css-no-unsupported-functions | error (CSS/SCSS/HTML/JSX inline) | CSS function name in functions/unsupported.json with status: "missing" (e.g. clamp, min). Rows with status: "unknown" are not linted. |
| gameface/css-partial-property-values | error (CSS) | Keyword values from partial catalog (unsupportedValues ∪ logRejectedValues minus supportedValues). Length/unit values (e.g. width: 10px) are not flagged when the catalog row lists keyword gates or supportedUnits only. Declaration-level partialPropertyCatalog applies only to rows without keyword lists (e.g. non-suppressed probe / note); value-accepted-but-not-computed probes are skipped. |
| gameface/html-embedded-css-no-unsupported-functions | error (HTML) | Same function catalog as css-no-unsupported-functions inside <style> blocks. |
| gameface/html-inline-css-no-unsupported-functions | error (HTML) | Same function catalog on style="..." attributes. |
| gameface/jsx-inline-css-no-unsupported-functions | error (JSX) | Same function catalog on JSX style string values. |
| gameface/css-no-var-in-keyframes | error (CSS/SCSS/HTML/JSX) | Doc constraint: var() inside @keyframes. |
| gameface/css-no-calc-in-keyframes | error (CSS/SCSS/HTML/JSX) | Doc constraint: calc() inside @keyframes. |
| gameface/css-var-no-fallback | error (CSS/SCSS/HTML/JSX) | Doc constraint: var(--name, fallback) second argument. |
| gameface/css-calc-no-mixed-percent-units | error (CSS/SCSS/HTML/JSX) | Doc constraint: calc() mixing % with other length units (e.g. 50% - 20px). |
| gameface/html-embedded-css-no-var-in-keyframes | error (HTML) | Same as css-no-var-in-keyframes in <style>. |
| gameface/html-embedded-css-no-calc-in-keyframes | error (HTML) | Same as css-no-calc-in-keyframes in <style>. |
| gameface/html-embedded-css-var-no-fallback | error (HTML) | Same as css-var-no-fallback in <style>. |
| gameface/html-embedded-css-calc-no-mixed-percent-units | error (HTML) | Same as css-calc-no-mixed-percent-units in <style>. |
| gameface/html-inline-css-no-var-in-keyframes | error (HTML) | Same keyframes var() check on inline style when parseable as stylesheet. |
| gameface/html-inline-css-no-calc-in-keyframes | error (HTML) | Same keyframes calc() check on inline style. |
| gameface/html-inline-css-var-no-fallback | error (HTML) | Same var() fallback check on inline style. |
| gameface/html-inline-css-calc-no-mixed-percent-units | error (HTML) | Same calc() mixed-% check on inline style. |
| gameface/jsx-inline-css-no-var-in-keyframes | error (JSX) | Same keyframes var() check on JSX string style. |
| gameface/jsx-inline-css-no-calc-in-keyframes | error (JSX) | Same keyframes calc() check on JSX string style. |
| gameface/jsx-inline-css-var-no-fallback | error (JSX) | Same var() fallback check on JSX string style. |
| gameface/jsx-inline-css-calc-no-mixed-percent-units | error (JSX) | Same calc() mixed-% check on JSX string style. |
| gameface/css-no-unsupported-selectors | error (CSS) | Pseudo text exactly matches selectors/unsupported.json name (e.g. :any-link). |
| gameface/css-partial-selectors | warn (CSS) | Pseudo source text (including arguments, e.g. :nth-child(2 of .x)) exactly matches selectors/partial.json name. Simple :nth-child(2) is not listed and will not warn. |
| gameface/html-parsed-no-impl | error (HTML) | Tag is parsed-no-impl in html/unsupported.json. Default scope: "curated" only reports a focused set (e.g. <select>, table model, <iframe>, …) so <h1> / <p> are not flagged. Use scope: "all" to report every parsed-no-impl tag (still minus ignoreTags). |
| gameface/html-partial-features | warn (HTML) | Default mode: "attribute-checks": for tags in html/partial.json with evidence.checks, warns only if you use an HTML attribute mapped to a checks entry that is false (e.g. <img alt> when altProperty is false). Use mode: "tag-warn" for the old blanket partial warning. mode: "off" disables. warnAllowlist: true warns on any tag not in html/supported.json. |
| gameface/html-databind-spelling | error (HTML) | Any attribute containing data-bind must be a known name: static list + data-bind-style-<css> where <css> is not listed as unsupported in css/unsupported.json, plus legacy Coherent style binds and customDataBindAttributes. |
| gameface/html-databind-curly-brackets | error (HTML) | data-bind* attribute values must contain {{ and }}. |
| gameface/html-databind-property-accessors | error (HTML) | Catches broken {{Model.}} / split-brace / bracket-in-brace patterns (ported from HTMLLint). |
| gameface/html-databind-bind-for | error (HTML) | iter:{{collection}} or index, iter:{{collection}} (optional [ {{a}}, {{b}} ]). Rejects ; and malformed iterators. |
| gameface/html-databind-class-toggle | error (HTML) | Each ;-separated entry must be class-name:condition with {{…}} in the condition. |
| gameface/html-databind-model-properties | error (HTML) | Model paths in the opening tag must match JSON files under settings.gameface.modelsDir (ported from HTMLLint model-properties). |
| gameface/jsx-databind-spelling | error (JSX) | Same allowlist as html-databind-spelling on data-bind* JSX attributes. |
| gameface/jsx-databind-curly-brackets | error (JSX) | Same as html-databind-curly-brackets. |
| gameface/jsx-databind-property-accessors | error (JSX) | Same as html-databind-property-accessors. |
| gameface/jsx-databind-bind-for | error (JSX) | Same as html-databind-bind-for. |
| gameface/jsx-databind-class-toggle | error (JSX) | Same as html-databind-class-toggle. |
| gameface/jsx-databind-model-properties | error (JSX) | Same as html-databind-model-properties on the opening element. |
| gameface/js-no-unsupported-globals | error (JS/JSX/TS/TSX) | Flags references to globals listed in js/unsupported.json (e.g. Chrome, new AbortController()). Only flags unbound globals; skips bindings, imports, and non-computed member keys. |
| gameface/js-partial-member-access | warn (JS/JSX/TS/TSX) | Flags static Type.member where member is listed under evidence.missing for that type in js/partial.json (or on js/unsupported.json rows). Limited to Identifier.member shape; dynamic and computed access are not analyzed. |
| gameface/html-svg-no-unsupported-elements | error (HTML) | Inside <svg>, disallows unsupported tags and SMIL (<animate>, <foreignObject>, …) per SVG Support. |
| gameface/jsx-svg-no-unsupported-elements | error (JSX/TSX) | Same denylist for JSX <svg> trees. |
| gameface/html-svg-mask-clip-path-conflict | warn (HTML) | Warns when both mask and clip-path use url(#…) on the same SVG element (clip-path wins). |
| gameface/jsx-svg-mask-clip-path-conflict | warn (JSX/TSX) | JSX parity. |
| gameface/html-svg-tspan-ignored | off | Optional warn: <tspan> text is merged into parent <text> without separate styling. |
| gameface/css-svg-keyframes-sizing-units | error (CSS/SCSS) | In @keyframes, width / height / font-size must include units for Gameface SVG CSS animation. |
| gameface/html-embedded-css-svg-keyframes-sizing-units | error (HTML) | Same in <style> blocks. |
| gameface/html-inline-css-svg-keyframes-sizing-units | error (HTML) | Same in style="..." when parseable. |
| gameface/jsx-inline-css-svg-keyframes-sizing-units | error (JSX) | Same in JSX string style. |
| gameface/css-svg-keyframes-path-arc-animation | warn (CSS/SCSS) | Warns when @keyframes animates d with elliptical arc (A/a) commands. |
| gameface/html-embedded-css-svg-keyframes-path-arc-animation | warn (HTML) | Same in <style>. |
| gameface/html-inline-css-svg-keyframes-path-arc-animation | warn (HTML) | Same in inline style. |
| gameface/jsx-inline-css-svg-keyframes-path-arc-animation | warn (JSX) | Same in JSX string style. |
| gameface/html-svg-stroke-dash-non-path | error (HTML) | stroke-dasharray / stroke-dashoffset only on <path>, not other SVG shapes (engine limit). |
| gameface/jsx-svg-stroke-dash-non-path | error (JSX/TSX) | JSX parity (attributes and inline style). |
| gameface/css-svg-stroke-dash-non-path | error (CSS/SCSS) | Same properties in rules that target non-path SVG tags (e.g. circle { stroke-dasharray: … }). |
| gameface/html-embedded-css-svg-stroke-dash-non-path | error (HTML) | Same in <style> blocks. |
| gameface/css-svg-keyframes-stroke-dash-path-only | warn (CSS/SCSS) | @keyframes that animate stroke-dasharray / stroke-dashoffset only run on <path>. |
| gameface/html-embedded-css-svg-keyframes-stroke-dash-path-only | warn (HTML) | Same in <style>. |
| gameface/html-inline-css-svg-keyframes-stroke-dash-path-only | warn (HTML) | Same in inline style. |
| gameface/jsx-inline-css-svg-keyframes-stroke-dash-path-only | warn (JSX) | Same in JSX string style. |
Data binding (HTMLLint parity)
The legacy HTMLLint package (HTMLHint + .cohtmllintrc) is superseded on the ESLint path by the gameface/html-databind-* and gameface/jsx-databind-* rules above. You can keep HTMLLint for non-ESLint workflows; for ESLint + @html-eslint, use the preset rules and the same settings (modelsDir, customDataBindAttributes).
- HTML partial: Only attributes covered by the
CHECK_KEY_TO_HTML_ATTRmap inhtml-partial-features.jsare checked; JS-only gaps (toDataURL,sheetProperty, etc.) cannot be seen from markup alone. - CSS selectors: Matching is exact string on the pseudo node source; authors using different spacing or equivalent forms may not match catalog keys.
- CSS functions: Only
functions/unsupported.jsonrows withstatus: "missing"are reported.unknownrows (url,image-set,cross-fade) are omitted because the catalog could not auto-probe them. Nested calls (e.g.calc(min(…))) are checked per function node. JSXstyle={{ … }}object literals are not analyzed for functions (stringstylevalues only). - CSS doc restrictions:
css-no-var-in-keyframes,css-no-calc-in-keyframes,css-var-no-fallback, andcss-calc-no-mixed-percent-units(and HTML/JSX variants) encode Gameface documentation limits that are separate from the function catalog. They are not inferred fromfunctions/supported.jsonprobes. JSX objectstyleprops are not analyzed for these rules either. - SVG: Rules under
gameface/*-svg-*follow the SVG Support doc (gameface-svg-support.js), plus engine limits such as path-onlystroke-dash*(svg-stroke-dash.js). HTML<a>outside<svg>is allowed; only SVG-namespace<a>is flagged. Standalone.svgasset files are not linted in v1. - JS API checks:
gameface/js-no-unsupported-globalsandgameface/js-partial-member-accessare catalog-driven and static. They only see references resolved as unbound globals (no enclosing binding) and member access of the shapeIdentifier.member(non-computed). Dynamic patterns likeobj.effect,globalThis['Chrome'], or members reached through aliases are not analyzed. TypeScript files use@typescript-eslint/parserwithout type information (noparserOptions.project), so type inference is not used. - SCSS (
.scss): The preset uses@eslint/csswithlanguageOptions.tolerant: trueon**/*.scssso Sass syntax ($variables,&nesting, etc.) does not fail with parse errors. Gameface rules still run on declarations the parser recovers. Values that contain variables (e.g.width: $component-width) are not resolved, so keyword/unit checks may be skipped for those declarations. Native SCSS parsing in@eslint/cssis still in progress (eslint/css#90); for Sass-specific linting (naming, mixins, etc.) you can keep Stylelint alongside ESLint. - Coherent / vendor CSS properties (
coh-*, etc.):gameface/css-no-unsupported-propertiesonly flags names incss/unsupported.json; it does not treat Coherent props as “unknown”. If you still see unknown property diagnostics, they usually come from@eslint/csscss/no-invalid-properties(when enabled) or the editor (e.g. VS Codecss.lint.unknownProperties/unknownVendorSpecificProperties). This plugin cannot change those engines. Mitigations: set"css/no-invalid-properties": "off"(or avoid enabling@eslint/cssrecommended rules that turn it on) for Gameface CSS globs, add VS Codecss.customDatafor your vendor properties, or relax editor CSS lint settings.
Development
# From repo root eslint-gameface/
npm run lint
npm testnpm test runs tests/feature-index-smoke.mjs, tests/html-embedded-inline-jsx.mjs, tests/html-databind-rules.mjs, tests/jsx-databind-rules.mjs, tests/js-api-rules.mjs, and tests/catalog-lint-matrix.mjs. Fixture notes: tests/fixtures/README.md.
Catalog-driven lint matrix
After updating gameface-features/ JSON, regenerate the sampled test matrix and run catalog lint checks:
# From repo root eslint-gameface/
npm run test:extract-matrix # writes tests/generated/test-matrix.json
npm run test:catalog # ESLint lintText per matrix row (isolated rule config)
npm run test:matrix:check # CI: fail if committed matrix ≠ fresh extract (ignores generatedAt)The extractor (scripts/extract-catalog-test-matrix.mjs) samples catalog rows with a fixed seed (42) and per-category caps, plus fixed databind scenarios and control rows. Each matrix row carries ruleId, snippet, and expect.reports so tests/catalog-lint-matrix.mjs can assert the intended rule fires (or stays silent for controls).
Legacy migration (optional)
Older HTMLHint / Stylelint workflows can be replaced incrementally.
License
MIT
