Как исправить ошибку 'Cannot use import statement outside a module'
Вы пишете корректный оператор import, запускаете код и сразу же получаете ошибку:
SyntaxError: Cannot use import statement outside a module
Эта ошибка, связанная с ES-модулями JavaScript, является одним из наиболее распространённых источников путаницы для разработчиков, работающих с Node.js, браузерами и тестовыми окружениями. Решение не всегда одинаково — и применение неправильного подхода только усугубляет ситуацию. Вот как диагностировать вашу ситуацию и правильно её решить.
Ключевые моменты
- Эта ошибка возникает из-за несоответствия модульных систем, а не синтаксической ошибки — ваша среда выполнения ожидала CommonJS, но обнаружила синтаксис ES-модулей
import. - В Node.js установите
"type": "module"вpackage.jsonили используйте расширение файла.mjs, чтобы включить ESM. - В браузерах добавьте
type="module"к тегу<script>, чтобы движок обрабатывал файл как ES-модуль. - В Jest настройте Babel для преобразования ESM в CommonJS и скорректируйте
transformIgnorePatternsдля сторонних пакетов, использующих только ESM. - Всегда сначала диагностируйте своё окружение — Node.js, браузер или test runner — прежде чем применять исправление.
Почему возникает эта ошибка
Ошибка означает, что среда выполнения обнаружила синтаксис ES-модулей (import), но ожидала что-то другое — обычно CommonJS (require). Это несоответствие модульных систем, а не синтаксическая ошибка в вашем коде.
В JavaScript существует две модульные системы:
| Система | Синтаксис | По умолчанию в |
|---|---|---|
| CommonJS (CJS) | require() | Node.js (устаревший) |
| ES Modules (ESM) | import | Браузеры, современный Node.js |
Среда выполнения решает, какую систему использовать, на основе конфигурации — а не самого кода. Поэтому один и тот же оператор import работает в одном проекте и не работает в другом.
Как исправить ошибку Import Statement Outside Module в Node.js
Node.js обрабатывает файлы .js как CommonJS, если только в package.json не установлено "type": "module". Чтобы использовать import, необходимо явно включить ESM.
Вариант 1: Установите "type": "module" в package.json
{
"name": "my-app",
"version": "1.0.0",
"type": "module"
}
Это указывает Node.js обрабатывать все файлы .js в проекте как ES-модули. См. официальную документацию Node.js по ES-модулям для получения подробной информации о том, как Node определяет тип модуля.
Вариант 2: Используйте расширение файла .mjs
Переименуйте файл из app.js в app.mjs. Node.js всегда обрабатывает файлы .mjs как ESM, независимо от package.json.
Вариант 3: Используйте .cjs для файлов CommonJS
Если ваш проект использует "type": "module", но одному файлу нужен require(), присвойте ему расширение .cjs.
Важно: Не добавляйте просто
"type": "module"без проверки остальных файлов. Любой файл, использующийrequire(),module.exportsили__dirname, перестанет работать при использовании ESM.
Исправление ошибки в JavaScript для браузера
В браузерах статический import работает только внутри модульного скрипта. Если ваш тег <script> не объявляет type="module", браузер обрабатывает его как классический скрипт и отклоняет синтаксис import.
<!-- Это вызовет ошибку -->
<script src="app.js"></script>
<!-- Это работает -->
<script type="module" src="app.js"></script>
Это поведение является частью стандартной спецификации JavaScript-модулей, реализованной современными браузерами.
Обратите внимание, что модульные скрипты изолированы — переменные, объявленные внутри одного модуля, недоступны глобально. Поэтому после добавления type="module" вы можете увидеть ReferenceError для переменной, которая, как вы ожидали, должна быть глобальной. Решение — явно экспортировать и импортировать значения, а не полагаться на глобальную область видимости.
Discover how at OpenReplay.com.
Исправление ошибки в Jest и других тестовых окружениях
Jest работает в Node.js и традиционно использует CommonJS по умолчанию, даже если ваш исходный код использует ESM. Это распространённый источник ошибки “cannot use import statement outside a module” в тестовых наборах.
Один из подходов — настроить Babel для преобразования ESM в CommonJS во время тестирования:
// babel.config.js
module.exports = {
presets: ['@babel/preset-env'],
}
В качестве альтернативы Jest можно настроить на запуск тестов в нативном режиме ESM, если ваш проект уже использует ES-модули. См. документацию Jest по ECMAScript Modules для получения подробной информации.
Если сторонний пакет (например, swiper, lodash-es или подобные библиотеки, использующие только ESM) вызывает ошибку, необходимо указать Jest преобразовывать его, а не игнорировать:
// jest.config.js
module.exports = {
transformIgnorePatterns: [
'/node_modules/(?!(swiper|ssr-window|dom7)/)',
],
}
Ключевой момент: transformIgnorePatterns использует негативный просмотр вперёд. Вы говорите: “игнорировать всё в node_modules, кроме этих пакетов”. Перечисление их в отдельных элементах массива не сработает — они должны быть объединены в одном регулярном выражении с помощью |.
Конфигурация TypeScript
Если вы используете TypeScript, проверьте ваш tsconfig.json. Поле module контролирует, какой синтаксис модулей TypeScript генерирует в скомпилированном выводе:
{
"compilerOptions": {
"module": "ESNext",
"target": "ESNext"
}
}
Установка module в CommonJS при написании операторов import является валидным TypeScript — компилятор преобразует import в require() в выводе. Однако установка module в ESNext при запуске вывода в окружении CommonJS Node.js (без "type": "module" в package.json) вызовет ошибку во время выполнения. Убедитесь, что настройка модуля в вашем tsconfig.json соответствует тому, что фактически ожидает ваша среда выполнения.
Диагностируйте, прежде чем исправлять
Одна и та же ошибка появляется в разных окружениях по разным причинам. Прежде чем применять какое-либо исправление, спросите себя:
- Где возникает ошибка — в Node.js, браузере или test runner?
- Что указано в поле
"type"вашегоpackage.json? - Вы используете сборщик или запускаете файлы напрямую?
Ответы на эти три вопроса каждый раз укажут вам на правильное решение.
Часто задаваемые вопросы
Да, но вам нужно использовать расширения файлов, чтобы различать их. Файлы с расширением .mjs всегда обрабатываются как ES-модули, а файлы с расширением .cjs всегда обрабатываются как CommonJS, независимо от поля type в package.json. Это позволяет использовать обе системы одновременно в одном проекте.
Нет. Каждый пакет в node_modules имеет свой собственный package.json, поэтому ваше поле type применяется только к файлам вашего проекта. Зависимости определяют свою собственную модульную систему независимо. Ошибка обычно возникает из ваших собственных файлов или при импорте зависимости, использующей только ESM, в контексте CommonJS без надлежащего преобразования.
Сборщики, такие как Webpack и Vite, обрабатывают операторы import на этапе сборки и создают единый выходной файл. Они разрешают синтаксис модулей до того, как среда выполнения его увидит. Когда вы запускаете файлы напрямую в Node.js или браузере без сборщика, сама среда выполнения должна понимать формат модуля, и именно здесь несоответствия вызывают эту ошибку.
Эти глобальные переменные CommonJS недоступны в ES-модулях. Вместо этого вы можете воссоздать их, используя import.meta.url. Например, используйте new URL(import.meta.url).pathname для получения пути к файлу или комбинируйте его с модулем path и fileURLToPath из модуля url, чтобы воспроизвести поведение __dirname.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.