@wix/zero-config-implementation 1.29.0 → 1.31.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/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"registry": "https://registry.npmjs.org/",
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "1.
|
|
7
|
+
"version": "1.31.0",
|
|
8
8
|
"description": "Core library for extracting component manifests from JS and CSS files",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"main": "dist/index.js",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
]
|
|
84
84
|
}
|
|
85
85
|
},
|
|
86
|
-
"falconPackageHash": "
|
|
86
|
+
"falconPackageHash": "47671a9f6b5bba6b6a6a93ccf874198731eefd39404baf528141978b"
|
|
87
87
|
}
|
|
@@ -30,6 +30,15 @@ describe('formatDisplayName', () => {
|
|
|
30
30
|
)
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
+
it('drops leading words and result is less than 50 characters', () => {
|
|
34
|
+
// capitalCase('myComponentWithAVeryShortAndLongEnoughName')
|
|
35
|
+
// => "My Component With A Very Short And Long Enough Name" (51 chars)
|
|
36
|
+
// Drop "My" => "Component With A Very Short And Long Enough Name" (48 chars) < 50
|
|
37
|
+
const result = formatDisplayName('myComponentWithAVeryShortAndLongEnoughName')
|
|
38
|
+
expect(result).toBe('Component With A Very Short And Long Enough Name')
|
|
39
|
+
expect(result.length).toBe(48)
|
|
40
|
+
})
|
|
41
|
+
|
|
33
42
|
it('truncates at 50 characters when a single word exceeds the limit', () => {
|
|
34
43
|
// capitalCase of 51 repeated 'a' characters is a single word starting with uppercase
|
|
35
44
|
const longSingleWord = 'a'.repeat(51)
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Each element gets a semantic name computed by concatenating ancestor names.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { pascalCase } from 'case-anything'
|
|
8
|
+
import { camelCase, pascalCase } from 'case-anything'
|
|
9
9
|
import { type DefaultTreeAdapterMap, parseFragment } from 'parse5'
|
|
10
10
|
import { TRACE_ATTR } from '../../../../component-renderer'
|
|
11
11
|
import { findPreferredSemanticClass } from '../../../../utils/css-class'
|
|
@@ -34,59 +34,59 @@ export interface ExtractedElement {
|
|
|
34
34
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
35
35
|
|
|
36
36
|
const TAG_NAMES: Record<string, string> = {
|
|
37
|
-
a: '
|
|
38
|
-
abbr: '
|
|
39
|
-
article: '
|
|
40
|
-
aside: '
|
|
41
|
-
button: '
|
|
42
|
-
caption: '
|
|
43
|
-
col: '
|
|
44
|
-
colgroup: '
|
|
45
|
-
dd: '
|
|
46
|
-
details: '
|
|
47
|
-
div: '
|
|
48
|
-
dl: '
|
|
49
|
-
dt: '
|
|
50
|
-
fieldset: '
|
|
51
|
-
figcaption: '
|
|
52
|
-
figure: '
|
|
53
|
-
footer: '
|
|
54
|
-
form: '
|
|
55
|
-
h1: '
|
|
56
|
-
h2: '
|
|
57
|
-
h3: '
|
|
58
|
-
h4: '
|
|
59
|
-
h5: '
|
|
60
|
-
h6: '
|
|
61
|
-
header: '
|
|
62
|
-
hr: '
|
|
63
|
-
img: '
|
|
64
|
-
input: '
|
|
65
|
-
label: '
|
|
66
|
-
legend: '
|
|
67
|
-
li: '
|
|
68
|
-
main: '
|
|
69
|
-
nav: '
|
|
70
|
-
ol: '
|
|
71
|
-
optgroup: '
|
|
72
|
-
option: '
|
|
73
|
-
p: '
|
|
74
|
-
pre: '
|
|
75
|
-
progress: '
|
|
76
|
-
section: '
|
|
77
|
-
select: '
|
|
78
|
-
span: '
|
|
79
|
-
strong: '
|
|
80
|
-
summary: '
|
|
81
|
-
table: '
|
|
82
|
-
tbody: '
|
|
83
|
-
td: '
|
|
84
|
-
textarea: '
|
|
85
|
-
tfoot: '
|
|
86
|
-
th: '
|
|
87
|
-
thead: '
|
|
88
|
-
tr: '
|
|
89
|
-
ul: '
|
|
37
|
+
a: 'anchor',
|
|
38
|
+
abbr: 'abbreviation',
|
|
39
|
+
article: 'article',
|
|
40
|
+
aside: 'aside',
|
|
41
|
+
button: 'button',
|
|
42
|
+
caption: 'caption',
|
|
43
|
+
col: 'tableColumn',
|
|
44
|
+
colgroup: 'tableColumnGroup',
|
|
45
|
+
dd: 'descriptionDetails',
|
|
46
|
+
details: 'details',
|
|
47
|
+
div: 'div',
|
|
48
|
+
dl: 'descriptionList',
|
|
49
|
+
dt: 'descriptionTerm',
|
|
50
|
+
fieldset: 'fieldset',
|
|
51
|
+
figcaption: 'figureCaption',
|
|
52
|
+
figure: 'figure',
|
|
53
|
+
footer: 'footer',
|
|
54
|
+
form: 'form',
|
|
55
|
+
h1: 'heading1',
|
|
56
|
+
h2: 'heading2',
|
|
57
|
+
h3: 'heading3',
|
|
58
|
+
h4: 'heading4',
|
|
59
|
+
h5: 'heading5',
|
|
60
|
+
h6: 'heading6',
|
|
61
|
+
header: 'header',
|
|
62
|
+
hr: 'horizontalRule',
|
|
63
|
+
img: 'image',
|
|
64
|
+
input: 'input',
|
|
65
|
+
label: 'label',
|
|
66
|
+
legend: 'legend',
|
|
67
|
+
li: 'listItem',
|
|
68
|
+
main: 'main',
|
|
69
|
+
nav: 'navigation',
|
|
70
|
+
ol: 'orderedList',
|
|
71
|
+
optgroup: 'optionGroup',
|
|
72
|
+
option: 'option',
|
|
73
|
+
p: 'paragraph',
|
|
74
|
+
pre: 'preformatted',
|
|
75
|
+
progress: 'progress',
|
|
76
|
+
section: 'section',
|
|
77
|
+
select: 'select',
|
|
78
|
+
span: 'span',
|
|
79
|
+
strong: 'strong',
|
|
80
|
+
summary: 'summary',
|
|
81
|
+
table: 'table',
|
|
82
|
+
tbody: 'tableBody',
|
|
83
|
+
td: 'tableCell',
|
|
84
|
+
textarea: 'textArea',
|
|
85
|
+
tfoot: 'tableFooter',
|
|
86
|
+
th: 'tableHeader',
|
|
87
|
+
thead: 'tableHead',
|
|
88
|
+
tr: 'tableRow',
|
|
89
|
+
ul: 'unorderedList',
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -118,10 +118,10 @@ const hasDirectTextContent = (element: Element): boolean =>
|
|
|
118
118
|
element.childNodes.some((child) => isTextNode(child) && child.value.trim().length > 0)
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
|
-
* Normalizes a tag name to its semantic
|
|
121
|
+
* Normalizes a tag name to its semantic camelCase form
|
|
122
122
|
*/
|
|
123
123
|
function normalizeTagName(tag: string): string {
|
|
124
|
-
return TAG_NAMES[tag.toLowerCase()] ??
|
|
124
|
+
return TAG_NAMES[tag.toLowerCase()] ?? camelCase(tag)
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
/**
|
|
@@ -148,16 +148,17 @@ function getTextContent(element: Element): string {
|
|
|
148
148
|
* Priority: id > aria-label > aria-labelledby > normalized tag name
|
|
149
149
|
*/
|
|
150
150
|
function getElementNamePart(element: Element, getElementById: (id: string) => Element | undefined): string {
|
|
151
|
+
let elementName: string | undefined
|
|
151
152
|
const id = getAttribute(element, 'id')
|
|
152
153
|
// Skip spy-instrumented ids (mock_propName_XXXXXX) — they are runtime mock values,
|
|
153
154
|
// not semantic identifiers, and would produce garbage element names.
|
|
154
155
|
if (id && !id.includes('mock_')) {
|
|
155
|
-
|
|
156
|
+
elementName = id
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
const ariaLabel = getAttribute(element, 'aria-label')
|
|
159
160
|
if (ariaLabel && !ariaLabel.startsWith('mock_')) {
|
|
160
|
-
|
|
161
|
+
elementName = ariaLabel
|
|
161
162
|
}
|
|
162
163
|
|
|
163
164
|
const ariaLabelledBy = getAttribute(element, 'aria-labelledby')
|
|
@@ -166,7 +167,7 @@ function getElementNamePart(element: Element, getElementById: (id: string) => El
|
|
|
166
167
|
if (labelElement) {
|
|
167
168
|
const labelText = getTextContent(labelElement)
|
|
168
169
|
if (labelText) {
|
|
169
|
-
|
|
170
|
+
elementName = labelText
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
173
|
}
|
|
@@ -174,7 +175,11 @@ function getElementNamePart(element: Element, getElementById: (id: string) => El
|
|
|
174
175
|
const classAttr = getAttribute(element, 'class')
|
|
175
176
|
if (classAttr) {
|
|
176
177
|
const semanticClass = findPreferredSemanticClass(classAttr.split(' '))
|
|
177
|
-
if (semanticClass)
|
|
178
|
+
if (semanticClass) elementName = semanticClass
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (elementName) {
|
|
182
|
+
return camelCase(elementName)
|
|
178
183
|
}
|
|
179
184
|
|
|
180
185
|
return normalizeTagName(element.tagName)
|
|
@@ -268,8 +273,10 @@ export function buildElementTree(html: string, store: ExtractorStore): Extracted
|
|
|
268
273
|
// Compute this element's name part
|
|
269
274
|
const namePart = isRoot ? 'root' : getElementNamePart(node, getElementById)
|
|
270
275
|
|
|
271
|
-
// Full name is ancestor path + this element's name (no separator)
|
|
272
|
-
|
|
276
|
+
// Full name is ancestor path + this element's name (no separator).
|
|
277
|
+
// Use pascalCase on namePart when appending so that
|
|
278
|
+
// e.g. "mediaSection" + "vectorArt" → "mediaSectionVectorArt".
|
|
279
|
+
const name = isRoot ? 'root' : ancestorPath.length > 0 ? ancestorPath + pascalCase(namePart) : namePart
|
|
273
280
|
|
|
274
281
|
// Check for text content
|
|
275
282
|
const hasText = hasDirectTextContent(node)
|