@wix/zero-config-implementation 1.90.0 → 1.91.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.90.0",
7
+ "version": "1.91.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",
@@ -106,5 +106,5 @@
106
106
  ]
107
107
  }
108
108
  },
109
- "falconPackageHash": "3d380033b2cb76ee95040a71fda50b5bbee8d8cb70460bfb8c8a5313"
109
+ "falconPackageHash": "7232f116f870e20fe94caa49ba503a0a945ec4eed14c9f9b55544590"
110
110
  }
@@ -225,6 +225,91 @@ describe('toEditorReactComponent — @property design tokens', () => {
225
225
  })
226
226
  })
227
227
 
228
+ it('carries the --min / --max / --step descriptors of a number @property into number limits', () => {
229
+ const component = createComponentWithCss(`
230
+ @property --speed { syntax: "<number>"; inherits: true; initial-value: 4; --min: 1; --max: 10; --step: 0.5; }
231
+ `)
232
+
233
+ const customProps = toEditorReactComponent(component).editorElement?.cssCustomProperties
234
+
235
+ expect(customProps?.speed).toEqual({
236
+ displayName: 'Speed',
237
+ defaultValue: '4',
238
+ cssPropertyType: 'number',
239
+ number: { min: '1', max: '10', multiplier: '0.5' },
240
+ })
241
+ })
242
+
243
+ it('emits only the usable half of a partial range and omits the rest', () => {
244
+ const component = createComponentWithCss(`
245
+ @property --weight { syntax: "<number>"; inherits: true; initial-value: 2; --min: 0; }
246
+ @property --tempo { syntax: "<number>"; inherits: true; initial-value: 3; --max: 9; --step: nonsense; }
247
+ @property --offset { syntax: "<number>"; inherits: true; initial-value: 0; --min: ; --max: 12; }
248
+ `)
249
+
250
+ const customProps = toEditorReactComponent(component).editorElement?.cssCustomProperties
251
+
252
+ expect(customProps?.weight).toEqual({
253
+ displayName: 'Weight',
254
+ defaultValue: '2',
255
+ cssPropertyType: 'number',
256
+ number: { min: '0' },
257
+ })
258
+ expect(customProps?.tempo).toEqual({
259
+ displayName: 'Tempo',
260
+ defaultValue: '3',
261
+ cssPropertyType: 'number',
262
+ number: { max: '9' },
263
+ })
264
+ expect(customProps?.offset).toEqual({
265
+ displayName: 'Offset',
266
+ defaultValue: '0',
267
+ cssPropertyType: 'number',
268
+ number: { max: '12' },
269
+ })
270
+ })
271
+
272
+ it('drops an inverted range and a non-positive step, omitting `number` when nothing is usable', () => {
273
+ const component = createComponentWithCss(`
274
+ @property --span { syntax: "<number>"; inherits: true; initial-value: 5; --min: 10; --max: 2; --step: 0; }
275
+ @property --frozen { syntax: "<number>"; inherits: true; initial-value: 5; --min: 5; --max: 5; --step: 1; }
276
+ `)
277
+
278
+ const customProps = toEditorReactComponent(component).editorElement?.cssCustomProperties
279
+
280
+ expect(customProps?.span).toEqual({
281
+ displayName: 'Span',
282
+ defaultValue: '5',
283
+ cssPropertyType: 'number',
284
+ })
285
+ expect(customProps?.frozen).toEqual({
286
+ displayName: 'Frozen',
287
+ defaultValue: '5',
288
+ cssPropertyType: 'number',
289
+ number: { multiplier: '1' },
290
+ })
291
+ })
292
+
293
+ it('ignores range descriptors on a non-number @property', () => {
294
+ const component = createComponentWithCss(`
295
+ @property --sdf-safelight-color { syntax: "<color>"; inherits: true; initial-value: #F02011; --min: 1; --max: 10; }
296
+ @property --gap { syntax: "<length>"; inherits: true; initial-value: 4px; --min: 1; --max: 10; --step: 1; }
297
+ `)
298
+
299
+ const customProps = toEditorReactComponent(component).editorElement?.cssCustomProperties
300
+
301
+ expect(customProps?.['sdf-safelight-color']).toEqual({
302
+ displayName: 'Sdf Safelight Color',
303
+ defaultValue: '#F02011',
304
+ cssPropertyType: 'color',
305
+ })
306
+ expect(customProps?.gap).toEqual({
307
+ displayName: 'Gap',
308
+ defaultValue: '4px',
309
+ cssPropertyType: 'length',
310
+ })
311
+ })
312
+
228
313
  it('emits a customEnum dropdown for a pipe-separated ident-list @property', () => {
229
314
  const component = createComponentWithCss(`
230
315
  @property --sdf-palette { syntax: "warm | cool | neutral"; inherits: false; initial-value: warm; }
@@ -1,5 +1,6 @@
1
1
  import type {
2
2
  CssCustomPropertyItem,
3
+ CssNumber,
3
4
  CssPropertyItem,
4
5
  CustomPropertyEnum,
5
6
  DataItem,
@@ -324,6 +325,7 @@ function buildNearestCommonAncestorCustomProps(
324
325
  ? registration.cssPropertyType
325
326
  : getVarPropertyTypeFromCssInfos(varName, defaultValue ?? '', component.css)
326
327
  const customEnum = buildCustomEnum(registration)
328
+ const numberLimits = buildCssNumber(registration, cssPropertyType)
327
329
  const cleanVarName = varName.startsWith('--') ? varName.slice(2) : varName
328
330
 
329
331
  const existingProps = nearestCommonAncestorCustomProps.get(nearestCommonAncestorTraceId) ?? {}
@@ -332,6 +334,7 @@ function buildNearestCommonAncestorCustomProps(
332
334
  ...(defaultValue !== undefined && { defaultValue }),
333
335
  ...(cssPropertyType !== undefined && { cssPropertyType }),
334
336
  ...(customEnum && { customEnum }),
337
+ ...(numberLimits && { number: numberLimits }),
335
338
  }
336
339
  nearestCommonAncestorCustomProps.set(nearestCommonAncestorTraceId, existingProps)
337
340
  }
@@ -368,6 +371,42 @@ function buildCustomEnum(registration: RegisteredCustomProperty | undefined): Cu
368
371
  }
369
372
  }
370
373
 
374
+ function parseFiniteDescriptor(rawValue: string | undefined): number | undefined {
375
+ const trimmed = rawValue?.trim()
376
+ if (!trimmed) return undefined
377
+ const parsed = Number(trimmed)
378
+ return Number.isFinite(parsed) ? parsed : undefined
379
+ }
380
+
381
+ /**
382
+ * Builds the clamp the panel's number control applies, from an @property's range
383
+ * descriptors. Anything unusable is dropped rather than passed through.
384
+ */
385
+ function buildCssNumber(
386
+ registration: RegisteredCustomProperty | undefined,
387
+ cssPropertyType: CssCustomPropertyItem['cssPropertyType'],
388
+ ): CssNumber | undefined {
389
+ if (!registration?.range || cssPropertyType !== CSS_PROPERTIES.CSS_DATA_TYPE.number) return undefined
390
+
391
+ let min = parseFiniteDescriptor(registration.range.min)
392
+ let max = parseFiniteDescriptor(registration.range.max)
393
+ if (min !== undefined && max !== undefined && min >= max) {
394
+ min = undefined
395
+ max = undefined
396
+ }
397
+
398
+ const step = parseFiniteDescriptor(registration.range.step)
399
+ const multiplier = step !== undefined && step > 0 ? step : undefined
400
+
401
+ if (min === undefined && max === undefined && multiplier === undefined) return undefined
402
+
403
+ return {
404
+ ...(min !== undefined && { min: String(min) }),
405
+ ...(max !== undefined && { max: String(max) }),
406
+ ...(multiplier !== undefined && { multiplier: String(multiplier) }),
407
+ }
408
+ }
409
+
371
410
  /**
372
411
  * Queries each CSS parser API for the property type of a variable and returns
373
412
  * the first defined result.
@@ -9,6 +9,7 @@ import type {
9
9
  CssDataType,
10
10
  NativePseudoClass,
11
11
  RegisteredCustomProperty,
12
+ RegisteredPropertyRange,
12
13
  StateClassRule,
13
14
  } from './types'
14
15
 
@@ -501,6 +502,19 @@ function extractAtruleDescriptors(block: CssNode): Map<string, string> {
501
502
  return descriptors
502
503
  }
503
504
 
505
+ /** Picks the range descriptors out of the block; css-tree collects them like any other declaration. */
506
+ function extractRangeDescriptors(descriptors: Map<string, string>): RegisteredPropertyRange | undefined {
507
+ const min = descriptors.get('--min')
508
+ const max = descriptors.get('--max')
509
+ const step = descriptors.get('--step')
510
+ if (min === undefined && max === undefined && step === undefined) return undefined
511
+ return {
512
+ ...(min !== undefined && { min }),
513
+ ...(max !== undefined && { max }),
514
+ ...(step !== undefined && { step }),
515
+ }
516
+ }
517
+
504
518
  /**
505
519
  * Walks the stylesheet for `@property` at-rules and builds a map of registered custom
506
520
  * properties keyed by the `--`-prefixed variable name.
@@ -528,11 +542,13 @@ function parseRegisteredProperties(cssString: string): Map<string, RegisteredCus
528
542
  if (!resolved) return
529
543
  const { cssPropertyType, enumOptions } = resolved
530
544
  const initialValue = descriptors.get('initial-value')
545
+ const range = extractRangeDescriptors(descriptors)
531
546
 
532
547
  registered.set(varName, {
533
548
  cssPropertyType,
534
549
  ...(enumOptions && { enumOptions }),
535
550
  ...(initialValue !== undefined && { defaultValue: initialValue }),
551
+ ...(range && { range }),
536
552
  })
537
553
  },
538
554
  })
@@ -47,6 +47,23 @@ export interface RegisteredCustomProperty {
47
47
  * (`a | b | c`). Present only when `cssPropertyType` is `customEnum`.
48
48
  */
49
49
  enumOptions?: string[]
50
+ /**
51
+ * The numeric range for the control, as raw descriptor strings. Validated at
52
+ * conversion time. Meaningful only when `cssPropertyType` is `number`.
53
+ */
54
+ range?: RegisteredPropertyRange
55
+ }
56
+
57
+ /**
58
+ * `--min` / `--max` / `--step` descriptors an importer may emit inside an `@property`
59
+ * block. The `--` prefix keeps them in the author namespace: browsers ignore descriptors
60
+ * they don't know, so the CSS stays valid and CSS can't standardise these names out from
61
+ * under us.
62
+ */
63
+ export interface RegisteredPropertyRange {
64
+ min?: string
65
+ max?: string
66
+ step?: string
50
67
  }
51
68
 
52
69
  export type NativePseudoClass = 'hover' | 'focus' | 'disabled' | 'invalid'