@wix/zero-config-implementation 1.24.0 → 1.26.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/dist/index.js +3284 -3267
- package/dist/information-extractors/css/types.d.ts +2 -0
- package/dist/manifest-pipeline.d.ts +1 -0
- package/package.json +2 -2
- package/src/information-extractors/css/parse.ts +3 -2
- package/src/information-extractors/css/selector-matcher.ts +46 -21
- package/src/information-extractors/css/types.ts +2 -0
- package/src/information-extractors/react/extractors/css-properties.ts +34 -24
- package/src/manifest-pipeline.ts +2 -0
- package/src/module-loader.ts +2 -7
|
@@ -14,6 +14,7 @@ export interface ExtractedCssInfo {
|
|
|
14
14
|
api: CSSParserAPI;
|
|
15
15
|
properties: Map<string, string>;
|
|
16
16
|
customProperties: Map<string, string>;
|
|
17
|
+
isCssModule: boolean;
|
|
17
18
|
}
|
|
18
19
|
export interface ComponentInfoWithCss extends CoupledComponentInfo {
|
|
19
20
|
css: ExtractedCssInfo[];
|
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.26.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": "3dd0aab1018f95841806376f73376b94c1dcf15d586caa70806a01f8"
|
|
87
87
|
}
|
|
@@ -345,7 +345,8 @@ function extractPropertyNameAndValue(decl: LightningDecl): CSSProperty | null {
|
|
|
345
345
|
if (name) {
|
|
346
346
|
const value = propertyValueToString(decl)
|
|
347
347
|
if (value) {
|
|
348
|
-
|
|
348
|
+
const varRefs = extractVarNamesFromTokens(unparsedValue.value ?? [])
|
|
349
|
+
return { name, value, ...(varRefs.length > 0 && { varRefs }) }
|
|
349
350
|
}
|
|
350
351
|
}
|
|
351
352
|
return null
|
|
@@ -412,7 +413,7 @@ function serializeCustomPropertyValue(valueArray: unknown[]): string {
|
|
|
412
413
|
} else if (tokenValue.type === 'dimension') {
|
|
413
414
|
parts.push(`${tokenValue.value}${tokenValue.unit}`)
|
|
414
415
|
} else if (tokenValue.type === 'white-space') {
|
|
415
|
-
parts.
|
|
416
|
+
// skip — parts.join(' ') already handles separation
|
|
416
417
|
}
|
|
417
418
|
}
|
|
418
419
|
}
|
|
@@ -4,6 +4,45 @@ import type { ExtractedCssInfo } from '../../index'
|
|
|
4
4
|
import type { ExtractedElement } from '../react/extractors/core/tree-builder'
|
|
5
5
|
import type { CSSProperty, CssSelectorMatch, MatchedCssData } from './types'
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Matches selectors composed entirely of one or more class selectors with no combinators,
|
|
9
|
+
* pseudo-classes, tag names, or IDs — e.g. `.wrapper`, `.wrapper.active`.
|
|
10
|
+
* Used to identify selectors eligible for CSS module prefix matching.
|
|
11
|
+
*/
|
|
12
|
+
const SIMPLE_CLASS_SELECTOR_PATTERN = /^(\.[a-zA-Z][\w-]*)+$/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Finds DOM elements matching a CSS selector, with special handling for CSS module files.
|
|
16
|
+
*
|
|
17
|
+
* For CSS module files, class names in the source CSS (e.g. `.wrapper`) are hashed at build
|
|
18
|
+
* time into names like `wrapper_AbCdE`. When the selector is a simple class selector
|
|
19
|
+
* (e.g. `.wrapper` or `.wrapper.active`), this function matches elements whose class list
|
|
20
|
+
* contains a class that exactly equals the local name OR starts with `localName_` /
|
|
21
|
+
* `_localName_` (the two most common Vite CSS module hash patterns).
|
|
22
|
+
*
|
|
23
|
+
* For non-module files, or for selectors that contain combinators or non-class parts
|
|
24
|
+
* (e.g. `div .wrapper`, `#id`), falls back to a direct Cheerio query.
|
|
25
|
+
*/
|
|
26
|
+
function findMatchingElements($: cheerio.CheerioAPI, domSelector: string, isCssModule: boolean) {
|
|
27
|
+
if (isCssModule && SIMPLE_CLASS_SELECTOR_PATTERN.test(domSelector)) {
|
|
28
|
+
const classNames = domSelector.split('.').filter(Boolean)
|
|
29
|
+
return $('[class]').filter((_index, element) => {
|
|
30
|
+
const classAttr = $(element).attr('class')
|
|
31
|
+
if (!classAttr) return false
|
|
32
|
+
const elementClasses = classAttr.trim().split(/\s+/)
|
|
33
|
+
return classNames.every((localName) =>
|
|
34
|
+
elementClasses.some(
|
|
35
|
+
(elementClass) =>
|
|
36
|
+
elementClass === localName ||
|
|
37
|
+
elementClass.startsWith(`${localName}_`) ||
|
|
38
|
+
elementClass.startsWith(`_${localName}_`),
|
|
39
|
+
),
|
|
40
|
+
)
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
return $(domSelector)
|
|
44
|
+
}
|
|
45
|
+
|
|
7
46
|
export function matchCssSelectors(
|
|
8
47
|
html: string,
|
|
9
48
|
elements: ExtractedElement[],
|
|
@@ -28,8 +67,8 @@ export function matchCssSelectors(
|
|
|
28
67
|
if (!domSelector) continue
|
|
29
68
|
|
|
30
69
|
try {
|
|
31
|
-
|
|
32
|
-
const traceId = $(
|
|
70
|
+
findMatchingElements($, domSelector, cssInfo.isCssModule).each((_index, element) => {
|
|
71
|
+
const traceId = $(element).attr(TRACE_ATTR)
|
|
33
72
|
if (!traceId) return
|
|
34
73
|
|
|
35
74
|
// Use reduce to separate regular and custom properties in one pass
|
|
@@ -37,13 +76,13 @@ export function matchCssSelectors(
|
|
|
37
76
|
regular: CSSProperty[]
|
|
38
77
|
custom: Record<string, string>
|
|
39
78
|
}>(
|
|
40
|
-
(
|
|
79
|
+
(accumulator, prop) => {
|
|
41
80
|
if (prop.name.startsWith('--')) {
|
|
42
|
-
|
|
81
|
+
accumulator.custom[prop.name] = prop.value
|
|
43
82
|
} else {
|
|
44
|
-
|
|
83
|
+
accumulator.regular.push(prop)
|
|
45
84
|
}
|
|
46
|
-
return
|
|
85
|
+
return accumulator
|
|
47
86
|
},
|
|
48
87
|
{ regular: [], custom: {} },
|
|
49
88
|
)
|
|
@@ -55,7 +94,7 @@ export function matchCssSelectors(
|
|
|
55
94
|
|
|
56
95
|
// Track which CSS custom properties are used (via var()) by this element
|
|
57
96
|
for (const regularProp of regular) {
|
|
58
|
-
for (const varName of
|
|
97
|
+
for (const varName of regularProp.varRefs ?? []) {
|
|
59
98
|
const traceIdSet = varUsedByTraceId.get(varName) ?? new Set()
|
|
60
99
|
traceIdSet.add(traceId)
|
|
61
100
|
varUsedByTraceId.set(varName, traceIdSet)
|
|
@@ -81,20 +120,6 @@ export function matchCssSelectors(
|
|
|
81
120
|
}
|
|
82
121
|
}
|
|
83
122
|
|
|
84
|
-
/**
|
|
85
|
-
* Extracts all CSS custom property names referenced via var() in a property value string.
|
|
86
|
-
*/
|
|
87
|
-
function extractVarRefs(value: string): string[] {
|
|
88
|
-
const varNames: string[] = []
|
|
89
|
-
const varPattern = /var\(\s*(--[\w-]+)/g
|
|
90
|
-
let varMatch = varPattern.exec(value)
|
|
91
|
-
while (varMatch !== null) {
|
|
92
|
-
varNames.push(varMatch[1])
|
|
93
|
-
varMatch = varPattern.exec(value)
|
|
94
|
-
}
|
|
95
|
-
return varNames
|
|
96
|
-
}
|
|
97
|
-
|
|
98
123
|
function enrichElements(
|
|
99
124
|
elements: ExtractedElement[],
|
|
100
125
|
matchesByTraceId: Map<string, CssSelectorMatch[]>,
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* based on its tag, role, and whether it has text content.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { CSS_PROPERTIES } from '@wix/zero-config-schema'
|
|
8
9
|
import type { CreateElementEvent, ReactExtractor } from './core/types'
|
|
9
10
|
|
|
10
11
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -62,41 +63,50 @@ const INTERACTIVE_TAGS = new Set(['button', 'input', 'textarea', 'select'])
|
|
|
62
63
|
/**
|
|
63
64
|
* CSS properties for Layout Containers and Interactive Elements
|
|
64
65
|
*/
|
|
66
|
+
const { CSS_PROPERTY_TYPE } = CSS_PROPERTIES
|
|
67
|
+
|
|
65
68
|
const CONTAINER_CSS_PROPERTIES = [
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
69
|
+
CSS_PROPERTY_TYPE.background,
|
|
70
|
+
CSS_PROPERTY_TYPE.borderTop,
|
|
71
|
+
CSS_PROPERTY_TYPE.borderBottom,
|
|
72
|
+
CSS_PROPERTY_TYPE.borderInlineStart,
|
|
73
|
+
CSS_PROPERTY_TYPE.borderInlineEnd,
|
|
74
|
+
CSS_PROPERTY_TYPE.paddingTop,
|
|
75
|
+
CSS_PROPERTY_TYPE.paddingBottom,
|
|
76
|
+
CSS_PROPERTY_TYPE.paddingInlineStart,
|
|
77
|
+
CSS_PROPERTY_TYPE.paddingInlineEnd,
|
|
78
|
+
CSS_PROPERTY_TYPE.borderStartStartRadius,
|
|
79
|
+
CSS_PROPERTY_TYPE.borderStartEndRadius,
|
|
80
|
+
CSS_PROPERTY_TYPE.borderEndStartRadius,
|
|
81
|
+
CSS_PROPERTY_TYPE.borderEndEndRadius,
|
|
82
|
+
CSS_PROPERTY_TYPE.boxShadow,
|
|
80
83
|
]
|
|
81
84
|
|
|
82
85
|
/**
|
|
83
86
|
* CSS properties for Text elements and leaf elements with textContent
|
|
84
87
|
*/
|
|
85
|
-
const TEXT_CSS_PROPERTIES = [
|
|
88
|
+
const TEXT_CSS_PROPERTIES = [
|
|
89
|
+
CSS_PROPERTY_TYPE.font,
|
|
90
|
+
CSS_PROPERTY_TYPE.lineHeight,
|
|
91
|
+
CSS_PROPERTY_TYPE.letterSpacing,
|
|
92
|
+
CSS_PROPERTY_TYPE.textDecorationLine,
|
|
93
|
+
CSS_PROPERTY_TYPE.textTransform,
|
|
94
|
+
CSS_PROPERTY_TYPE.color,
|
|
95
|
+
]
|
|
86
96
|
|
|
87
97
|
/**
|
|
88
98
|
* CSS properties for Media elements (excluding svg)
|
|
89
99
|
*/
|
|
90
100
|
const MEDIA_CSS_PROPERTIES = [
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
101
|
+
CSS_PROPERTY_TYPE.borderTop,
|
|
102
|
+
CSS_PROPERTY_TYPE.borderBottom,
|
|
103
|
+
CSS_PROPERTY_TYPE.borderInlineStart,
|
|
104
|
+
CSS_PROPERTY_TYPE.borderInlineEnd,
|
|
105
|
+
CSS_PROPERTY_TYPE.borderStartStartRadius,
|
|
106
|
+
CSS_PROPERTY_TYPE.borderStartEndRadius,
|
|
107
|
+
CSS_PROPERTY_TYPE.borderEndStartRadius,
|
|
108
|
+
CSS_PROPERTY_TYPE.borderEndEndRadius,
|
|
109
|
+
CSS_PROPERTY_TYPE.boxShadow,
|
|
100
110
|
]
|
|
101
111
|
|
|
102
112
|
// ─────────────────────────────────────────────────────────────────────────────
|
package/src/manifest-pipeline.ts
CHANGED
|
@@ -36,6 +36,7 @@ export interface ExtractedCssInfo {
|
|
|
36
36
|
api: CSSParserAPI
|
|
37
37
|
properties: Map<string, string>
|
|
38
38
|
customProperties: Map<string, string>
|
|
39
|
+
isCssModule: boolean
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
export interface ComponentInfoWithCss extends CoupledComponentInfo {
|
|
@@ -378,6 +379,7 @@ function extractCssInfo(
|
|
|
378
379
|
api,
|
|
379
380
|
properties,
|
|
380
381
|
customProperties,
|
|
382
|
+
isCssModule: /\.module\.(css|scss|sass)$/.test(cssPath),
|
|
381
383
|
})
|
|
382
384
|
} catch (error) {
|
|
383
385
|
warnings.push({
|
package/src/module-loader.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'
|
|
2
|
-
import { pascalCase } from 'case-anything'
|
|
3
2
|
import { ResultAsync, errAsync, okAsync } from 'neverthrow'
|
|
4
3
|
import type { ComponentType } from 'react'
|
|
5
4
|
import { IoError } from './errors'
|
|
@@ -37,12 +36,8 @@ export function loadModule(entryPath: string): ResultAsync<Record<string, unknow
|
|
|
37
36
|
})
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
function isPascalCase(name: string): boolean {
|
|
41
|
-
return name.length > 0 && pascalCase(name) === name
|
|
42
|
-
}
|
|
43
|
-
|
|
44
39
|
function isComponent(value: unknown): value is ComponentType<unknown> {
|
|
45
|
-
if (typeof value === 'function') return
|
|
40
|
+
if (typeof value === 'function') return true
|
|
46
41
|
// React.memo() and React.forwardRef() return objects, not functions
|
|
47
42
|
if (typeof value === 'object' && value !== null && '$$typeof' in value) return true
|
|
48
43
|
return false
|
|
@@ -51,7 +46,7 @@ function isComponent(value: unknown): value is ComponentType<unknown> {
|
|
|
51
46
|
export function findComponent(moduleExports: Record<string, unknown>, name: string): ComponentType<unknown> | null {
|
|
52
47
|
// Direct named export
|
|
53
48
|
const direct = moduleExports[name]
|
|
54
|
-
if (
|
|
49
|
+
if (isComponent(direct)) {
|
|
55
50
|
return direct as ComponentType<unknown>
|
|
56
51
|
}
|
|
57
52
|
|