LCOV - code coverage report
Current view: top level - dashql-app/src/ui/vegalite - vegalite_spec.ts Coverage Total Hit
Test: DashQL 915828b Lines: 100.0 % 24 24
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : /// The keys of a Vega-Lite spec that hold the views it composes
       2            1 : const VIEW_LIST_KEYS = ['layer', 'concat', 'hconcat', 'vconcat'];
       3              : 
       4            1 : function isObject(value: unknown): value is Record<string, unknown> {
       5            1 :     return value != null && typeof value === 'object' && !Array.isArray(value);
       6            1 : }
       7              : 
       8              : /// Give every view with a mark an encoding, an empty one if it has none.
       9              : ///
      10              : /// This works around a bug of Vega-Lite (seen in 6.5.0): `getPointOverlay` decides whether line, area and trail
      11              : /// marks get a point overlay and reads `encoding.shape` without checking for an encoding. When neither the mark
      12              : /// nor the config sets `point`, compiling a view without an encoding throws
      13              : /// "Cannot read properties of undefined (reading 'shape')". Bars and the other marks never reach that code.
      14              : ///
      15              : /// The chart builder creates such views: a new chart is `{ mark: 'bar' }` without fields, switching its mark gives
      16              : /// `{ mark: 'line' }`, and a new layer is `{ mark: 'line' }` too. Hand-written specs of scripts can be the same.
      17              : /// A view in a layer, a concat, a facet or a repeat fails alike, unless the parent has an encoding that the
      18              : /// layers inherit. The empty encoding is added only to the spec that is compiled, every place that compiles
      19              : /// Vega-Lite calls this, so the authored spec in the script keeps its keys.
      20              : ///
      21              : /// Returns the spec itself when no view lacks an encoding.
      22            1 : export function withViewEncodings<T>(spec: T): T {
      23            1 :     if (!isObject(spec)) return spec;
      24            1 :     let out: Record<string, unknown> | null = null;
      25            1 :     const set = (key: string, value: unknown) => {
      26            1 :         out ??= { ...spec };
      27            1 :         out[key] = value;
      28            1 :     };
      29            1 :     if ('mark' in spec && !isObject(spec.encoding)) set('encoding', {});
      30            1 :     for (const key of VIEW_LIST_KEYS) {
      31            1 :         const views = spec[key];
      32            1 :         if (!Array.isArray(views)) continue;
      33            1 :         const next = views.map(withViewEncodings);
      34            1 :         if (next.some((view, i) => view !== views[i])) set(key, next);
      35            1 :     }
      36              :     // The view of a facet or a repeat
      37            1 :     if (isObject(spec.spec)) {
      38            1 :         const view = withViewEncodings(spec.spec);
      39            1 :         if (view !== spec.spec) set('spec', view);
      40            1 :     }
      41            1 :     return (out ?? spec) as T;
      42            1 : }
        

Generated by: LCOV version 2.0-1