web-components-doctor 0.1.0 → 0.3.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/README.md +150 -14
- package/dist/adapters/jsx-adapter.d.ts +20 -0
- package/dist/adapters/jsx-adapter.d.ts.map +1 -0
- package/dist/adapters/jsx-adapter.js +150 -0
- package/dist/adapters/jsx-adapter.js.map +1 -0
- package/dist/adapters/utils.d.ts +36 -0
- package/dist/adapters/utils.d.ts.map +1 -0
- package/dist/adapters/utils.js +58 -0
- package/dist/adapters/utils.js.map +1 -0
- package/dist/core/rule-factory.d.ts +10 -0
- package/dist/core/rule-factory.d.ts.map +1 -1
- package/dist/core/rule-factory.js +290 -144
- package/dist/core/rule-factory.js.map +1 -1
- package/dist/core/types.d.ts +12 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/descriptors/components.d.ts.map +1 -1
- package/dist/descriptors/components.js +47 -0
- package/dist/descriptors/components.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/rules/valid-slot-children.d.ts +13 -0
- package/dist/rules/valid-slot-children.d.ts.map +1 -0
- package/dist/rules/valid-slot-children.js +15 -0
- package/dist/rules/valid-slot-children.js.map +1 -0
- package/dist/rules/valid-slot-names.d.ts +13 -0
- package/dist/rules/valid-slot-names.d.ts.map +1 -0
- package/dist/rules/valid-slot-names.js +15 -0
- package/dist/rules/valid-slot-names.js.map +1 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@ npm install web-components-doctor --save-dev
|
|
|
10
10
|
|
|
11
11
|
## Quick Start (ESLint 9+ flat config)
|
|
12
12
|
|
|
13
|
+
### Lit (html tagged templates)
|
|
14
|
+
|
|
13
15
|
```js
|
|
14
16
|
// eslint.config.js
|
|
15
17
|
import swc from 'web-components-doctor';
|
|
@@ -19,6 +21,34 @@ export default [
|
|
|
19
21
|
];
|
|
20
22
|
```
|
|
21
23
|
|
|
24
|
+
### JSX / TSX (React, Preact, etc.)
|
|
25
|
+
|
|
26
|
+
The plugin automatically detects both syntaxes. Just enable JSX parsing in your ESLint config:
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
// eslint.config.js
|
|
30
|
+
import swc from 'web-components-doctor';
|
|
31
|
+
|
|
32
|
+
export default [
|
|
33
|
+
{
|
|
34
|
+
...swc.configs.recommended,
|
|
35
|
+
languageOptions: {
|
|
36
|
+
parserOptions: {
|
|
37
|
+
ecmaFeatures: { jsx: true },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Both kebab-case custom elements and PascalCase React wrappers are supported:
|
|
45
|
+
|
|
46
|
+
```jsx
|
|
47
|
+
// These are equivalent — both are checked
|
|
48
|
+
<sp-action-menu label="Actions"></sp-action-menu>
|
|
49
|
+
<SpActionMenu label="Actions"></SpActionMenu>
|
|
50
|
+
```
|
|
51
|
+
|
|
22
52
|
Or for strict CI enforcement:
|
|
23
53
|
|
|
24
54
|
```js
|
|
@@ -34,13 +64,21 @@ export default [
|
|
|
34
64
|
Require accessibility attributes on SWC elements.
|
|
35
65
|
|
|
36
66
|
```js
|
|
37
|
-
// ❌ Bad
|
|
67
|
+
// ❌ Bad — Lit
|
|
38
68
|
html`<sp-action-menu></sp-action-menu>`;
|
|
39
69
|
html`<sp-picker></sp-picker>`;
|
|
40
70
|
|
|
41
|
-
//
|
|
71
|
+
// ❌ Bad — JSX
|
|
72
|
+
<SpActionMenu></SpActionMenu>
|
|
73
|
+
<SpPicker></SpPicker>
|
|
74
|
+
|
|
75
|
+
// ✅ Good — Lit
|
|
42
76
|
html`<sp-action-menu label="More actions"></sp-action-menu>`;
|
|
43
77
|
html`<sp-picker aria-label="Select option"></sp-picker>`;
|
|
78
|
+
|
|
79
|
+
// ✅ Good — JSX
|
|
80
|
+
<SpActionMenu label="More actions"></SpActionMenu>
|
|
81
|
+
<SpPicker aria-label="Select option"></SpPicker>
|
|
44
82
|
```
|
|
45
83
|
|
|
46
84
|
| Element | Required (at least one of) |
|
|
@@ -60,13 +98,21 @@ html`<sp-picker aria-label="Select option"></sp-picker>`;
|
|
|
60
98
|
Flag deprecated attributes and attribute values.
|
|
61
99
|
|
|
62
100
|
```js
|
|
63
|
-
// ❌ Bad
|
|
101
|
+
// ❌ Bad — Lit
|
|
64
102
|
html`<sp-button variant="cta">Click</sp-button>`;
|
|
65
103
|
html`<sp-overlay allow-outside-click></sp-overlay>`;
|
|
66
104
|
|
|
67
|
-
//
|
|
105
|
+
// ❌ Bad — JSX
|
|
106
|
+
<SpButton variant="cta">Click</SpButton>
|
|
107
|
+
<SpOverlay allowOutsideClick></SpOverlay>
|
|
108
|
+
|
|
109
|
+
// ✅ Good — Lit
|
|
68
110
|
html`<sp-button variant="accent">Click</sp-button>`;
|
|
69
111
|
html`<sp-overlay></sp-overlay>`;
|
|
112
|
+
|
|
113
|
+
// ✅ Good — JSX
|
|
114
|
+
<SpButton variant="accent">Click</SpButton>
|
|
115
|
+
<SpOverlay></SpOverlay>
|
|
70
116
|
```
|
|
71
117
|
|
|
72
118
|
| Deprecated | Replacement |
|
|
@@ -106,21 +152,90 @@ html`<sp-theme color="light"></sp-theme>`;
|
|
|
106
152
|
html`<sp-button variant="accent">Click</sp-button>`;
|
|
107
153
|
```
|
|
108
154
|
|
|
155
|
+
### `swc/valid-slot-names`
|
|
156
|
+
|
|
157
|
+
Warn when a child element targets a slot that the parent component doesn't define.
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
// ❌ Bad — Lit
|
|
161
|
+
html`<sp-action-menu label="Actions">
|
|
162
|
+
<sp-menu-item slot="header">Edit</sp-menu-item>
|
|
163
|
+
</sp-action-menu>`;
|
|
164
|
+
|
|
165
|
+
// ❌ Bad — JSX
|
|
166
|
+
<SpActionMenu label="Actions">
|
|
167
|
+
<SpMenuItem slot="header">Edit</SpMenuItem>
|
|
168
|
+
</SpActionMenu>
|
|
169
|
+
|
|
170
|
+
// ✅ Good — Lit
|
|
171
|
+
html`<sp-action-menu label="Actions">
|
|
172
|
+
<sp-menu-item>Edit</sp-menu-item>
|
|
173
|
+
<sp-icon slot="icon"></sp-icon>
|
|
174
|
+
</sp-action-menu>`;
|
|
175
|
+
|
|
176
|
+
// ✅ Good — JSX
|
|
177
|
+
<SpActionMenu label="Actions">
|
|
178
|
+
<SpMenuItem>Edit</SpMenuItem>
|
|
179
|
+
<SpIcon slot="icon"></SpIcon>
|
|
180
|
+
</SpActionMenu>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### `swc/valid-slot-children`
|
|
184
|
+
|
|
185
|
+
Warn when a child element's tag is not accepted in the slot it targets.
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
// ❌ Bad — Lit (sp-action-menu default slot only accepts sp-menu-item/group/divider)
|
|
189
|
+
html`<sp-action-menu label="Actions">
|
|
190
|
+
<sp-button>Wrong child</sp-button>
|
|
191
|
+
</sp-action-menu>`;
|
|
192
|
+
|
|
193
|
+
// ❌ Bad — JSX
|
|
194
|
+
<SpActionMenu label="Actions">
|
|
195
|
+
<SpButton>Wrong child</SpButton>
|
|
196
|
+
</SpActionMenu>
|
|
197
|
+
|
|
198
|
+
// ✅ Good — Lit
|
|
199
|
+
html`<sp-action-menu label="Actions">
|
|
200
|
+
<sp-menu-item>Edit</sp-menu-item>
|
|
201
|
+
<sp-menu-divider></sp-menu-divider>
|
|
202
|
+
<sp-menu-item>Delete</sp-menu-item>
|
|
203
|
+
</sp-action-menu>`;
|
|
204
|
+
|
|
205
|
+
// ✅ Good — JSX
|
|
206
|
+
<SpActionMenu label="Actions">
|
|
207
|
+
<SpMenuItem>Edit</SpMenuItem>
|
|
208
|
+
<SpMenuDivider></SpMenuDivider>
|
|
209
|
+
<SpMenuItem>Delete</SpMenuItem>
|
|
210
|
+
</SpActionMenu>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
| Component | Default slot accepts | Named slots |
|
|
214
|
+
|---|---|---|
|
|
215
|
+
| `<sp-action-menu>` | `sp-menu-item`, `sp-menu-group`, `sp-menu-divider` | `icon`, `label`, `tooltip` |
|
|
216
|
+
| `<sp-picker>` | `sp-menu-item`, `sp-menu-group`, `sp-menu-divider` | `label`, `tooltip`, `description` |
|
|
217
|
+
| `<sp-tabs>` | `sp-tab`, `sp-tab-panel` | — |
|
|
218
|
+
| `<sp-button>` | (any) | `icon` (accepts `sp-icon`) |
|
|
219
|
+
| `<sp-dialog-wrapper>` | (any) | `hero`, `heading`, `button` (accepts `sp-button`) |
|
|
220
|
+
| `<overlay-trigger>` | (any) | `click-content`, `hover-content`, `longpress-content` |
|
|
221
|
+
|
|
109
222
|
## Architecture
|
|
110
223
|
|
|
111
224
|
This plugin is **data-driven**. Rules are generated from component descriptors rather than hand-written per component:
|
|
112
225
|
|
|
113
226
|
```
|
|
114
227
|
src/
|
|
115
|
-
├── adapters/
|
|
116
|
-
│
|
|
228
|
+
├── adapters/ # Template syntax parsers
|
|
229
|
+
│ ├── lit-adapter.ts # Extracts elements from html`` tagged templates
|
|
230
|
+
│ ├── jsx-adapter.ts # Extracts elements from JSX/TSX (PascalCase + kebab-case)
|
|
231
|
+
│ └── utils.ts # Shared utilities (pascalToKebab, camelToKebab, etc.)
|
|
117
232
|
├── core/
|
|
118
|
-
│ ├── types.ts
|
|
119
|
-
│ └── rule-factory.ts
|
|
233
|
+
│ ├── types.ts # Normalized IR (ParsedElement, descriptors)
|
|
234
|
+
│ └── rule-factory.ts # Generates ESLint rules from descriptors (dual-visitor)
|
|
120
235
|
├── descriptors/
|
|
121
|
-
│ └── components.ts
|
|
122
|
-
├── rules/
|
|
123
|
-
└── index.ts
|
|
236
|
+
│ └── components.ts # Component accessibility/deprecation metadata
|
|
237
|
+
├── rules/ # Thin wrappers: factory(descriptors)
|
|
238
|
+
└── index.ts # Plugin entry with recommended/strict configs
|
|
124
239
|
```
|
|
125
240
|
|
|
126
241
|
### Adding a new component
|
|
@@ -136,14 +251,32 @@ Add an entry to `src/descriptors/components.ts`:
|
|
|
136
251
|
validAttributeValues: {
|
|
137
252
|
variant: ['primary', 'secondary'],
|
|
138
253
|
},
|
|
254
|
+
slots: [
|
|
255
|
+
{ name: '', acceptedChildren: ['sp-menu-item'] },
|
|
256
|
+
{ name: 'icon', acceptedChildren: ['sp-icon'] },
|
|
257
|
+
],
|
|
139
258
|
}
|
|
140
259
|
```
|
|
141
260
|
|
|
142
261
|
No new rule code needed — the rule factory picks it up automatically.
|
|
143
262
|
|
|
144
|
-
###
|
|
263
|
+
### JSX naming conventions
|
|
264
|
+
|
|
265
|
+
The JSX adapter supports two usage patterns:
|
|
145
266
|
|
|
146
|
-
|
|
267
|
+
| Pattern | Example | Resolves to |
|
|
268
|
+
|---|---|---|
|
|
269
|
+
| Kebab-case (direct CE usage) | `<sp-action-menu>` | `sp-action-menu` |
|
|
270
|
+
| PascalCase (React wrapper) | `<SpActionMenu>` | `sp-action-menu` |
|
|
271
|
+
|
|
272
|
+
CamelCase props are automatically converted to their kebab-case attribute equivalents:
|
|
273
|
+
- `ariaLabel` → `aria-label`
|
|
274
|
+
- `isDecorative` → `is-decorative`
|
|
275
|
+
- `allowOutsideClick` → `allow-outside-click`
|
|
276
|
+
|
|
277
|
+
### Adding a new template syntax
|
|
278
|
+
|
|
279
|
+
Create a new adapter in `src/adapters/` that returns `ParsedElement[]` from the relevant AST node type, then register its visitor in `rule-factory.ts`. The rule logic is template-agnostic.
|
|
147
280
|
|
|
148
281
|
## Configs
|
|
149
282
|
|
|
@@ -157,8 +290,11 @@ Create a new adapter in `src/adapters/` that returns `ParsedElement[]` from the
|
|
|
157
290
|
The plugin **skips** attributes with dynamic template expressions since they can't be statically verified:
|
|
158
291
|
|
|
159
292
|
```js
|
|
160
|
-
// No warning —
|
|
293
|
+
// No warning — Lit dynamic value
|
|
161
294
|
html`<sp-action-menu label=${this.label}></sp-action-menu>`;
|
|
295
|
+
|
|
296
|
+
// No warning — JSX dynamic value
|
|
297
|
+
<SpActionMenu label={this.label}></SpActionMenu>
|
|
162
298
|
```
|
|
163
299
|
|
|
164
300
|
## Related
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
import type { Rule } from 'eslint';
|
|
13
|
+
import type { ParsedElement } from '../core/types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Extract a ParsedElement from a JSXElement AST node.
|
|
16
|
+
* Returns an empty array if not an SWC element.
|
|
17
|
+
* Recursively extracts direct children for slot validation.
|
|
18
|
+
*/
|
|
19
|
+
export declare function extractElementFromJSX(node: Rule.Node): ParsedElement[];
|
|
20
|
+
//# sourceMappingURL=jsx-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsx-adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/jsx-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAsKtE;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,GAAG,aAAa,EAAE,CAkCtE"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
import { camelToKebab, isSwcJsxTag, resolveJsxTagName } from './utils.js';
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a JSX element's name node to a plain string.
|
|
15
|
+
* Handles both `<SpButton>` (JSXIdentifier) and `<Sp.Button>` (JSXMemberExpression).
|
|
16
|
+
*/
|
|
17
|
+
function getJsxElementName(nameNode) {
|
|
18
|
+
if (nameNode.type === 'JSXIdentifier') {
|
|
19
|
+
return nameNode.name;
|
|
20
|
+
}
|
|
21
|
+
if (nameNode.type === 'JSXMemberExpression') {
|
|
22
|
+
const parts = [];
|
|
23
|
+
let current = nameNode;
|
|
24
|
+
while (current.type === 'JSXMemberExpression') {
|
|
25
|
+
parts.unshift(current.property.name);
|
|
26
|
+
current = current.object;
|
|
27
|
+
}
|
|
28
|
+
parts.unshift(current.name);
|
|
29
|
+
return parts.join('.');
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Determine if an attribute name is already kebab-case (contains a hyphen)
|
|
35
|
+
* meaning it doesn't need conversion.
|
|
36
|
+
*/
|
|
37
|
+
function isKebabCase(name) {
|
|
38
|
+
return name.includes('-');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Normalize a JSX attribute name to its HTML attribute equivalent.
|
|
42
|
+
* If the prop is already kebab-case (e.g. `aria-label`), keep as-is.
|
|
43
|
+
* Otherwise convert camelCase to kebab-case.
|
|
44
|
+
*/
|
|
45
|
+
function normalizeAttributeName(jsxPropName) {
|
|
46
|
+
if (isKebabCase(jsxPropName))
|
|
47
|
+
return jsxPropName;
|
|
48
|
+
return camelToKebab(jsxPropName);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Extract attributes from a JSXOpeningElement into a normalized map.
|
|
52
|
+
*/
|
|
53
|
+
function extractAttributes(opening) {
|
|
54
|
+
const attributes = new Map();
|
|
55
|
+
for (const attr of opening.attributes) {
|
|
56
|
+
if (attr.type === 'JSXSpreadAttribute') {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const propName = attr.name.name;
|
|
60
|
+
const htmlName = normalizeAttributeName(propName);
|
|
61
|
+
if (attr.value === null) {
|
|
62
|
+
attributes.set(htmlName, { value: '', isDynamic: false });
|
|
63
|
+
}
|
|
64
|
+
else if (attr.value.type === 'Literal') {
|
|
65
|
+
const raw = attr.value.value === null ? '' : String(attr.value.value);
|
|
66
|
+
attributes.set(htmlName, { value: raw, isDynamic: false });
|
|
67
|
+
}
|
|
68
|
+
else if (attr.value.type === 'JSXExpressionContainer') {
|
|
69
|
+
attributes.set(htmlName, { value: null, isDynamic: true });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return attributes;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Recursively extract child ParsedElements from JSX children.
|
|
76
|
+
* Includes all element children so slot validation can check non-SWC tags too.
|
|
77
|
+
*/
|
|
78
|
+
function extractChildElements(children) {
|
|
79
|
+
const results = [];
|
|
80
|
+
for (const child of children) {
|
|
81
|
+
if (child.type !== 'JSXElement' || !child.openingElement)
|
|
82
|
+
continue;
|
|
83
|
+
const rawName = getJsxElementName(child.openingElement.name);
|
|
84
|
+
if (!rawName)
|
|
85
|
+
continue;
|
|
86
|
+
const isSwc = isSwcJsxTag(rawName);
|
|
87
|
+
const tagName = isSwc ? resolveJsxTagName(rawName) : rawName;
|
|
88
|
+
const attributes = extractAttributes(child.openingElement);
|
|
89
|
+
const grandchildren = child.children
|
|
90
|
+
? extractChildElements(child.children)
|
|
91
|
+
: [];
|
|
92
|
+
let hasTextContent = false;
|
|
93
|
+
if (child.children) {
|
|
94
|
+
for (const grandchild of child.children) {
|
|
95
|
+
if (grandchild.type === 'JSXText') {
|
|
96
|
+
const text = (grandchild.value ?? '').trim();
|
|
97
|
+
if (text.length > 0) {
|
|
98
|
+
hasTextContent = true;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
results.push({
|
|
105
|
+
tagName,
|
|
106
|
+
attributes,
|
|
107
|
+
children: grandchildren,
|
|
108
|
+
hasTextContent,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return results;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Extract a ParsedElement from a JSXElement AST node.
|
|
115
|
+
* Returns an empty array if not an SWC element.
|
|
116
|
+
* Recursively extracts direct children for slot validation.
|
|
117
|
+
*/
|
|
118
|
+
export function extractElementFromJSX(node) {
|
|
119
|
+
const jsxNode = node;
|
|
120
|
+
const opening = jsxNode.openingElement;
|
|
121
|
+
const rawName = getJsxElementName(opening.name);
|
|
122
|
+
if (!rawName || !isSwcJsxTag(rawName))
|
|
123
|
+
return [];
|
|
124
|
+
const tagName = resolveJsxTagName(rawName);
|
|
125
|
+
const attributes = extractAttributes(opening);
|
|
126
|
+
const children = jsxNode.children
|
|
127
|
+
? extractChildElements(jsxNode.children)
|
|
128
|
+
: [];
|
|
129
|
+
let hasTextContent = false;
|
|
130
|
+
if (jsxNode.children) {
|
|
131
|
+
for (const child of jsxNode.children) {
|
|
132
|
+
if (child.type === 'JSXText') {
|
|
133
|
+
const text = (child.value ?? '').trim();
|
|
134
|
+
if (text.length > 0) {
|
|
135
|
+
hasTextContent = true;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return [
|
|
142
|
+
{
|
|
143
|
+
tagName,
|
|
144
|
+
attributes,
|
|
145
|
+
children,
|
|
146
|
+
hasTextContent,
|
|
147
|
+
},
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=jsx-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsx-adapter.js","sourceRoot":"","sources":["../../src/adapters/jsx-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Convert a PascalCase SWC React wrapper name to its kebab-case tag name.
|
|
14
|
+
* e.g. "SpActionMenu" -> "sp-action-menu", "SpProgressBar" -> "sp-progress-bar"
|
|
15
|
+
*/
|
|
16
|
+
export declare function pascalToKebab(name: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Convert a camelCase JSX prop name to its kebab-case HTML attribute equivalent.
|
|
19
|
+
* e.g. "ariaLabel" -> "aria-label", "isDecorative" -> "is-decorative"
|
|
20
|
+
*/
|
|
21
|
+
export declare function camelToKebab(name: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Check whether a kebab-case tag name is a recognized SWC element.
|
|
24
|
+
*/
|
|
25
|
+
export declare function isSwcTagName(tagName: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Check whether a JSX tag name looks like an SWC React wrapper (PascalCase starting with "Sp")
|
|
28
|
+
* or a custom element used directly in JSX (kebab-case starting with "sp-").
|
|
29
|
+
*/
|
|
30
|
+
export declare function isSwcJsxTag(name: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a JSX tag name to its canonical kebab-case SWC tag name.
|
|
33
|
+
* Handles both PascalCase wrappers and direct custom element usage.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveJsxTagName(name: string): string;
|
|
36
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/adapters/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKjD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAItD"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Convert a PascalCase SWC React wrapper name to its kebab-case tag name.
|
|
14
|
+
* e.g. "SpActionMenu" -> "sp-action-menu", "SpProgressBar" -> "sp-progress-bar"
|
|
15
|
+
*/
|
|
16
|
+
export function pascalToKebab(name) {
|
|
17
|
+
return name
|
|
18
|
+
.replace(/([A-Z])/g, (match, char, index) => index === 0 ? char.toLowerCase() : `-${char.toLowerCase()}`)
|
|
19
|
+
.replace(/^sp-/, 'sp-');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Convert a camelCase JSX prop name to its kebab-case HTML attribute equivalent.
|
|
23
|
+
* e.g. "ariaLabel" -> "aria-label", "isDecorative" -> "is-decorative"
|
|
24
|
+
*/
|
|
25
|
+
export function camelToKebab(name) {
|
|
26
|
+
return name.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check whether a kebab-case tag name is a recognized SWC element.
|
|
30
|
+
*/
|
|
31
|
+
export function isSwcTagName(tagName) {
|
|
32
|
+
return tagName.startsWith('sp-') || tagName === 'overlay-trigger';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check whether a JSX tag name looks like an SWC React wrapper (PascalCase starting with "Sp")
|
|
36
|
+
* or a custom element used directly in JSX (kebab-case starting with "sp-").
|
|
37
|
+
*/
|
|
38
|
+
export function isSwcJsxTag(name) {
|
|
39
|
+
if (name.startsWith('sp-') || name === 'overlay-trigger')
|
|
40
|
+
return true;
|
|
41
|
+
if (/^Sp[A-Z]/.test(name))
|
|
42
|
+
return true;
|
|
43
|
+
if (name === 'OverlayTrigger')
|
|
44
|
+
return true;
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a JSX tag name to its canonical kebab-case SWC tag name.
|
|
49
|
+
* Handles both PascalCase wrappers and direct custom element usage.
|
|
50
|
+
*/
|
|
51
|
+
export function resolveJsxTagName(name) {
|
|
52
|
+
if (name.startsWith('sp-') || name === 'overlay-trigger')
|
|
53
|
+
return name;
|
|
54
|
+
if (name === 'OverlayTrigger')
|
|
55
|
+
return 'overlay-trigger';
|
|
56
|
+
return pascalToKebab(name);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/adapters/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI;SACR,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAC1C,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAC5D;SACA,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,OAAO,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,iBAAiB,CAAC;AACpE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,iBAAiB;QAAE,OAAO,IAAI,CAAC;IACtE,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,IAAI,KAAK,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAC3C,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,iBAAiB;QAAE,OAAO,IAAI,CAAC;IACtE,IAAI,IAAI,KAAK,gBAAgB;QAAE,OAAO,iBAAiB,CAAC;IACxD,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -30,5 +30,15 @@ export declare function createRequiredAttributesRule(descriptors: ComponentDescr
|
|
|
30
30
|
* Creates the `valid-attribute-values` rule.
|
|
31
31
|
*/
|
|
32
32
|
export declare function createValidAttributeValuesRule(descriptors: ComponentDescriptorMap): RuleModule;
|
|
33
|
+
/**
|
|
34
|
+
* Creates the `valid-slot-names` rule that checks children aren't placed
|
|
35
|
+
* into non-existent slots.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createValidSlotNamesRule(descriptors: ComponentDescriptorMap): RuleModule;
|
|
38
|
+
/**
|
|
39
|
+
* Creates the `valid-slot-children` rule that checks a child element's tag
|
|
40
|
+
* is accepted in the slot it targets.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createValidSlotChildrenRule(descriptors: ComponentDescriptorMap): RuleModule;
|
|
33
43
|
export {};
|
|
34
44
|
//# sourceMappingURL=rule-factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-factory.d.ts","sourceRoot":"","sources":["../../src/core/rule-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAEV,sBAAsB,
|
|
1
|
+
{"version":3,"file":"rule-factory.d.ts","sourceRoot":"","sources":["../../src/core/rule-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAEV,sBAAsB,EAKvB,MAAM,YAAY,CAAC;AAIpB,KAAK,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;AAmClC;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,sBAAsB,GAClC,UAAU,CAyGZ;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,sBAAsB,GAClC,UAAU,CAyEZ;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,sBAAsB,GAClC,UAAU,CA8CZ;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,sBAAsB,GAClC,UAAU,CAwDZ;AAYD;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,sBAAsB,GAClC,UAAU,CAwDZ;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,sBAAsB,GAClC,UAAU,CA8DZ"}
|