@airframeui/eslint-plugin 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +146 -0
- package/dist/class-attr.d.ts +20 -0
- package/dist/class-attr.d.ts.map +1 -0
- package/dist/class-attr.js +102 -0
- package/dist/class-attr.js.map +1 -0
- package/dist/class-catalog.d.ts +44 -0
- package/dist/class-catalog.d.ts.map +1 -0
- package/dist/class-catalog.js +223 -0
- package/dist/class-catalog.js.map +1 -0
- package/dist/data.json +2862 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +490 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Airframeui
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @airframeui/eslint-plugin
|
|
2
|
+
|
|
3
|
+
ESLint rules that keep projects on [@airframeui](https://airframeui.com) — catch leftover Tailwind/Bootstrap classes, unknown `af-*` names, redundant defaults, and field-error ARIA.
|
|
4
|
+
|
|
5
|
+
Docs: [airframeui.com/docs/eslint-plugin](https://airframeui.com/docs/eslint-plugin)
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -D @airframeui/eslint-plugin
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires ESLint 9+ (flat config) or ESLint 8 with eslintrc. Same version as `@airframeui/core` / `@airframeui/tokens` (Changesets fixed group). The plugin ships a build-time snapshot of `classes.json` and the token catalog — the same sources [IntelliSense for Airframe](https://airframeui.com/docs/vscode-extension) uses. If those packages are installed in the project, their catalogs win so lint matches the CSS you actually ship.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Flat config (recommended)
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
// eslint.config.js
|
|
21
|
+
import airframe from '@airframeui/eslint-plugin';
|
|
22
|
+
|
|
23
|
+
export default [airframe.configs['flat/recommended']];
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Legacy `.eslintrc`
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
module.exports = {
|
|
30
|
+
plugins: ['@airframeui'],
|
|
31
|
+
extends: ['plugin:@airframeui/recommended'],
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Autofix
|
|
36
|
+
|
|
37
|
+
Rules marked **yes** in the table set `meta.fixable` and rewrite the `class` / `className` string (quotes are preserved; an empty class attribute is removed).
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx eslint . --fix
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Same command as the editor **Fix all auto-fixable problems** action. Autofix only runs for deterministic mappings:
|
|
44
|
+
|
|
45
|
+
| Fixed | Not fixed |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| Known aliases (`af-btn-danger` → `af-btn af-is-danger`) | Unknown `af-*` with only a fuzzy guess |
|
|
48
|
+
| Heuristic `af-{pattern}-{variant}` when both parts exist in `classes.json` | Tailwind / Bootstrap leftovers (unless they also match a layout recipe) |
|
|
49
|
+
| Layout recipes (`flex flex-col gap-4` → `af-stack af-gap-lg`) | Field-error ARIA (you must choose the `id`) |
|
|
50
|
+
| Redundant defaults and width (`af-card af-stack af-gap-md` → `af-card`) | |
|
|
51
|
+
|
|
52
|
+
## Rules
|
|
53
|
+
|
|
54
|
+
All seven rules ship in `recommended`. They only inspect **string literals** on `class` / `className` in JSX. Existing `af-*` tokens are ignored by the Tailwind and Bootstrap detectors.
|
|
55
|
+
|
|
56
|
+
| Rule | Recommended | Autofix | What it means |
|
|
57
|
+
| --- | --- | --- | --- |
|
|
58
|
+
| `@airframeui/no-tailwind-classes` | warn | no | A class token looks like Tailwind (layout, spacing, type, colour, `sm:` / `md:` prefixes). Prefer Airframe classes. |
|
|
59
|
+
| `@airframeui/no-bootstrap-classes` | warn | no | A class token looks like Bootstrap (`btn`, `row`, `col-*`, `card-*`, `d-flex`, …). Prefer Airframe classes. |
|
|
60
|
+
| `@airframeui/prefer-airframe-layout-recipes` | warn | yes | A known utility combo should be an Airframe layout recipe (`af-stack`, `af-inline`, `af-grid`, `af-container`). |
|
|
61
|
+
| `@airframeui/require-field-error-aria` | error | no | Error copy and error inputs must be wired for assistive tech. |
|
|
62
|
+
| `@airframeui/no-unknown-airframe-class` | error | yes* | Invented `af-*` names. Autofix when there is a known alias or catalog heuristic. |
|
|
63
|
+
| `@airframeui/no-redundant-defaults` | warn | yes | Classes that restate a pattern default (`af-card` + `af-stack`, `af-container af-container-xl`, `<h1 class="af-text-h1">`). |
|
|
64
|
+
| `@airframeui/no-redundant-width` | warn | yes | `af-w-full` on form controls (already 100%); `af-block` on `af-btn` (use `af-w-full`). |
|
|
65
|
+
|
|
66
|
+
### `@airframeui/no-tailwind-classes`
|
|
67
|
+
|
|
68
|
+
Flags whole tokens such as `flex`, `flex-col`, `grid-cols-3`, `gap-4`, `p-4`, `items-center`, `justify-between`, `text-sm`, `bg-blue-600`, `md:gap-4`. Does not flag `af-grid` or `af-btn`.
|
|
69
|
+
|
|
70
|
+
### `@airframeui/no-bootstrap-classes`
|
|
71
|
+
|
|
72
|
+
Flags whole tokens such as `btn`, `btn-primary`, `row`, `col-lg-6`, `d-flex`, `container`, `card-body`, `alert-warning`. Does not flag `af-card` or `af-btn`.
|
|
73
|
+
|
|
74
|
+
### `@airframeui/prefer-airframe-layout-recipes`
|
|
75
|
+
|
|
76
|
+
Rewrites these combos. Gap numbers map to the Airframe scale (`4` → `lg`, `8` → `2xl`). Default `md` (`gap-3` / `g-3`) is omitted because `af-stack` / `af-grid` already use it.
|
|
77
|
+
|
|
78
|
+
| Before | After |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| `flex flex-col` / `flex flex-col gap-4` | `af-stack` / `af-stack af-gap-lg` |
|
|
81
|
+
| `flex flex-wrap` / `flex flex-wrap gap-2` | `af-inline` / `af-inline af-gap-sm` |
|
|
82
|
+
| `grid` / `grid grid-cols-3 gap-6` | `af-grid` / `af-grid af-grid-3 af-gap-xl` |
|
|
83
|
+
| `d-flex flex-column` | `af-stack` |
|
|
84
|
+
| `d-flex flex-wrap` | `af-inline` |
|
|
85
|
+
| `d-grid` / `row` | `af-grid` |
|
|
86
|
+
| `container` / `container-fluid` | `af-container` |
|
|
87
|
+
|
|
88
|
+
Skipped if the attribute already has `af-stack`, `af-inline`, `af-grid`, `af-cluster`, `af-spread`, `af-center`, or `af-container`.
|
|
89
|
+
|
|
90
|
+
### `@airframeui/require-field-error-aria`
|
|
91
|
+
|
|
92
|
+
Two checks:
|
|
93
|
+
|
|
94
|
+
| Markup | Required |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| `class` contains `af-field__error` | `id` on that element (for `aria-describedby`) |
|
|
97
|
+
| `class` contains `af-is-error`, or `aria-invalid="true"` | `aria-describedby` on that element |
|
|
98
|
+
|
|
99
|
+
### `@airframeui/no-unknown-airframe-class`
|
|
100
|
+
|
|
101
|
+
Flags `af-*` tokens that are not in the class catalog. Prefers the linted project's `@airframeui/core/classes.json`, then the snapshot generated from core at plugin build time.
|
|
102
|
+
|
|
103
|
+
| Invented | Autofix |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| `af-btn-primary` / `af-btn-secondary` / `af-btn-danger` | `af-btn af-is-primary` (etc.) |
|
|
106
|
+
| `af-btn-destructive` | `af-btn af-is-danger` |
|
|
107
|
+
| `af-btn-block` / `af-is-block` / `af-is-full` | `af-w-full` |
|
|
108
|
+
| `af-{pattern}-{variant}` when both `af-{pattern}` and `af-is-{variant}` exist | `af-{pattern} af-is-{variant}` |
|
|
109
|
+
| Anything else unknown | Message may suggest a close name; no autofix |
|
|
110
|
+
|
|
111
|
+
### `@airframeui/no-redundant-defaults`
|
|
112
|
+
|
|
113
|
+
| Before | After |
|
|
114
|
+
| --- | --- |
|
|
115
|
+
| `af-card af-stack af-gap-md` | `af-card` |
|
|
116
|
+
| `af-grid af-gap-md` / `af-stack af-gap-md` | `af-grid` / `af-stack` |
|
|
117
|
+
| `af-container af-container-xl` | `af-container-xl` |
|
|
118
|
+
| `<h1 class="af-text-h1">` | `<h1>` |
|
|
119
|
+
|
|
120
|
+
Kept: `af-card af-gap-lg`, `af-card af-inline`, `af-container af-container-lg@xl`, `<h2 class="af-text-h1">`.
|
|
121
|
+
|
|
122
|
+
### `@airframeui/no-redundant-width`
|
|
123
|
+
|
|
124
|
+
| Before | After |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `af-input af-w-full` (also textarea, select, range, progress, meter, input-group, `af-field__control`) | `af-input` |
|
|
127
|
+
| `af-btn af-block` | `af-btn af-w-full` |
|
|
128
|
+
|
|
129
|
+
Not flagged: `af-btn af-w-full`, `af-input af-is-autosize af-w-full`.
|
|
130
|
+
|
|
131
|
+
### Notes
|
|
132
|
+
|
|
133
|
+
- Template literals, `class:list`, Vue/Svelte bindings, and Astro expression attributes are not covered yet.
|
|
134
|
+
- Tailwind/Bootstrap detectors ignore existing `af-*` tokens so `af-grid` / `af-btn` do not false-positive.
|
|
135
|
+
|
|
136
|
+
## Links
|
|
137
|
+
|
|
138
|
+
- Docs: [airframeui.com/docs/eslint-plugin](https://airframeui.com/docs/eslint-plugin)
|
|
139
|
+
- Site: [airframeui.com](https://airframeui.com)
|
|
140
|
+
- Source: [github.com/airframeui/airframeui](https://github.com/airframeui/airframeui/tree/main/packages/eslint-plugin)
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
Airframe core libraries are licensed under the MIT License.
|
|
145
|
+
|
|
146
|
+
Commercial tooling, premium themes, and hosted services may be offered under separate commercial licenses.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
export declare function splitClasses(value: string): string[];
|
|
3
|
+
export declare function isAirframeClass(token: string): boolean;
|
|
4
|
+
export declare function getJsxAttrName(node: Rule.Node): string | undefined;
|
|
5
|
+
export declare function getStringAttrValue(node: Rule.Node): string | undefined;
|
|
6
|
+
export declare function getJsxTagName(attrNode: Rule.Node): string | undefined;
|
|
7
|
+
export type ClassAttr = {
|
|
8
|
+
node: Rule.Node;
|
|
9
|
+
classValue: string;
|
|
10
|
+
tagName?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function visitClassAttributes(_context: Rule.RuleContext, check: (attr: ClassAttr) => void): Rule.RuleListener;
|
|
13
|
+
/** Rewrite tokens in order; `rewrite` returning parts replaces that token. Dedupes. */
|
|
14
|
+
export declare function rewriteTokens(tokens: string[], rewrite: (token: string) => string[] | null): string[];
|
|
15
|
+
/**
|
|
16
|
+
* Autofix a `class` / `className` literal. Preserves the original quote.
|
|
17
|
+
* Empty `next` removes the attribute (and the space before it).
|
|
18
|
+
*/
|
|
19
|
+
export declare function classAttrFix(context: Rule.RuleContext, node: Rule.Node, next: string): ((fixer: Rule.RuleFixer) => Rule.Fix) | null;
|
|
20
|
+
//# sourceMappingURL=class-attr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"class-attr.d.ts","sourceRoot":"","sources":["../src/class-attr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAInC,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAEpD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,GAAG,MAAM,GAAG,SAAS,CAGlE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,GAAG,MAAM,GAAG,SAAS,CAKtE;AAgBD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,GAAG,MAAM,GAAG,SAAS,CAYrE;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,IAAI,CAAC,WAAW,EAC1B,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,GAC/B,IAAI,CAAC,YAAY,CAWnB;AAED,uFAAuF;AACvF,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,GAAG,IAAI,GAC1C,MAAM,EAAE,CAYV;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,IAAI,CAAC,WAAW,EACzB,IAAI,EAAE,IAAI,CAAC,IAAI,EACf,IAAI,EAAE,MAAM,GACX,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAe9C"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const CLASS_ATTRS = new Set(['className', 'class']);
|
|
2
|
+
export function splitClasses(value) {
|
|
3
|
+
return value.trim().split(/\s+/).filter(Boolean);
|
|
4
|
+
}
|
|
5
|
+
export function isAirframeClass(token) {
|
|
6
|
+
return token === 'af' || token.startsWith('af-');
|
|
7
|
+
}
|
|
8
|
+
export function getJsxAttrName(node) {
|
|
9
|
+
if (!node || typeof node !== 'object' || !('name' in node))
|
|
10
|
+
return undefined;
|
|
11
|
+
return node.name?.name;
|
|
12
|
+
}
|
|
13
|
+
export function getStringAttrValue(node) {
|
|
14
|
+
const value = node.value;
|
|
15
|
+
if (!value || typeof value !== 'object')
|
|
16
|
+
return undefined;
|
|
17
|
+
if (typeof value.value === 'string')
|
|
18
|
+
return value.value;
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
function getStringAttrRange(node) {
|
|
22
|
+
const value = node.value;
|
|
23
|
+
const range = value?.range;
|
|
24
|
+
if (Array.isArray(range) && range.length === 2)
|
|
25
|
+
return [range[0], range[1]];
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function getAttrQuote(context, node) {
|
|
29
|
+
const range = getStringAttrRange(node);
|
|
30
|
+
if (!range)
|
|
31
|
+
return '"';
|
|
32
|
+
const ch = context.sourceCode.text[range[0]];
|
|
33
|
+
return ch === "'" ? "'" : '"';
|
|
34
|
+
}
|
|
35
|
+
export function getJsxTagName(attrNode) {
|
|
36
|
+
let parent = attrNode;
|
|
37
|
+
while (parent && typeof parent === 'object' && 'parent' in parent) {
|
|
38
|
+
parent = parent.parent;
|
|
39
|
+
if (!parent || typeof parent !== 'object' || !('type' in parent))
|
|
40
|
+
continue;
|
|
41
|
+
const parentType = parent.type;
|
|
42
|
+
if (parentType !== 'JSXOpeningElement' && parentType !== 'JSXElement')
|
|
43
|
+
continue;
|
|
44
|
+
const name = parent.name;
|
|
45
|
+
if (name?.type === 'JSXIdentifier' && typeof name.name === 'string')
|
|
46
|
+
return name.name;
|
|
47
|
+
if (typeof name?.name === 'string' && !name.type)
|
|
48
|
+
return name.name;
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
export function visitClassAttributes(_context, check) {
|
|
53
|
+
return {
|
|
54
|
+
JSXAttribute(node) {
|
|
55
|
+
const name = getJsxAttrName(node);
|
|
56
|
+
if (!name || !CLASS_ATTRS.has(name))
|
|
57
|
+
return;
|
|
58
|
+
const val = getStringAttrValue(node);
|
|
59
|
+
if (typeof val !== 'string')
|
|
60
|
+
return;
|
|
61
|
+
const tagName = getJsxTagName(node);
|
|
62
|
+
check(tagName ? { node, classValue: val, tagName } : { node, classValue: val });
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Rewrite tokens in order; `rewrite` returning parts replaces that token. Dedupes. */
|
|
67
|
+
export function rewriteTokens(tokens, rewrite) {
|
|
68
|
+
const out = [];
|
|
69
|
+
const seen = new Set();
|
|
70
|
+
for (const token of tokens) {
|
|
71
|
+
const parts = rewrite(token) ?? [token];
|
|
72
|
+
for (const part of parts) {
|
|
73
|
+
if (!part || seen.has(part))
|
|
74
|
+
continue;
|
|
75
|
+
seen.add(part);
|
|
76
|
+
out.push(part);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Autofix a `class` / `className` literal. Preserves the original quote.
|
|
83
|
+
* Empty `next` removes the attribute (and the space before it).
|
|
84
|
+
*/
|
|
85
|
+
export function classAttrFix(context, node, next) {
|
|
86
|
+
const valueRange = getStringAttrRange(node);
|
|
87
|
+
if (!valueRange)
|
|
88
|
+
return null;
|
|
89
|
+
const quote = getAttrQuote(context, node);
|
|
90
|
+
const attrRange = node.range;
|
|
91
|
+
return (fixer) => {
|
|
92
|
+
if (next === '' && attrRange) {
|
|
93
|
+
const src = context.sourceCode.text;
|
|
94
|
+
let from = attrRange[0];
|
|
95
|
+
while (from > 0 && /[ \t]/.test(src[from - 1]))
|
|
96
|
+
from -= 1;
|
|
97
|
+
return fixer.removeRange([from, attrRange[1]]);
|
|
98
|
+
}
|
|
99
|
+
return fixer.replaceTextRange(valueRange, `${quote}${next}${quote}`);
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=class-attr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"class-attr.js","sourceRoot":"","sources":["../src/class-attr.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Keep in sync with `@airframeui/mcp` `validate-tools` CLASS_ALIASES. */
|
|
2
|
+
export declare const CLASS_ALIASES: Record<string, {
|
|
3
|
+
replacement: string;
|
|
4
|
+
note: string;
|
|
5
|
+
}>;
|
|
6
|
+
export declare const FORM_CONTROLS: Set<string>;
|
|
7
|
+
export type ClassIndex = {
|
|
8
|
+
all: string[];
|
|
9
|
+
known: Set<string>;
|
|
10
|
+
};
|
|
11
|
+
export type TokenIndex = {
|
|
12
|
+
all: string[];
|
|
13
|
+
known: Set<string>;
|
|
14
|
+
};
|
|
15
|
+
type BundledCatalog = {
|
|
16
|
+
version: string;
|
|
17
|
+
classes: string[];
|
|
18
|
+
tokens: string[];
|
|
19
|
+
};
|
|
20
|
+
export declare function loadBundledCatalog(): BundledCatalog;
|
|
21
|
+
export declare function splitResponsive(cls: string): {
|
|
22
|
+
base: string;
|
|
23
|
+
suffix: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function isAfClassToken(token: string): boolean;
|
|
26
|
+
export declare function applySuffix(parts: string[], suffix: string): string[];
|
|
27
|
+
export declare function getAlias(base: string): {
|
|
28
|
+
replacement: string;
|
|
29
|
+
note: string;
|
|
30
|
+
} | undefined;
|
|
31
|
+
/** `af-btn-danger` → `af-btn af-is-danger`, or `af-card-title` → `af-card__title`. */
|
|
32
|
+
export declare function heuristicReplacement(cls: string, known: Set<string>): string | null;
|
|
33
|
+
export declare function suggestClass(unknown: string, all: string[]): string | undefined;
|
|
34
|
+
/** Prefer the linted project's @airframeui/core; fall back to the build-time snapshot. */
|
|
35
|
+
export declare function loadClassIndex(cwd: string): ClassIndex;
|
|
36
|
+
/** Prefer the linted project's @airframeui/tokens; fall back to the build-time snapshot. */
|
|
37
|
+
export declare function loadTokenIndex(cwd: string): TokenIndex;
|
|
38
|
+
export declare function resolveUnknownClass(token: string, known: Set<string>, all: string[]): {
|
|
39
|
+
suggestion?: string;
|
|
40
|
+
note?: string;
|
|
41
|
+
fixable: boolean;
|
|
42
|
+
} | null;
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=class-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"class-catalog.d.ts","sourceRoot":"","sources":["../src/class-catalog.ts"],"names":[],"mappings":"AAMA,0EAA0E;AAC1E,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA6B/E,CAAC;AAEF,eAAO,MAAM,aAAa,aASxB,CAAC;AAIH,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACpB,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAOF,wBAAgB,kBAAkB,IAAI,cAAc,CAYnD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAI7E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAMrE;AAED,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,GACX;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAEnD;AAED,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAUnF;AAmBD,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAa/E;AAkBD,0FAA0F;AAC1F,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAwBtD;AAED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CA2BtD;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,EAClB,GAAG,EAAE,MAAM,EAAE,GACZ;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAiBjE"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { splitClasses } from './class-attr.js';
|
|
6
|
+
/** Keep in sync with `@airframeui/mcp` `validate-tools` CLASS_ALIASES. */
|
|
7
|
+
export const CLASS_ALIASES = {
|
|
8
|
+
'af-btn-danger': {
|
|
9
|
+
replacement: 'af-btn af-is-danger',
|
|
10
|
+
note: 'Variants use af-is-*, not af-btn-*',
|
|
11
|
+
},
|
|
12
|
+
'af-btn-primary': {
|
|
13
|
+
replacement: 'af-btn af-is-primary',
|
|
14
|
+
note: 'Variants use af-is-*, not af-btn-*',
|
|
15
|
+
},
|
|
16
|
+
'af-btn-secondary': {
|
|
17
|
+
replacement: 'af-btn af-is-secondary',
|
|
18
|
+
note: 'Variants use af-is-*, not af-btn-*',
|
|
19
|
+
},
|
|
20
|
+
'af-btn-destructive': {
|
|
21
|
+
replacement: 'af-btn af-is-danger',
|
|
22
|
+
note: 'Airframe uses af-is-danger, not destructive',
|
|
23
|
+
},
|
|
24
|
+
'af-btn-block': {
|
|
25
|
+
replacement: 'af-w-full',
|
|
26
|
+
note: 'There is no af-btn-block. Stretch buttons with af-w-full',
|
|
27
|
+
},
|
|
28
|
+
'af-is-block': {
|
|
29
|
+
replacement: 'af-w-full',
|
|
30
|
+
note: 'There is no af-is-block. Stretch shrink-to-content patterns with af-w-full',
|
|
31
|
+
},
|
|
32
|
+
'af-is-full': {
|
|
33
|
+
replacement: 'af-w-full',
|
|
34
|
+
note: 'There is no af-is-full. Stretch shrink-to-content patterns with af-w-full',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
export const FORM_CONTROLS = new Set([
|
|
38
|
+
'af-input',
|
|
39
|
+
'af-textarea',
|
|
40
|
+
'af-select',
|
|
41
|
+
'af-range',
|
|
42
|
+
'af-progress',
|
|
43
|
+
'af-meter',
|
|
44
|
+
'af-input-group',
|
|
45
|
+
'af-field__control',
|
|
46
|
+
]);
|
|
47
|
+
const AF_CLASS = /^af-[a-z0-9-]+(?:@[a-z0-9]+)?$/i;
|
|
48
|
+
const catalogCache = new Map();
|
|
49
|
+
const tokenCache = new Map();
|
|
50
|
+
let bundled = null;
|
|
51
|
+
export function loadBundledCatalog() {
|
|
52
|
+
if (bundled)
|
|
53
|
+
return bundled;
|
|
54
|
+
const file = fileURLToPath(new URL('./data.json', import.meta.url));
|
|
55
|
+
const doc = JSON.parse(readFileSync(file, 'utf8'));
|
|
56
|
+
const classes = Array.isArray(doc.classes) ? doc.classes : [];
|
|
57
|
+
const tokens = Array.isArray(doc.tokens) ? doc.tokens : [];
|
|
58
|
+
bundled = {
|
|
59
|
+
version: typeof doc.version === 'string' ? doc.version : '0.0.0',
|
|
60
|
+
classes,
|
|
61
|
+
tokens,
|
|
62
|
+
};
|
|
63
|
+
return bundled;
|
|
64
|
+
}
|
|
65
|
+
export function splitResponsive(cls) {
|
|
66
|
+
const at = cls.indexOf('@');
|
|
67
|
+
if (at === -1)
|
|
68
|
+
return { base: cls, suffix: '' };
|
|
69
|
+
return { base: cls.slice(0, at), suffix: cls.slice(at) };
|
|
70
|
+
}
|
|
71
|
+
export function isAfClassToken(token) {
|
|
72
|
+
return AF_CLASS.test(token);
|
|
73
|
+
}
|
|
74
|
+
export function applySuffix(parts, suffix) {
|
|
75
|
+
if (!suffix || parts.length === 0)
|
|
76
|
+
return parts;
|
|
77
|
+
const out = [...parts];
|
|
78
|
+
const last = out[out.length - 1];
|
|
79
|
+
if (last)
|
|
80
|
+
out[out.length - 1] = `${last}${suffix}`;
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
export function getAlias(base) {
|
|
84
|
+
return Object.hasOwn(CLASS_ALIASES, base) ? CLASS_ALIASES[base] : undefined;
|
|
85
|
+
}
|
|
86
|
+
/** `af-btn-danger` → `af-btn af-is-danger`, or `af-card-title` → `af-card__title`. */
|
|
87
|
+
export function heuristicReplacement(cls, known) {
|
|
88
|
+
const dashed = cls.match(/^af-([a-z0-9]+)-([a-z0-9]+)$/);
|
|
89
|
+
if (!dashed)
|
|
90
|
+
return null;
|
|
91
|
+
const [, pattern, rest] = dashed;
|
|
92
|
+
const modifier = `af-is-${rest}`;
|
|
93
|
+
const root = `af-${pattern}`;
|
|
94
|
+
if (known.has(root) && known.has(modifier))
|
|
95
|
+
return `${root} ${modifier}`;
|
|
96
|
+
const part = `af-${pattern}__${rest}`;
|
|
97
|
+
if (known.has(part))
|
|
98
|
+
return part;
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
function levenshtein(a, b) {
|
|
102
|
+
if (a === b)
|
|
103
|
+
return 0;
|
|
104
|
+
if (!a.length)
|
|
105
|
+
return b.length;
|
|
106
|
+
if (!b.length)
|
|
107
|
+
return a.length;
|
|
108
|
+
const row = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
109
|
+
for (let i = 0; i < a.length; i++) {
|
|
110
|
+
let prev = i + 1;
|
|
111
|
+
for (let j = 0; j < b.length; j++) {
|
|
112
|
+
const cur = a[i] === b[j] ? row[j] : Math.min(row[j], row[j + 1], prev) + 1;
|
|
113
|
+
row[j] = prev;
|
|
114
|
+
prev = cur;
|
|
115
|
+
}
|
|
116
|
+
row[b.length] = prev;
|
|
117
|
+
}
|
|
118
|
+
return row[b.length];
|
|
119
|
+
}
|
|
120
|
+
export function suggestClass(unknown, all) {
|
|
121
|
+
const needle = unknown.toLowerCase();
|
|
122
|
+
const maxDist = Math.max(3, Math.floor(unknown.length / 3));
|
|
123
|
+
const pool = all.filter((name) => !name.includes('@'));
|
|
124
|
+
let best;
|
|
125
|
+
for (const name of pool) {
|
|
126
|
+
const dist = levenshtein(needle, name.toLowerCase());
|
|
127
|
+
if (dist > maxDist)
|
|
128
|
+
continue;
|
|
129
|
+
if (!best || dist < best.dist || (dist === best.dist && name < best.name)) {
|
|
130
|
+
best = { name, dist };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return best?.name;
|
|
134
|
+
}
|
|
135
|
+
function resolveFromCwd(cwd, specifier) {
|
|
136
|
+
try {
|
|
137
|
+
return createRequire(path.join(cwd, 'package.json')).resolve(specifier);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
try {
|
|
141
|
+
return createRequire(import.meta.url).resolve(specifier);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function indexFromList(all) {
|
|
149
|
+
return { all, known: new Set(all) };
|
|
150
|
+
}
|
|
151
|
+
/** Prefer the linted project's @airframeui/core; fall back to the build-time snapshot. */
|
|
152
|
+
export function loadClassIndex(cwd) {
|
|
153
|
+
const cached = catalogCache.get(cwd);
|
|
154
|
+
if (cached)
|
|
155
|
+
return cached;
|
|
156
|
+
const file = resolveFromCwd(cwd, '@airframeui/core/classes.json');
|
|
157
|
+
if (file) {
|
|
158
|
+
try {
|
|
159
|
+
const doc = JSON.parse(readFileSync(file, 'utf8'));
|
|
160
|
+
const all = Array.isArray(doc.classes)
|
|
161
|
+
? doc.classes.filter((x) => typeof x === 'string')
|
|
162
|
+
: [];
|
|
163
|
+
if (all.length > 0) {
|
|
164
|
+
const index = indexFromList(all);
|
|
165
|
+
catalogCache.set(cwd, index);
|
|
166
|
+
return index;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// use bundled snapshot
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const index = indexFromList(loadBundledCatalog().classes);
|
|
174
|
+
catalogCache.set(cwd, index);
|
|
175
|
+
return index;
|
|
176
|
+
}
|
|
177
|
+
/** Prefer the linted project's @airframeui/tokens; fall back to the build-time snapshot. */
|
|
178
|
+
export function loadTokenIndex(cwd) {
|
|
179
|
+
const cached = tokenCache.get(cwd);
|
|
180
|
+
if (cached)
|
|
181
|
+
return cached;
|
|
182
|
+
const file = resolveFromCwd(cwd, '@airframeui/tokens/catalog.json');
|
|
183
|
+
if (file) {
|
|
184
|
+
try {
|
|
185
|
+
const doc = JSON.parse(readFileSync(file, 'utf8'));
|
|
186
|
+
const all = Array.isArray(doc.tokens)
|
|
187
|
+
? doc.tokens
|
|
188
|
+
.map((token) => token?.name)
|
|
189
|
+
.filter((name) => typeof name === 'string' && name.startsWith('--af-'))
|
|
190
|
+
: [];
|
|
191
|
+
if (all.length > 0) {
|
|
192
|
+
const index = { all, known: new Set(all) };
|
|
193
|
+
tokenCache.set(cwd, index);
|
|
194
|
+
return index;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// use bundled snapshot
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const all = loadBundledCatalog().tokens;
|
|
202
|
+
const index = { all, known: new Set(all) };
|
|
203
|
+
tokenCache.set(cwd, index);
|
|
204
|
+
return index;
|
|
205
|
+
}
|
|
206
|
+
export function resolveUnknownClass(token, known, all) {
|
|
207
|
+
const { base, suffix } = splitResponsive(token);
|
|
208
|
+
if (known.has(token))
|
|
209
|
+
return null;
|
|
210
|
+
const alias = getAlias(base);
|
|
211
|
+
if (alias) {
|
|
212
|
+
const parts = applySuffix(splitClasses(alias.replacement), suffix);
|
|
213
|
+
return { suggestion: parts.join(' '), note: alias.note, fixable: true };
|
|
214
|
+
}
|
|
215
|
+
const heuristic = heuristicReplacement(base, known);
|
|
216
|
+
if (heuristic) {
|
|
217
|
+
const parts = applySuffix(splitClasses(heuristic), suffix);
|
|
218
|
+
return { suggestion: parts.join(' '), fixable: true };
|
|
219
|
+
}
|
|
220
|
+
const closest = suggestClass(base, all);
|
|
221
|
+
return closest ? { suggestion: closest, fixable: false } : { fixable: false };
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=class-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"class-catalog.js","sourceRoot":"","sources":["../src/class-catalog.ts"],"names":[],"mappings":"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"}
|