@wix/zero-config-implementation 1.93.0 → 1.95.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.93.0",
7
+ "version": "1.95.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",
@@ -39,7 +39,7 @@
39
39
  }
40
40
  },
41
41
  "dependencies": {
42
- "@wix/react-component-schema": "1.8.0",
42
+ "@wix/react-component-schema": "1.9.0",
43
43
  "@wix/seo-service": "^1.9.0",
44
44
  "@wix/services-manager": "^1.0.7",
45
45
  "@wix/services-manager-react": "^1.0.8",
@@ -106,5 +106,5 @@
106
106
  ]
107
107
  }
108
108
  },
109
- "falconPackageHash": "d7c036bb07aaafe708985efa56e88c2eefb34077d2d892938112978f"
109
+ "falconPackageHash": "831bcccde22937359efc18de9a5b8b4e52d3ca3fa6ebdbdd9bcb4a65"
110
110
  }
@@ -107,6 +107,103 @@ describe('toEditorReactComponent display conversion', () => {
107
107
  })
108
108
  })
109
109
 
110
+ // CSS display values whose spelling differs from the enum's. Emitting one
111
+ // unmapped makes DevCenter reject the whole components override, permanently
112
+ // blocking Checkpoint for the project (ECL-18531).
113
+ describe('display value enum mapping', () => {
114
+ const { DISPLAY_VALUE } = CSS_PROPERTIES
115
+ const tagDefaults: [tag: string, cssDisplay: string, enumValue: string][] = [
116
+ ['td', 'table-cell', DISPLAY_VALUE.tableCell],
117
+ ['th', 'table-cell', DISPLAY_VALUE.tableCell],
118
+ ['tr', 'table-row', DISPLAY_VALUE.tableRow],
119
+ ['tbody', 'table-row-group', DISPLAY_VALUE.tableRowGroup],
120
+ ['thead', 'table-header-group', DISPLAY_VALUE.tableHeaderGroup],
121
+ ['tfoot', 'table-footer-group', DISPLAY_VALUE.tableFooterGroup],
122
+ ['caption', 'table-caption', DISPLAY_VALUE.tableCaption],
123
+ ['col', 'table-column', DISPLAY_VALUE.tableColumn],
124
+ ['colgroup', 'table-column-group', DISPLAY_VALUE.tableColumnGroup],
125
+ ]
126
+
127
+ it.each(tagDefaults)('maps a <%s> tag default (%s) onto its enum name', (tag, _cssDisplay, expectedEnumValue) => {
128
+ const rootElement = createElementWithDisplayMatcher(tag, matcherDataFromCss(''))
129
+
130
+ const result = toEditorReactComponent(createComponent(rootElement))
131
+
132
+ expect(result.editorElement?.cssProperties?.display).toEqual({
133
+ display: {
134
+ displayValues: [CSS_PROPERTIES.DISPLAY_VALUE.none, expectedEnumValue],
135
+ },
136
+ })
137
+ })
138
+
139
+ it('treats an explicit `display: table-cell` declaration the same as the tag default', () => {
140
+ const fromTag = toEditorReactComponent(
141
+ createComponent(createElementWithDisplayMatcher('td', matcherDataFromCss(''))),
142
+ )
143
+ const fromCss = toEditorReactComponent(
144
+ createComponent(createElementWithDisplayMatcher('div', matcherDataFromCss('display: table-cell'))),
145
+ )
146
+
147
+ expect(fromCss.editorElement?.cssProperties?.display).toEqual(fromTag.editorElement?.cssProperties?.display)
148
+ expect(fromCss.editorElement?.cssProperties?.display).toEqual({
149
+ display: {
150
+ displayValues: [CSS_PROPERTIES.DISPLAY_VALUE.none, CSS_PROPERTIES.DISPLAY_VALUE.tableCell],
151
+ },
152
+ })
153
+ })
154
+
155
+ it('still maps kebab-case values that do have an enum representation', () => {
156
+ const rootElement = createElementWithDisplayMatcher('div', matcherDataFromCss('display: inline-block'))
157
+
158
+ const result = toEditorReactComponent(createComponent(rootElement))
159
+
160
+ expect(result.editorElement?.cssProperties?.display).toEqual({
161
+ display: {
162
+ displayValues: [CSS_PROPERTIES.DISPLAY_VALUE.none, CSS_PROPERTIES.DISPLAY_VALUE.inlineBlock],
163
+ },
164
+ })
165
+ })
166
+
167
+ it('never emits a display value outside the enum for any known tag default', () => {
168
+ const allowedValues = new Set<string>(Object.values(CSS_PROPERTIES.DISPLAY_VALUE))
169
+
170
+ for (const [tag] of tagDefaults) {
171
+ const rootElement = createElementWithDisplayMatcher(tag, matcherDataFromCss(''))
172
+ const result = toEditorReactComponent(createComponent(rootElement))
173
+ const emittedValues = result.editorElement?.cssProperties?.display?.display?.displayValues ?? []
174
+
175
+ expect(emittedValues.filter((displayValue) => !allowedValues.has(displayValue))).toEqual([])
176
+ }
177
+ })
178
+
179
+ /**
180
+ * Completeness check driven by the enum itself, so a value added in a future
181
+ * `@wix/component-protocol` bump cannot sit unmapped — which is the gap that
182
+ * made ECL-18531 possible. Values whose CSS spelling already matches the enum
183
+ * name need no entry; those that differ must be mapped.
184
+ *
185
+ * Skips names containing `_`: `UNKNOWN_DisplayValue` and the deprecated
186
+ * snake_case aliases, none of which are CSS spellings.
187
+ */
188
+ it('reaches every enum value from its CSS spelling', () => {
189
+ const toCssSpelling = (enumValue: string) => enumValue.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`)
190
+
191
+ const unreachable = Object.values(CSS_PROPERTIES.DISPLAY_VALUE)
192
+ .filter((enumValue) => !enumValue.includes('_'))
193
+ .filter((enumValue) => {
194
+ const rootElement = createElementWithDisplayMatcher(
195
+ 'div',
196
+ matcherDataFromCss(`display: ${toCssSpelling(enumValue)}`),
197
+ )
198
+ const result = toEditorReactComponent(createComponent(rootElement))
199
+
200
+ return result.editorElement?.cssProperties?.display?.display?.displayValues?.[1] !== enumValue
201
+ })
202
+
203
+ expect(unreachable).toEqual([])
204
+ })
205
+ })
206
+
110
207
  it('omits the nearest matching ancestor class prefix from inner element display names', () => {
111
208
  const rootElement = createSemanticElement({
112
209
  traceId: 'trace-root',
@@ -166,6 +263,29 @@ describe('toEditorReactComponent display conversion', () => {
166
263
  })
167
264
  })
168
265
 
266
+ describe('toEditorReactComponent — cssProperties defaultValue', () => {
267
+ it('ignores the var() fallback when building a regular CSS property defaultValue', () => {
268
+ const extractorData = new Map<string, unknown>()
269
+ extractorData.set('css-matcher', matcherDataFromCss('background: var(wst-primary-background-color, tomato);'))
270
+ extractorData.set('css-properties', { relevant: [CSS_PROPERTIES.CSS_PROPERTY_TYPE.background] })
271
+
272
+ const rootElement: ExtractedElement = {
273
+ traceId: 'trace-root',
274
+ name: 'root',
275
+ tag: 'div',
276
+ attributes: {},
277
+ extractorData,
278
+ children: [],
279
+ }
280
+
281
+ const result = toEditorReactComponent(createComponent(rootElement))
282
+
283
+ expect(result.editorElement?.cssProperties?.background).toEqual({
284
+ defaultValue: 'var(--wst-primary-background-color)',
285
+ })
286
+ })
287
+ })
288
+
169
289
  describe('toEditorReactComponent selector conversion', () => {
170
290
  it.each([
171
291
  ['a', 'a___'],
@@ -16,6 +16,7 @@ import { IoError } from '../errors'
16
16
  import { buildRefElementManifestKey } from '../extensions/ref-elements/path-utils'
17
17
  import type { RefElementMatch } from '../extensions/ref-elements/types'
18
18
  import type { ComponentInfoWithCss } from '../index'
19
+ import { stripVarFallback } from '../information-extractors/css/parse'
19
20
  import type { MatchedCssData, RegisteredCustomProperty } from '../information-extractors/css/types'
20
21
  import type {
21
22
  CoupledComponentInfo,
@@ -541,20 +542,30 @@ function getMatchedPropertyValues(element: ExtractedElement): Map<string, string
541
542
  return values
542
543
  }
543
544
 
544
- const CSS_DISPLAY_TO_ENUM: Record<string, string> = {
545
- 'inline-block': 'inlineBlock',
546
- 'inline-flex': 'inlineFlex',
547
- 'inline-grid': 'inlineGrid',
548
- 'inline-table': 'inlineTable',
549
- 'list-item': 'listItem',
550
- 'flow-root': 'flowRoot',
551
- }
545
+ const { CSS_PROPERTY_TYPE, DISPLAY_VALUE } = CSS_PROPERTIES
552
546
 
553
- function toDisplayEnumValue(cssValue: string): NonNullable<Display['displayValues']>[number] {
554
- return (CSS_DISPLAY_TO_ENUM[cssValue] ?? cssValue) as NonNullable<Display['displayValues']>[number]
547
+ type DisplayEnumValue = NonNullable<Display['displayValues']>[number]
548
+
549
+ const CSS_DISPLAY_TO_ENUM: Record<string, DisplayEnumValue> = {
550
+ 'inline-block': DISPLAY_VALUE.inlineBlock,
551
+ 'inline-flex': DISPLAY_VALUE.inlineFlex,
552
+ 'inline-grid': DISPLAY_VALUE.inlineGrid,
553
+ 'inline-table': DISPLAY_VALUE.inlineTable,
554
+ 'list-item': DISPLAY_VALUE.listItem,
555
+ 'flow-root': DISPLAY_VALUE.flowRoot,
556
+ 'table-row-group': DISPLAY_VALUE.tableRowGroup,
557
+ 'table-header-group': DISPLAY_VALUE.tableHeaderGroup,
558
+ 'table-footer-group': DISPLAY_VALUE.tableFooterGroup,
559
+ 'table-row': DISPLAY_VALUE.tableRow,
560
+ 'table-cell': DISPLAY_VALUE.tableCell,
561
+ 'table-column-group': DISPLAY_VALUE.tableColumnGroup,
562
+ 'table-column': DISPLAY_VALUE.tableColumn,
563
+ 'table-caption': DISPLAY_VALUE.tableCaption,
555
564
  }
556
565
 
557
- const { CSS_PROPERTY_TYPE, DISPLAY_VALUE } = CSS_PROPERTIES
566
+ function toDisplayEnumValue(cssValue: string): DisplayEnumValue {
567
+ return (CSS_DISPLAY_TO_ENUM[cssValue] ?? cssValue) as DisplayEnumValue
568
+ }
558
569
 
559
570
  function buildCssProperties(element: ExtractedElement | undefined): Record<string, CssPropertyItem> {
560
571
  const result: Record<string, CssPropertyItem> = {}
@@ -570,7 +581,8 @@ function buildCssProperties(element: ExtractedElement | undefined): Record<strin
570
581
  result[propName] = buildDisplayProperty(element)
571
582
  continue
572
583
  }
573
- const defaultValue = resolveLonghand(propName, cssPropertyValues)
584
+ const resolvedValue = resolveLonghand(propName, cssPropertyValues)
585
+ const defaultValue = resolvedValue !== undefined ? stripVarFallback(resolvedValue) : undefined
574
586
  result[propName] = {
575
587
  ...(defaultValue !== undefined && { defaultValue }),
576
588
  }
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { parseCss, resolveCssPropertyValue } from './parse'
2
+ import { parseCss, resolveCssPropertyValue, stripVarFallback } from './parse'
3
3
 
4
4
  describe('parseCss — getStateClasses', () => {
5
5
  it('extracts a custom-class modifier with no paired pseudo', () => {
@@ -325,3 +325,21 @@ describe('resolveVarIdentifiers — CSS Modules @value compatibility', () => {
325
325
  expect(contentProperty.value).toBe('"var(foo)"')
326
326
  })
327
327
  })
328
+
329
+ describe('stripVarFallback', () => {
330
+ it('removes a var() fallback, keeping only the variable reference', () => {
331
+ expect(stripVarFallback('var(--wst-primary-background-color, tomato)')).toBe('var(--wst-primary-background-color)')
332
+ })
333
+
334
+ it('leaves a var() with no fallback unchanged', () => {
335
+ expect(stripVarFallback('var(--x)')).toBe('var(--x)')
336
+ })
337
+
338
+ it('strips a fallback embedded within a larger value', () => {
339
+ expect(stripVarFallback('1px solid var(--border-color, gray)')).toBe('1px solid var(--border-color)')
340
+ })
341
+
342
+ it('leaves a value with no var() unchanged', () => {
343
+ expect(stripVarFallback('10px')).toBe('10px')
344
+ })
345
+ })
@@ -373,6 +373,34 @@ function resolveVarIdentifiers(valueNode: CssNode, valueAliases: Map<string, str
373
373
  return generate(valueNode)
374
374
  }
375
375
 
376
+ /**
377
+ * Removes the fallback argument from every var() call in a CSS value string. A var()
378
+ * fallback is not a meaningful "default value" for the editor to surface — only the
379
+ * variable reference itself is — so `var(--x, tomato)` becomes `var(--x)`.
380
+ */
381
+ export function stripVarFallback(cssValue: string): string {
382
+ if (!cssValue.includes('var(')) return cssValue
383
+
384
+ try {
385
+ const valueAst = parse(cssValue, { context: 'value' })
386
+ walk(valueAst, {
387
+ visit: 'Function',
388
+ enter(functionNode: FunctionNode) {
389
+ if (functionNode.name !== 'var') return
390
+ const functionArguments = [...functionNode.children]
391
+ const firstCommaIndex = functionArguments.findIndex(
392
+ (argumentNode) => argumentNode.type === 'Operator' && argumentNode.value === ',',
393
+ )
394
+ if (firstCommaIndex === -1) return
395
+ functionNode.children.fromArray(functionArguments.slice(0, firstCommaIndex))
396
+ },
397
+ })
398
+ return generate(valueAst)
399
+ } catch {
400
+ return cssValue
401
+ }
402
+ }
403
+
376
404
  /**
377
405
  * Extracts all CSS custom property names referenced via var() by walking
378
406
  * the declaration value AST for Function nodes named "var".