function collectSignals(spec, config) {
    const _ = name => value(spec[name], config[name]),
      signals = [signalObject('background', _('background')), signalObject('autosize', parseAutosize(_('autosize'))), signalObject('padding', parsePadding(_('padding'))), signalObject('width', _('width') || 0), signalObject('height', _('height') || 0)],
      pre = signals.reduce((p, s) => (p[s.name] = s, p), {}),
      map = {};

    // add spec signal array
    array$2(spec.signals).forEach(s => {
      if (has$1(pre, s.name)) {
        // merge if built-in signal
        s = extend(pre[s.name], s);
      } else {
        // otherwise add to signal list
        signals.push(s);
      }
      map[s.name] = s;
    });

    // add config signal array
    array$2(config.signals).forEach(s => {
      if (!has$1(map, s.name) && !has$1(pre, s.name)) {
        // add to signal list if not already defined
        signals.push(s);
      }
    });
    return signals;
  }
Collect top-level signals, merging values as needed. Signals
defined in the config signals arrays are added only if that
signal is not explicitly defined in the specification.
Built-in signals (autosize, background, padding, width, height)
receive special treatment. They are initialized using the
top-level spec property, or, if undefined in the spec, using
the corresponding top-level config property. If this property
is a signal reference object, the signal expression maps to the
signal 'update' property. If the spec's top-level signal array
contains an entry that matches a built-in signal, that entry
will be merged with the built-in specification, potentially
overwriting existing 'value' or 'update' properties.