Программирование

Как исправить ошибку 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, который выглядит так:

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, вам нужно:

  1. Добавить Babel‑runtime, который будет транспилировать модули «на лету» (или предварительно компилировать их).
  2. Настроить Babel так, чтобы он таргетировал текущую версию Node и преобразовывал ES‑модули в CommonJS.
  3. Запускать приложение через babel-node или node -r @babel/register, чтобы трансляция происходила до выполнения кода.

Ниже полный пошаговый набор, который работает с проектом Apollo Server.


Содержание


Что не так с текущей настройкой?

Ваш 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 – Установите нужные пакеты

bash
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 вместо:

bash
npm install --save-dev @babel/register

Шаг 2 – Создайте конфигурацию Babel

Создайте файл babel.config.json (или .babelrc) в корне проекта:

json
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "current"
        }
      }
    ]
  ],
  "plugins": []
}
  • targets.node: "current" говорит Babel генерировать код, совместимый с текущей версией Node.
  • Пресет автоматически переписывает import/export в CommonJS, поэтому дополнительный плагин не нужен.

Если хотите оставить исходные файлы нетронутыми и собрать их в папку dist/, добавьте скрипт сборки позже (см. Шаг 5).


Шаг 3 – Обновите скрипты в package.json

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 – Запустите сервер

  1. Разработка

    bash
    npm run dev
    

    Это выполнит src/index.js через babel-node. Babel‑runtime автоматически преобразует ваши import.

  2. Продакшн

    bash
    npm run build
    npm run start
    

    После шага сборки код в dist/ уже совместим с CommonJS, так что обычный Node может его запустить.


Опционально: один раз собрать для продакшена

Если хотите одну команду, которая соберёт и запустит:

bash
npm run build && npm run start

Или добавьте новый скрипт:

json
"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‑окружении.

Авторы
Проверено модерацией
Модерация