@wix/zero-config-implementation 1.45.0 → 1.47.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.45.0",
7
+ "version": "1.47.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",
@@ -84,5 +84,5 @@
84
84
  ]
85
85
  }
86
86
  },
87
- "falconPackageHash": "351d3032ce32fe0b9f5864b3e86e133388202d091c298071ef24a7e0"
87
+ "falconPackageHash": "3524e32da30b9a43b1f548d22b8e065abea7ce993f23ae0f52abc674"
88
88
  }
@@ -2,11 +2,12 @@ import type {
2
2
  CssCustomPropertyItem,
3
3
  CssPropertyItem,
4
4
  DataItem,
5
+ Display,
5
6
  EditorElement,
6
7
  EditorReactComponent,
7
8
  ElementItem,
8
9
  } from '@wix/react-component-schema'
9
- import { ELEMENTS } from '@wix/react-component-schema'
10
+ import { CSS_PROPERTIES, ELEMENTS } from '@wix/react-component-schema'
10
11
  import type { ComponentInfoWithCss } from '../index'
11
12
  import type { MatchedCssData } from '../information-extractors/css/types'
12
13
  import type {
@@ -16,6 +17,7 @@ import type {
16
17
  ExtractedElement,
17
18
  TrackingStores,
18
19
  } from '../information-extractors/react'
20
+ import { getDefaultDisplayForTag, resolveDisplayValue } from '../information-extractors/react'
19
21
  import { findPreferredSemanticClass } from '../utils/css-class'
20
22
  import { buildDataItem } from './data-item-builder'
21
23
  import { formatDisplayName } from './utils'
@@ -102,6 +104,7 @@ function buildElements(
102
104
  inlineElement: {
103
105
  selector: buildSelector(element),
104
106
  displayName: formatDisplayName(element.name),
107
+ behaviors: { removable: true, selectable: false },
105
108
  // Add data from inner element props if available
106
109
  ...(data && Object.keys(data).length > 0 && { data }),
107
110
  // CSS properties from heuristic + matched CSS files
@@ -308,10 +311,24 @@ function getMatchedPropertyValues(element: ExtractedElement): Map<string, string
308
311
  return values
309
312
  }
310
313
 
314
+ const CSS_DISPLAY_TO_ENUM: Record<string, string> = {
315
+ 'inline-block': 'inlineBlock',
316
+ 'inline-flex': 'inlineFlex',
317
+ 'inline-grid': 'inlineGrid',
318
+ 'inline-table': 'inlineTable',
319
+ 'list-item': 'listItem',
320
+ 'flow-root': 'flowRoot',
321
+ }
322
+
323
+ function toDisplayEnumValue(cssValue: string): NonNullable<Display['displayValues']>[number] {
324
+ return (CSS_DISPLAY_TO_ENUM[cssValue] ?? cssValue) as NonNullable<Display['displayValues']>[number]
325
+ }
326
+
327
+ const { CSS_PROPERTY_TYPE, DISPLAY_VALUE } = CSS_PROPERTIES
328
+
311
329
  function buildCssProperties(element: ExtractedElement | undefined): Record<string, CssPropertyItem> {
312
330
  const result: Record<string, CssPropertyItem> = {}
313
331
 
314
- // Get the CSS properties decided by the heuristic
315
332
  const cssData = element?.extractorData.get('css-properties') as CssPropertiesData | undefined
316
333
  const decidedProperties = cssData?.relevant
317
334
  if (!decidedProperties || decidedProperties.length === 0) {
@@ -319,12 +336,28 @@ function buildCssProperties(element: ExtractedElement | undefined): Record<strin
319
336
  }
320
337
  const cssPropertyValues = element ? getMatchedPropertyValues(element) : new Map<string, string>()
321
338
  for (const propName of decidedProperties) {
339
+ if (propName === CSS_PROPERTY_TYPE.display && element) {
340
+ result[propName] = buildDisplayProperty(element)
341
+ continue
342
+ }
322
343
  const defaultValue = cssPropertyValues.get(propName)
323
344
  result[propName] = {
324
- // Only include defaultValue if found in CSS files
325
345
  ...(defaultValue !== undefined && { defaultValue }),
326
346
  }
327
347
  }
328
348
 
329
349
  return result
330
350
  }
351
+
352
+ function buildDisplayProperty(element: ExtractedElement): CssPropertyItem {
353
+ const matcherData = element.extractorData.get('css-matcher') as MatchedCssData | undefined
354
+ const resolvedFromCss = matcherData ? resolveDisplayValue(matcherData) : undefined
355
+ const currentValue = resolvedFromCss ?? getDefaultDisplayForTag(element.tag)
356
+ const currentEnumValue = toDisplayEnumValue(currentValue)
357
+
358
+ return {
359
+ display: {
360
+ displayValues: [DISPLAY_VALUE.none, currentEnumValue],
361
+ },
362
+ }
363
+ }
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { parseCss } from '../../css/parse'
3
+ import type { MatchedCssData } from '../../css/types'
4
+ import { hasFlexOrGridDisplay } from './css-properties'
5
+
6
+ function matcherDataFromCss(declarations: string): MatchedCssData {
7
+ const properties = parseCss(`.test { ${declarations} }`).getPropertiesForSelector('.test')
8
+ const customProperties: Record<string, string> = {}
9
+ for (const property of properties) {
10
+ if (property.name.startsWith('--')) {
11
+ customProperties[property.name] = property.value
12
+ }
13
+ }
14
+ return {
15
+ matches: [{ selector: '.test', properties: properties.filter((property) => !property.name.startsWith('--')) }],
16
+ customProperties,
17
+ }
18
+ }
19
+
20
+ describe('hasFlexOrGridDisplay', () => {
21
+ it('returns true for display: flex', () => {
22
+ expect(hasFlexOrGridDisplay(matcherDataFromCss('display: flex'))).toBe(true)
23
+ })
24
+
25
+ it('returns false for display: block', () => {
26
+ expect(hasFlexOrGridDisplay(matcherDataFromCss('display: block'))).toBe(false)
27
+ })
28
+
29
+ it('returns false when no properties', () => {
30
+ expect(hasFlexOrGridDisplay(matcherDataFromCss(''))).toBe(false)
31
+ })
32
+
33
+ it('returns false when var resolves to a non-flex/grid value', () => {
34
+ expect(hasFlexOrGridDisplay(matcherDataFromCss('display: var(--d); --d: block'))).toBe(false)
35
+ })
36
+
37
+ it('returns true when var resolves to grid', () => {
38
+ expect(hasFlexOrGridDisplay(matcherDataFromCss('display: var(--d); --d: grid'))).toBe(true)
39
+ })
40
+
41
+ it('returns true when var cannot be resolved', () => {
42
+ expect(hasFlexOrGridDisplay(matcherDataFromCss('display: var(--d)'))).toBe(true)
43
+ })
44
+ })
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { CSS_PROPERTIES } from '@wix/react-component-schema'
9
- import type { CssSelectorMatch, MatchedCssData } from '../../css/types'
9
+ import type { MatchedCssData } from '../../css/types'
10
10
  import type { ExtractedElement } from './core/tree-builder'
11
11
  import type { CreateElementEvent, ReactExtractor } from './core/types'
12
12
 
@@ -186,6 +186,7 @@ export function getCssPropertiesForTag(tag: string, role?: string): string[] {
186
186
  break
187
187
  }
188
188
 
189
+ properties.push(CSS_PROPERTY_TYPE.display)
189
190
  return properties
190
191
  }
191
192
 
@@ -211,23 +212,85 @@ export function addGapProperty(existing: string[]): string[] {
211
212
  return [...existing, CSS_PROPERTY_TYPE.gap]
212
213
  }
213
214
 
215
+ // ─────────────────────────────────────────────────────────────────────────────
216
+ // Display Value Resolution
217
+ // ─────────────────────────────────────────────────────────────────────────────
218
+
219
+ const TAG_DEFAULT_DISPLAY: Record<string, string> = {
220
+ span: 'inline',
221
+ a: 'inline',
222
+ strong: 'inline',
223
+ em: 'inline',
224
+ b: 'inline',
225
+ i: 'inline',
226
+ label: 'inline',
227
+ img: 'inline',
228
+ input: 'inline',
229
+ select: 'inline',
230
+ textarea: 'inline',
231
+ button: 'inline-block',
232
+ li: 'list-item',
233
+ table: 'table',
234
+ }
235
+
236
+ export function getDefaultDisplayForTag(tag: string): string {
237
+ return TAG_DEFAULT_DISPLAY[tag.toLowerCase()] ?? 'block'
238
+ }
239
+
240
+ /**
241
+ * Resolves the current `display` value from matched CSS data, handling both
242
+ * literal values and CSS variable references.
243
+ * Returns the resolved value, or `undefined` if `display` is not declared
244
+ * or uses a variable that cannot be resolved.
245
+ */
246
+ export function resolveDisplayValue(matcherData: MatchedCssData): string | undefined {
247
+ for (const match of matcherData.matches) {
248
+ for (const property of match.properties) {
249
+ if (property.name !== 'display') continue
250
+
251
+ if (!property.varRefs || property.varRefs.length === 0) {
252
+ return property.value
253
+ }
254
+
255
+ for (const varName of property.varRefs) {
256
+ const resolvedValue = matcherData.customProperties[varName]
257
+ if (resolvedValue !== undefined) return resolvedValue
258
+ }
259
+ }
260
+ }
261
+ return undefined
262
+ }
263
+
214
264
  // ─────────────────────────────────────────────────────────────────────────────
215
265
  // Gap Enrichment
216
266
  // ─────────────────────────────────────────────────────────────────────────────
217
267
 
218
268
  const FLEX_GRID_DISPLAY_VALUES = new Set(['flex', 'grid', 'inline-flex', 'inline-grid'])
219
269
 
220
- function hasFlexOrGridDisplay(matches: CssSelectorMatch[]): boolean {
221
- for (const match of matches) {
270
+ /**
271
+ * Returns true if display uses a CSS variable that cannot be resolved --
272
+ * we optimistically assume it could be flex/grid.
273
+ */
274
+ function hasUnresolvableDisplayVar(matcherData: MatchedCssData): boolean {
275
+ for (const match of matcherData.matches) {
222
276
  for (const property of match.properties) {
223
- if (property.name === 'display' && FLEX_GRID_DISPLAY_VALUES.has(property.value)) {
224
- return true
277
+ if (property.name !== 'display') continue
278
+ if (property.varRefs && property.varRefs.length > 0) {
279
+ for (const varName of property.varRefs) {
280
+ if (matcherData.customProperties[varName] === undefined) return true
281
+ }
225
282
  }
226
283
  }
227
284
  }
228
285
  return false
229
286
  }
230
287
 
288
+ export function hasFlexOrGridDisplay(matcherData: MatchedCssData): boolean {
289
+ const displayValue = resolveDisplayValue(matcherData)
290
+ if (displayValue !== undefined) return FLEX_GRID_DISPLAY_VALUES.has(displayValue)
291
+ return hasUnresolvableDisplayVar(matcherData)
292
+ }
293
+
231
294
  /**
232
295
  * Walks the element tree and adds `gap` to relevant CSS properties
233
296
  * for elements that have display: flex|grid and more than 1 child.
@@ -237,7 +300,7 @@ export function enrichGapProperties(elements: ExtractedElement[]): ExtractedElem
237
300
  return elements.map((element) => {
238
301
  if (element.children.length > 1) {
239
302
  const matcherData = element.extractorData.get('css-matcher') as MatchedCssData | undefined
240
- if (matcherData && hasFlexOrGridDisplay(matcherData.matches)) {
303
+ if (matcherData && hasFlexOrGridDisplay(matcherData)) {
241
304
  const cssData = element.extractorData.get('css-properties') as CssPropertiesData | undefined
242
305
  if (cssData) {
243
306
  element.extractorData.set('css-properties', {
@@ -24,5 +24,10 @@ export type {
24
24
  export { createPropTrackerExtractor } from './prop-tracker'
25
25
  export type { PropTrackerData, PropTrackerExtractorState } from './prop-tracker'
26
26
 
27
- export { createCssPropertiesExtractor, enrichGapProperties } from './css-properties'
27
+ export {
28
+ createCssPropertiesExtractor,
29
+ enrichGapProperties,
30
+ resolveDisplayValue,
31
+ getDefaultDisplayForTag,
32
+ } from './css-properties'
28
33
  export type { CssPropertiesData } from './css-properties'
@@ -17,6 +17,8 @@ export {
17
17
  createPropTrackerExtractor,
18
18
  createCssPropertiesExtractor,
19
19
  enrichGapProperties,
20
+ resolveDisplayValue,
21
+ getDefaultDisplayForTag,
20
22
  } from './extractors'
21
23
 
22
24
  export type {