Как исправить ошибку import statement outside a module в Node.js
Узнайте, как устранить SyntaxError: Cannot use import statement outside a module в проекте Node.js с Babel 7, используя Apollo Server. Пошаговое руководство и скрипты.
Как исправить SyntaxError: Cannot use import statement outside a module в проекте Node.js ApolloServer с Babel 7?
У меня есть файл index.js, который выглядит так:
require('dotenv').config();
import { startServer } from './server';
startServer();
При запуске я получаю:
SyntaxError: Cannot use import statement outside a module
Я пробовал несколько вариантов:
- Добавил
type="module"(это для браузеров, а не для Node). - Переключил
importнаrequire, что работает, но я бы предпочёл оставлять ES6‑модули. - Использовал старые пресеты Babel (
"presets": ["es2015", "stage-2"]), но тогда появляется ошибка:Error: Plugin/Preset files are not allowed to export objects, only functions.
Вопрос: Какую конфигурацию Babel или настройку Node мне нужно применить, чтобы включить синтаксис ES6 import в обычной среде Node.js (без браузера) при работе с Apollo Server? Какие пресеты/плагины следует использовать, и как правильно оформить package.json и конфигурацию Babel, чтобы избежать ошибки импорта?
Любое пошаговое руководство или пример конфигурации будет очень полезен.
Ошибка возникает потому, что Node v12+ запускает JavaScript как CommonJS по умолчанию.
Если вы хотите оставить свой код написанным с использованием ES 6 import, но при этом запускать его в обычном Node, вам нужно:
- Добавить Babel‑runtime, который будет транспилировать модули «на лету» (или предварительно компилировать их).
- Настроить Babel так, чтобы он таргетировал текущую версию Node и преобразовывал ES‑модули в CommonJS.
- Запускать приложение через
babel-nodeилиnode -r @babel/register, чтобы трансляция происходила до выполнения кода.
Ниже полный пошаговый набор, который работает с проектом Apollo Server.
Содержание
- Что не так с текущей настройкой?
- Шаг 1 – Установите нужные пакеты
- Шаг 2 – Создайте конфигурацию Babel
- Шаг 3 – Обновите скрипты в
package.json - Шаг 4 – Запустите сервер
- Опционально: один раз собрать для продакшена
- Частые ошибки
- Заключение
- Источники
Что не так с текущей настройкой?
Ваш index.js смешивает CommonJS (require('dotenv')) и ES‑модули (import).
Node считает файл CommonJS, потому что в package.json нет поля "type": "module".
Когда он встречает строку import, выбрасывает:
SyntaxError: Cannot use import statement outside a module
Babel может преобразовать import в require, но трансляция должна происходить до того, как Node попытается распарсить файл.
Шаг 1 – Установите нужные пакеты
npm install --save-dev @babel/core @babel/cli @babel/node @babel/preset-env
@babel/core– движок Babel.@babel/cli– опционально, используется для компиляции в папкуdist.@babel/node– обёртка над Node, которая автоматически регистрирует Babel.@babel/preset-env– компилирует современный JavaScript под целевую среду (Node).
Если хотите транспилировать «на лету» без babel-node, установите @babel/register вместо:
npm install --save-dev @babel/register
Шаг 2 – Создайте конфигурацию Babel
Создайте файл babel.config.json (или .babelrc) в корне проекта:
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
],
"plugins": []
}
targets.node: "current"говорит Babel генерировать код, совместимый с текущей версией Node.- Пресет автоматически переписывает
import/exportв CommonJS, поэтому дополнительный плагин не нужен.
Если хотите оставить исходные файлы нетронутыми и собрать их в папку dist/, добавьте скрипт сборки позже (см. Шаг 5).
Шаг 3 – Обновите скрипты в package.json
{
"name": "apollo-babel-example",
"version": "1.0.0",
"main": "dist/index.js",
"scripts": {
"dev": "babel-node src/index.js",
"build": "babel src -d dist",
"start": "node dist/index.js"
},
"devDependencies": {
"@babel/core": "^7.x",
"@babel/cli": "^7.x",
"@babel/node": "^7.x",
"@babel/preset-env": "^7.x"
}
}
Пояснение
| Скрипт | Что делает | Когда использовать |
|---|---|---|
dev |
Запускает код напрямую через babel-node; удобно для разработки. |
npm run dev |
build |
Транспилирует весь каталог src/ в dist/. |
npm run build |
start |
Запускает скомпилированный код обычным Node. | npm run start (продакшн) |
Совет:
babel-nodeотлично подходит для dev, но в продакшене лучше избегать его, так как он компилирует при каждом старте.
Шаг 4 – Запустите сервер
-
Разработка
bashnpm run dev
Это выполнит
src/index.jsчерезbabel-node. Babel‑runtime автоматически преобразует вашиimport. -
Продакшн
bashnpm run build npm run start
После шага сборки код в
dist/уже совместим с CommonJS, так что обычный Node может его запустить.
Опционально: один раз собрать для продакшена
Если хотите одну команду, которая соберёт и запустит:
npm run build && npm run start
Или добавьте новый скрипт:
"serve": "npm run build && npm start"
Теперь npm run serve выполнит сборку и запустит сервер за один шаг.
Частые ошибки
| Проблема | Причина | Как исправить |
|---|---|---|
Импорт файла без расширения .js (import { foo } from './foo') |
В Node без "type": "module" разрешение модуля требует расширения. |
Добавьте расширение: import { foo } from './foo.js'. |
Babel не запускается, потому что вы используете node src/index.js напрямую |
Node не знает о трансформациях Babel. | Используйте babel-node или вручную зарегистрируйте Babel (require('@babel/register') в начале). |
require('dotenv').config(); после import |
Смешивание синтаксиса в одном файле может сбивать некоторые настройки Babel. | Перенесите вызов dotenv в отдельный файл или поместите его до любых import. |
@babel/register выдаёт «Plugin/Preset files are not allowed to export objects» |
Используется пресет, ожидающий синтаксис Babel 6. | Убедитесь, что используете пресеты Babel 7 (@babel/preset-env, а не es2015, stage-2). |
Заключение
- Babel + Node: настройте
@babel/preset-envпод текущую версию Node и запускайте код черезbabel-node(для dev) или соберите один раз и запускайте обычным Node (для prod). type="module"не нужен: Babel сам преобразует модули, так что вы можете писатьimportв исходниках.- Рабочий цикл скриптов:
npm run devдля быстрой итерации,npm run build && npm run startдля продакшена. - Apollo Server работает без проблем после транспиляции, так что можно продолжать использовать
import { ApolloServer } from 'apollo-server'без дополнительных настроек.
С этой конфигурацией вы больше не увидите ошибку «Cannot use import statement outside a module», и ваш проект Apollo Server будет плавно работать в обычном Node‑окружении.