@barefootjs/shared 0.18.2 → 0.18.4
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/dist/dom-prop.d.ts +5 -3
- package/dist/index.js +63 -0
- package/package.json +1 -1
- package/src/__tests__/dom-prop.test.ts +35 -8
- package/src/dom-prop.ts +82 -3
package/dist/dom-prop.d.ts
CHANGED
|
@@ -42,9 +42,11 @@ export declare function toHTMLAttrName(key: string): string;
|
|
|
42
42
|
* Runtime variant of attribute name mapping that additionally applies
|
|
43
43
|
* camelCase→kebab conversion for `data-*` and `aria-*` convenience
|
|
44
44
|
* props (e.g. `dataTestId` → `data-test-id`) and preserves SVG XML
|
|
45
|
-
* attribute casing.
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* attribute casing. HTML camelCase aliases resolve through the same
|
|
46
|
+
* `HTML_CAMEL_ALIASES` table as the compile-time variant (`tabIndex` →
|
|
47
|
+
* `tabindex`); generic kebab-casing still applies ONLY to `data-*` /
|
|
48
|
+
* `aria-*` — an unknown camelCase key passes through unchanged rather
|
|
49
|
+
* than being guessed into a hyphenated non-attribute.
|
|
48
50
|
*/
|
|
49
51
|
export declare function toHTMLAttrNameRuntime(key: string): string;
|
|
50
52
|
/**
|
package/dist/index.js
CHANGED
|
@@ -84,6 +84,63 @@ var SVG_CAMEL_TO_KEBAB = {
|
|
|
84
84
|
markerMid: "marker-mid",
|
|
85
85
|
markerEnd: "marker-end"
|
|
86
86
|
};
|
|
87
|
+
var HTML_CAMEL_ALIASES = {
|
|
88
|
+
acceptCharset: "accept-charset",
|
|
89
|
+
accessKey: "accesskey",
|
|
90
|
+
allowFullScreen: "allowfullscreen",
|
|
91
|
+
autoCapitalize: "autocapitalize",
|
|
92
|
+
autoComplete: "autocomplete",
|
|
93
|
+
autoCorrect: "autocorrect",
|
|
94
|
+
autoFocus: "autofocus",
|
|
95
|
+
autoPlay: "autoplay",
|
|
96
|
+
cellPadding: "cellpadding",
|
|
97
|
+
cellSpacing: "cellspacing",
|
|
98
|
+
charSet: "charset",
|
|
99
|
+
colSpan: "colspan",
|
|
100
|
+
contentEditable: "contenteditable",
|
|
101
|
+
controlsList: "controlslist",
|
|
102
|
+
crossOrigin: "crossorigin",
|
|
103
|
+
dateTime: "datetime",
|
|
104
|
+
dirName: "dirname",
|
|
105
|
+
encType: "enctype",
|
|
106
|
+
enterKeyHint: "enterkeyhint",
|
|
107
|
+
fetchPriority: "fetchpriority",
|
|
108
|
+
formAction: "formaction",
|
|
109
|
+
formEncType: "formenctype",
|
|
110
|
+
formMethod: "formmethod",
|
|
111
|
+
formNoValidate: "formnovalidate",
|
|
112
|
+
formTarget: "formtarget",
|
|
113
|
+
frameBorder: "frameborder",
|
|
114
|
+
hrefLang: "hreflang",
|
|
115
|
+
httpEquiv: "http-equiv",
|
|
116
|
+
imageSizes: "imagesizes",
|
|
117
|
+
imageSrcSet: "imagesrcset",
|
|
118
|
+
inputMode: "inputmode",
|
|
119
|
+
itemID: "itemid",
|
|
120
|
+
itemProp: "itemprop",
|
|
121
|
+
itemRef: "itemref",
|
|
122
|
+
itemScope: "itemscope",
|
|
123
|
+
itemType: "itemtype",
|
|
124
|
+
marginHeight: "marginheight",
|
|
125
|
+
marginWidth: "marginwidth",
|
|
126
|
+
maxLength: "maxlength",
|
|
127
|
+
minLength: "minlength",
|
|
128
|
+
noModule: "nomodule",
|
|
129
|
+
noValidate: "novalidate",
|
|
130
|
+
playsInline: "playsinline",
|
|
131
|
+
popoverTarget: "popovertarget",
|
|
132
|
+
popoverTargetAction: "popovertargetaction",
|
|
133
|
+
radioGroup: "radiogroup",
|
|
134
|
+
readOnly: "readonly",
|
|
135
|
+
referrerPolicy: "referrerpolicy",
|
|
136
|
+
rowSpan: "rowspan",
|
|
137
|
+
spellCheck: "spellcheck",
|
|
138
|
+
srcDoc: "srcdoc",
|
|
139
|
+
srcLang: "srclang",
|
|
140
|
+
srcSet: "srcset",
|
|
141
|
+
tabIndex: "tabindex",
|
|
142
|
+
useMap: "usemap"
|
|
143
|
+
};
|
|
87
144
|
var SVG_XML_CAMEL_ATTRS = new Set([
|
|
88
145
|
"allowReorder",
|
|
89
146
|
"attributeName",
|
|
@@ -178,6 +235,9 @@ function toHTMLAttrName(key) {
|
|
|
178
235
|
return "class";
|
|
179
236
|
if (key === "htmlFor")
|
|
180
237
|
return "for";
|
|
238
|
+
const htmlAlias = HTML_CAMEL_ALIASES[key];
|
|
239
|
+
if (htmlAlias !== undefined)
|
|
240
|
+
return htmlAlias;
|
|
181
241
|
const svgKebab = SVG_CAMEL_TO_KEBAB[key];
|
|
182
242
|
if (svgKebab !== undefined)
|
|
183
243
|
return svgKebab;
|
|
@@ -188,6 +248,9 @@ function toHTMLAttrNameRuntime(key) {
|
|
|
188
248
|
return "class";
|
|
189
249
|
if (key === "htmlFor")
|
|
190
250
|
return "for";
|
|
251
|
+
const htmlAlias = HTML_CAMEL_ALIASES[key];
|
|
252
|
+
if (htmlAlias !== undefined)
|
|
253
|
+
return htmlAlias;
|
|
191
254
|
const svgKebab = SVG_CAMEL_TO_KEBAB[key];
|
|
192
255
|
if (svgKebab !== undefined)
|
|
193
256
|
return svgKebab;
|
package/package.json
CHANGED
|
@@ -106,12 +106,27 @@ describe('toHTMLAttrName (compile-time)', () => {
|
|
|
106
106
|
expect(toHTMLAttrName('strokeWidth')).toBe('stroke-width')
|
|
107
107
|
})
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
// #2172: HTML camelCase aliases resolve through HTML_CAMEL_ALIASES —
|
|
110
|
+
// these are known HTML attributes with a defined lowercase spelling,
|
|
111
|
+
// NOT the generic kebab conversion (which stays data-*/aria-* only).
|
|
112
|
+
test('tabIndex lowers to tabindex (HTML alias table)', () => {
|
|
113
|
+
expect(toHTMLAttrName('tabIndex')).toBe('tabindex')
|
|
111
114
|
})
|
|
112
115
|
|
|
113
|
-
test('autoFocus
|
|
114
|
-
expect(toHTMLAttrName('autoFocus')).toBe('
|
|
116
|
+
test('autoFocus lowers to autofocus (HTML alias table)', () => {
|
|
117
|
+
expect(toHTMLAttrName('autoFocus')).toBe('autofocus')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test('readOnly lowers to the BOOLEAN_ATTRS member readonly', () => {
|
|
121
|
+
expect(toHTMLAttrName('readOnly')).toBe('readonly')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('spellCheck lowers to the enumerated (non-boolean) spellcheck', () => {
|
|
125
|
+
expect(toHTMLAttrName('spellCheck')).toBe('spellcheck')
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
test('unknown camelCase names still pass through unchanged', () => {
|
|
129
|
+
expect(toHTMLAttrName('myCustomAttr')).toBe('myCustomAttr')
|
|
115
130
|
})
|
|
116
131
|
})
|
|
117
132
|
|
|
@@ -144,12 +159,24 @@ describe('toHTMLAttrNameRuntime', () => {
|
|
|
144
159
|
expect(toHTMLAttrNameRuntime('ariaLabel')).toBe('aria-label')
|
|
145
160
|
})
|
|
146
161
|
|
|
147
|
-
test('tabIndex
|
|
148
|
-
expect(toHTMLAttrNameRuntime('tabIndex')).toBe('
|
|
162
|
+
test('tabIndex lowers to tabindex (HTML alias table, #2172)', () => {
|
|
163
|
+
expect(toHTMLAttrNameRuntime('tabIndex')).toBe('tabindex')
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
test('autoFocus lowers to autofocus (HTML alias table, #2172)', () => {
|
|
167
|
+
expect(toHTMLAttrNameRuntime('autoFocus')).toBe('autofocus')
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
test('readOnly lowers to the boolean attr spelling readonly (HTML alias table, #2172)', () => {
|
|
171
|
+
expect(toHTMLAttrNameRuntime('readOnly')).toBe('readonly')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
test('spellCheck lowers to the enumerated attr spelling spellcheck (HTML alias table, #2172)', () => {
|
|
175
|
+
expect(toHTMLAttrNameRuntime('spellCheck')).toBe('spellcheck')
|
|
149
176
|
})
|
|
150
177
|
|
|
151
|
-
test('
|
|
152
|
-
expect(toHTMLAttrNameRuntime('
|
|
178
|
+
test('unknown camelCase names still pass through unchanged', () => {
|
|
179
|
+
expect(toHTMLAttrNameRuntime('myCustomAttr')).toBe('myCustomAttr')
|
|
153
180
|
})
|
|
154
181
|
})
|
|
155
182
|
|
package/src/dom-prop.ts
CHANGED
|
@@ -72,6 +72,79 @@ const SVG_CAMEL_TO_KEBAB: Readonly<Record<string, string>> = {
|
|
|
72
72
|
markerEnd: 'marker-end',
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* HTML attributes written in React-style camelCase in JSX that map to a
|
|
77
|
+
* lowercase (or hyphenated) HTML attribute name. Mirrors React DOM's
|
|
78
|
+
* HTML attribute aliases. `className` / `htmlFor` are handled before
|
|
79
|
+
* this table (they predate it); everything here is a plain rename —
|
|
80
|
+
* value semantics are untouched (`readOnly` becomes the BOOLEAN_ATTRS
|
|
81
|
+
* member `readonly`; `spellCheck` stays the *enumerated* — NOT boolean
|
|
82
|
+
* — `spellcheck`).
|
|
83
|
+
*
|
|
84
|
+
* Consumed by `toHTMLAttrName` (compile-time Phase 1: `processAttributes`
|
|
85
|
+
* normalizes IRAttribute.name so adapters emit the HTML name as-is) and
|
|
86
|
+
* by `toHTMLAttrNameRuntime` (runtime spread paths). Names NOT in this
|
|
87
|
+
* table pass through unchanged, so `data-*`, `aria-*`, and custom-element
|
|
88
|
+
* attributes are never rewritten.
|
|
89
|
+
*/
|
|
90
|
+
const HTML_CAMEL_ALIASES: Readonly<Record<string, string>> = {
|
|
91
|
+
acceptCharset: 'accept-charset',
|
|
92
|
+
accessKey: 'accesskey',
|
|
93
|
+
allowFullScreen: 'allowfullscreen',
|
|
94
|
+
autoCapitalize: 'autocapitalize',
|
|
95
|
+
autoComplete: 'autocomplete',
|
|
96
|
+
autoCorrect: 'autocorrect',
|
|
97
|
+
autoFocus: 'autofocus',
|
|
98
|
+
autoPlay: 'autoplay',
|
|
99
|
+
cellPadding: 'cellpadding',
|
|
100
|
+
cellSpacing: 'cellspacing',
|
|
101
|
+
charSet: 'charset',
|
|
102
|
+
colSpan: 'colspan',
|
|
103
|
+
contentEditable: 'contenteditable',
|
|
104
|
+
controlsList: 'controlslist',
|
|
105
|
+
crossOrigin: 'crossorigin',
|
|
106
|
+
dateTime: 'datetime',
|
|
107
|
+
dirName: 'dirname',
|
|
108
|
+
encType: 'enctype',
|
|
109
|
+
enterKeyHint: 'enterkeyhint',
|
|
110
|
+
fetchPriority: 'fetchpriority',
|
|
111
|
+
formAction: 'formaction',
|
|
112
|
+
formEncType: 'formenctype',
|
|
113
|
+
formMethod: 'formmethod',
|
|
114
|
+
formNoValidate: 'formnovalidate',
|
|
115
|
+
formTarget: 'formtarget',
|
|
116
|
+
frameBorder: 'frameborder',
|
|
117
|
+
hrefLang: 'hreflang',
|
|
118
|
+
httpEquiv: 'http-equiv',
|
|
119
|
+
imageSizes: 'imagesizes',
|
|
120
|
+
imageSrcSet: 'imagesrcset',
|
|
121
|
+
inputMode: 'inputmode',
|
|
122
|
+
itemID: 'itemid',
|
|
123
|
+
itemProp: 'itemprop',
|
|
124
|
+
itemRef: 'itemref',
|
|
125
|
+
itemScope: 'itemscope',
|
|
126
|
+
itemType: 'itemtype',
|
|
127
|
+
marginHeight: 'marginheight',
|
|
128
|
+
marginWidth: 'marginwidth',
|
|
129
|
+
maxLength: 'maxlength',
|
|
130
|
+
minLength: 'minlength',
|
|
131
|
+
noModule: 'nomodule',
|
|
132
|
+
noValidate: 'novalidate',
|
|
133
|
+
playsInline: 'playsinline',
|
|
134
|
+
popoverTarget: 'popovertarget',
|
|
135
|
+
popoverTargetAction: 'popovertargetaction',
|
|
136
|
+
radioGroup: 'radiogroup',
|
|
137
|
+
readOnly: 'readonly',
|
|
138
|
+
referrerPolicy: 'referrerpolicy',
|
|
139
|
+
rowSpan: 'rowspan',
|
|
140
|
+
spellCheck: 'spellcheck',
|
|
141
|
+
srcDoc: 'srcdoc',
|
|
142
|
+
srcLang: 'srclang',
|
|
143
|
+
srcSet: 'srcset',
|
|
144
|
+
tabIndex: 'tabindex',
|
|
145
|
+
useMap: 'usemap',
|
|
146
|
+
}
|
|
147
|
+
|
|
75
148
|
/**
|
|
76
149
|
* SVG XML attribute names that are case-sensitive and MUST stay in camelCase.
|
|
77
150
|
*
|
|
@@ -169,6 +242,8 @@ export function classifyDOMProp(key: string): DOMPropClassification {
|
|
|
169
242
|
export function toHTMLAttrName(key: string): string {
|
|
170
243
|
if (key === 'className') return 'class'
|
|
171
244
|
if (key === 'htmlFor') return 'for'
|
|
245
|
+
const htmlAlias = HTML_CAMEL_ALIASES[key]
|
|
246
|
+
if (htmlAlias !== undefined) return htmlAlias
|
|
172
247
|
const svgKebab = SVG_CAMEL_TO_KEBAB[key]
|
|
173
248
|
if (svgKebab !== undefined) return svgKebab
|
|
174
249
|
return key
|
|
@@ -178,13 +253,17 @@ export function toHTMLAttrName(key: string): string {
|
|
|
178
253
|
* Runtime variant of attribute name mapping that additionally applies
|
|
179
254
|
* camelCase→kebab conversion for `data-*` and `aria-*` convenience
|
|
180
255
|
* props (e.g. `dataTestId` → `data-test-id`) and preserves SVG XML
|
|
181
|
-
* attribute casing.
|
|
182
|
-
*
|
|
183
|
-
*
|
|
256
|
+
* attribute casing. HTML camelCase aliases resolve through the same
|
|
257
|
+
* `HTML_CAMEL_ALIASES` table as the compile-time variant (`tabIndex` →
|
|
258
|
+
* `tabindex`); generic kebab-casing still applies ONLY to `data-*` /
|
|
259
|
+
* `aria-*` — an unknown camelCase key passes through unchanged rather
|
|
260
|
+
* than being guessed into a hyphenated non-attribute.
|
|
184
261
|
*/
|
|
185
262
|
export function toHTMLAttrNameRuntime(key: string): string {
|
|
186
263
|
if (key === 'className') return 'class'
|
|
187
264
|
if (key === 'htmlFor') return 'for'
|
|
265
|
+
const htmlAlias = HTML_CAMEL_ALIASES[key]
|
|
266
|
+
if (htmlAlias !== undefined) return htmlAlias
|
|
188
267
|
const svgKebab = SVG_CAMEL_TO_KEBAB[key]
|
|
189
268
|
if (svgKebab !== undefined) return svgKebab
|
|
190
269
|
if (SVG_XML_CAMEL_ATTRS.has(key)) return key
|