Интерпретатор выражений

По умолчанию парсер Vega генерирует код для разобранных выражений Vega, а среда выполнения создаёт из него функции JavaScript с помощью конструктора Function. Хотя парсер Vega выполняет собственные проверки безопасности, создание функций из исходного кода во время работы нарушает политики, защищающие от межсайтового скриптинга (XSS), например директиву script-src без разрешения unsafe-eval.

Начиная с версии 5.13.0 Vega поддерживает интерпретацию выражений, совместимую с политикой безопасности содержимого (Content Security Policy, CSP). Пакет vega-interpreter предоставляет подключаемый интерпретатор: он обходит абстрактное синтаксическое дерево (AST) выражения и последовательно выполняет его операции. Интерпретатор позволяет соблюдать CSP, но может снизить производительность. В тестах начального разбора и обработки потока данных он в среднем примерно на 10% медленнее. При интерактивных обновлениях разница может быть больше: в них обычно больше выражений, а однократные затраты на разбор через Function распределяются по последующим обновлениям.

Чтобы использовать интерпретатор, выполните три шага:

  1. Загрузите модуль vega-interpreter отдельным тегом script, как показано ниже, или включите его в собственную сборку Vega.
  2. Вызовите парсер Vega с параметром {ast: true}, чтобы в результат были включены разобранные синтаксические деревья.
  3. Передайте интерпретатор в конструктор представления Vega (View). Среда выполнения воспользуется альтернативным вычислителем выражений.

Пример

В следующем примере CSP задана через метатег HTML. В большинстве случаев политику задаёт сервер.

Исходный HTML-код страницы (ключевое слово CSP 'self' обязательно заключается в одинарные кавычки):

<head>
  <meta http-equiv="Content-Security-Policy" content="script-src 'self' cdn.jsdelivr.net" />
  <script src="https://cdn.jsdelivr.net/npm/vega@6"></script>
  <script src="https://cdn.jsdelivr.net/npm/vega-interpreter@1"></script>
</head>
<body>
  <div id="view"></div>
  <script src="main.js"></script>
</body>

Исходный код main.js. Скрипт вынесен в отдельный файл, поскольку многие политики CSP запрещают unsafe-inline.

let view;

fetch('https://vega.github.io/vega/examples/bar-chart.vg.json')
  .then(res => res.json())
  .then(spec => render(spec))
  .catch(err => console.error(err));

function render(spec) {
  // Разобрать спецификацию Vega и включить AST в результат
  // Вторым аргументом передать null вместо конфигурации
  const runtime = vega.parse(spec, null, { ast: true });

  // Передать интерпретатор выражений в конструктор View через параметр expr
  view = new vega.View(runtime, {
    expr:      vega.expressionInterpreter,
    renderer:  'svg',    // способ отрисовки: canvas или svg
    container: '#view',  // родительский контейнер DOM
    hover:     true      // включить обработку наведения
  });

  return view.runAsync();
}