Предупреждение EVAL появляется в Rollup в тех
случаях, когда анализируемый код содержит вызовы функции
eval() или конструкции, работающие аналогичным образом.
Такое предупреждение сигнализирует о том, что сборщик столкнулся с
динамическим выполнением строкового JavaScript-кода и не способен
полностью проанализировать последствия такого вызова на этапе
сборки.
Пример:
const code = 'console.log("Hello")';
eval(code);
При обработке такого файла Rollup может вывести предупреждение вида:
(!) Use of eval is strongly discouraged
https://rollupjs.org/troubleshooting/#avoiding-eval
src/main.js
1: eval(code)
Сообщение не означает ошибку сборки. Процесс обычно продолжается успешно, однако предупреждение указывает на потенциальные проблемы с оптимизацией, безопасностью и предсказуемостью итогового бандла.
Основная задача Rollup заключается в статическом анализе модулей.
Сборщик пытается заранее определить:
Функция eval() нарушает этот подход.
Рассмотрим пример:
const variableName = 'user';
eval(`console.log(${variableName})`);
Для человека результат достаточно очевиден, однако для Rollup содержимое строки является обычным текстом. Сборщик не может гарантированно определить:
Фактически после появления eval() статический анализ
становится частично невозможным.
Функция принимает строку и интерпретирует её как JavaScript-код.
Пример:
eval('const x = 10');
Эквивалентно:
const x = 10;
Более сложный пример:
const operation = '+';
const result = eval(`5 ${operation} 10`);
console.log(result);
Результат:
15
Код создаётся динамически во время выполнения программы.
Именно эта динамичность вызывает проблемы для сборщиков модулей.
Одним из главных преимуществ Rollup является агрессивное удаление неиспользуемого кода.
Пример:
export function used() {
console.log('used');
}
export function unused() {
console.log('unused');
}
Если импортируется только функция used, Rollup удалит
функцию unused.
Однако при наличии eval ситуация меняется.
eval('unused()');
Теперь сборщик уже не может уверенно определить, действительно ли функция не используется.
В результате:
Во время оптимизации Rollup может изменять внутренние имена переменных.
Пример:
const longVariableName = 123;
После минификации:
const a = 123;
Если же где-то используется:
eval('console.log(longVariableName)');
Переименование может привести к поломке логики.
Поэтому инструменты минификации вынуждены быть осторожнее при обработке подобных участков.
Рассмотрим пример:
const moduleName = 'UserModule';
eval(`loadModule("${moduleName}")`);
Во время разработки всё может работать корректно.
После:
результат выполнения может отличаться от ожидаемого.
Чем сложнее сборка, тем выше риск возникновения трудноуловимых ошибок.
JavaScript-движок оптимизирует обычный код заранее.
Для eval() ситуация иная.
Пример:
for (let i = 0; i < 10000; i++) {
eval('i + 1');
}
На каждой итерации строка должна:
Обычный код работает заметно быстрее.
Поэтому использование eval часто негативно влияет на
производительность приложения.
Одной из главных причин негативного отношения к eval
является безопасность.
Опасный пример:
const userInput = getUserInput();
eval(userInput);
Если пользователь введёт:
alert(document.cookie)
код будет выполнен.
Если приложение работает в браузере, злоумышленник может:
По этой причине многие политики безопасности прямо запрещают
использование eval.
Многие современные сайты используют механизм CSP (Content Security Policy).
Типичная политика:
Content-Security-Policy:
script-src 'self'
В подобных условиях использование eval() может быть
заблокировано браузером.
Ошибка выглядит примерно так:
Refused to evaluate a string as JavaScript because
'unsafe-eval' is not an allowed source.
Для разрешения работы пришлось бы добавить:
unsafe-eval
Однако это существенно ослабляет защиту приложения.
Поэтому корпоративные проекты часто полностью запрещают
eval.
Не всегда проблема связана с прямым вызовом функции.
Следующие конструкции также могут приводить к аналогичным последствиям.
Пример:
const sum = new Function(
'a',
'b',
'return a + b'
);
Фактически происходит динамическая компиляция кода.
Хотя Rollup обычно отдельно не маркирует такой код как
eval, проблемы остаются схожими.
Пример:
const expression = '5 + 10';
const result = Function(
`return ${expression}`
)();
Код создаётся во время выполнения.
Статический анализ становится невозможным.
Некоторые устаревшие пакеты активно используют динамическое выполнение кода.
Особенно часто это встречается в:
Пример зависимости:
eval(compiledTemplate);
Даже если собственный код не содержит eval,
предупреждение может появляться из-за стороннего пакета.
Некоторые шаблонизаторы компилируют шаблоны в строки JavaScript.
Например:
const renderer = eval(generatedCode);
Подобная архитектура была распространена до появления современных инструментов сборки.
В старых проектах можно встретить конструкции:
eval('obj.' + propertyName);
или
eval('myFunction()');
Такие решения часто создавались до появления современных возможностей JavaScript.
Rollup обычно показывает путь к файлу.
Пример:
(!) Use of eval is strongly discouraged
node_modules/legacy-lib/index.js
Если предупреждение относится к сторонней библиотеке, полезно проверить файл:
grep -r "eval(" node_modules
или выполнить поиск по проекту:
rg "eval\("
Результат позволяет быстро определить проблемный участок.
Вместо:
eval('user.name');
лучше:
user.name
или:
user['name']
Вместо:
eval(functionName + '()');
лучше:
const handlers = {
create,
update,
remove
};
handlers[functionName]();
Такой код:
Вместо:
eval(command);
можно использовать:
switch (command) {
case 'start':
start();
break;
case 'stop':
stop();
break;
}
Вместо генерации имени модуля через eval:
eval(`import("${moduleName}")`);
следует использовать:
await import(`./modules/${moduleName}.js`);
Динамический импорт поддерживается Rollup значительно лучше и позволяет создавать отдельные чанки.
Иногда предупреждение появляется внутри сторонней зависимости.
Например:
node_modules/some-library
Если библиотека:
предупреждение можно оставить без изменений.
Однако рекомендуется понимать источник проблемы и её потенциальные последствия.
Игнорирование предупреждения допустимо только после анализа того,
почему используется eval и какие риски это создаёт.
При необходимости предупреждение можно фильтровать через обработчик
onwarn.
Пример:
export default {
input: 'src/main.js',
onwarn(warning, warn) {
if (warning.code === 'EVAL') {
return;
}
warn(warning);
}
};
После этого сообщения типа EVAL не будут
отображаться.
Следует учитывать, что фильтрация скрывает предупреждение, но не устраняет саму проблему.
Использование eval() в современных проектах
считается нежелательной практикой.
Основные причины:
Для проектов на Rollup предпочтительно использовать:
В большинстве случаев любой сценарий применения eval()
может быть реализован более безопасным, быстрым и предсказуемым
способом, что позволяет Rollup выполнять оптимизации максимально
эффективно.