Рендеринг на стороне сервера: ошибка синтаксиса узла: невозможно использовать оператор импорта вне модуля

#reactjs #babeljs #es6-modules

#reactjs #babeljs #es6-модули

Вопрос:

итак, после нескольких дней поиска или поиска в Google и пробования некоторых других настроек я не могу исправить эту ошибку.

Предыстория: я пишу приложение react с рендерингом на стороне сервера. Раньше я писал много приложений react, но только с рендерингом на стороне клиента, но для нового проекта я решил попробовать SSR, и он работает, пока я не добавлю Formik в микс, который импортирует lodash-es . После более глубокого изучения кажется, что модули ES node_modules не «транспилируются», поэтому при запуске сервера происходит сбой, потому что узел, похоже, не может его импортировать. Вот ошибка:

 $ cross-env NODE_ENV=development webpack --config webpack.config.js --mode=development
$ node dist/server.js
.../node_modules/lodash-es/isPlainObject.js:1
import baseGetTag from './_baseGetTag.js';
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (node:internal/modules/cjs/loader:1018:16)
    at Module._compile (node:internal/modules/cjs/loader:1066:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1131:10)
    at Module.load (node:internal/modules/cjs/loader:967:32)
    at Function.Module._load (node:internal/modules/cjs/loader:807:14)
    at Module.require (node:internal/modules/cjs/loader:991:19)
    at require (node:internal/modules/cjs/helpers:92:18)
    at eval (webpack://@nesta-se/admin/external_"lodash-es/isPlainObject"?:1:18)
    at Object.lodash-es/isPlainObject (/Users/benjamin/GitHub/nesta-admin/build/server.js:809:1)
    at __webpack_require__ (/Users/benjamin/GitHub/nesta-admin/build/server.js:1077:32)
 

Я использую Webpack и Babel для переноса своего кода.

babel.config.js

 {
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "debug": true,
        "corejs": 3,
        "targets": {
          "browsers": [
            "last 2 versions"
          ],
          "node": "current"
        }
      }
    ],
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-syntax-dynamic-import",
    "lodash",
    [
      "babel-plugin-styled-components",
      {
        "displayName": true,
        "ssr": true
      }
    ]
  ]
}
 

webpack.config.js

 const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const nodeExternals = require('webpack-node-externals');

var common = {
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/(?!lodash-es)/,
        include: path.resolve(__dirname, 'src'),
        use: ['babel-loader'],
      },
      // {
      //   test: /.(gif|png|jpe?g|svg)$/i,
      //   use: ['file-loader'],
      // },
    ],
  },
  mode: 'development',

  output: {
    chunkFilename: '[chunkhash].js',
    // https://reactjs.org/docs/cross-origin-errors.html
    crossOriginLoading: 'anonymous',
    filename: '[name].js',
    publicPath: '/',
  },
};

var server = merge(common, {
  name: 'server',
  target: 'node',
  externals: [nodeExternals()],

  entry: {
    server: path.resolve(__dirname, 'src', 'server.js'),
  },

  output: {
    filename: 'server.js',
    path: path.resolve('build'),
    publicPath: '/',
  },

  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ],

  node: {
    global: false,
    __filename: false,
    __dirname: false,
  },
});

var client = = merge(common, {
  devtool: 'cheap-module-source-map',
  devServer: {
    hot: true,
  },
  name: 'client',
  target: 'web',

  entry: {
    client: [
      'react-hot-loader/babel',
      'webpack-hot-middleware/client',
      path.resolve('src', 'client.js'),
    ],
  },

  output: {
    filename: '[name].js',
    path: path.resolve('build'),
    publicPath: '/',
  },

  plugins: [new webpack.HotModuleReplacementPlugin()],

  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          chunks: 'initial',
          name: 'vendor',
          test: (module) => /node_modules/.test(module.resource),
          enforce: true,
        },
      },
    },
  },
});

module.exports = [client, server];
 

И это зависимости от моего package.json :

 "dependencies": {
    "compression": "^1.7.4",
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "express-http-proxy": "^1.6.2",
    "file-loader": "^6.2.0",
    "formik": "^2.2.1",
    "isomorphic-fetch": "^3.0.0",
    "lodash": "^4.17.20",
    "method-override": "^3.0.0",
    "morgan": "^1.10.0",
    "react": "^16.13.0",
    "react-device-detect": "^1.14.0",
    "react-dom": "^16.13.0",
    "react-helmet": "^6.1.0",
    "react-is": "^17.0.1",
    "react-redux": "^7.2.0",
    "react-router-dom": "^5.1.2",
    "redux": "^4.0.5",
    "redux-freeze": "^0.1.7",
    "redux-logger": "^3.0.6",
    "styled-components": "^5.2.0"
  },
  "devDependencies": {
    "@babel/core": "^7.12.9",
    "@babel/preset-env": "^7.12.7",
    "@babel/preset-react": "^7.12.7",
    "@babel/runtime": "^7.12.5",
    "babel-loader": "^8.2.2",
    "babel-plugin-lodash": "^3.3.4",
    "babel-plugin-styled-components": "^1.12.0",
    "babel-preset-airbnb": "^5.0.0",
    "core-js": "^3.8.0",
    "cross-env": "^7.0.3",
    "nodemon": "^2.0.6",
    "npm-run-all": "^4.1.5",
    "react-hot-loader": "^4.13.0",
    "stylelint": "^13.7.2",
    "stylelint-config-recommended": "^3.0.0",
    "stylelint-config-styled-components": "^0.1.1",
    "stylelint-processor-styled-components": "^1.10.0",
    "webpack": "^5.10.0",
    "webpack-cli": "^4.2.0",
    "webpack-dev-middleware": "^4.0.2",
    "webpack-hot-middleware": "^2.25.0",
    "webpack-merge": "^5.2.0",
    "webpack-node-externals": "^2.5.2"
  },
 

Комментарии:

1.Почему вы exclude , а не include lodash-es для решения этой проблемы? Если это не исправит проблему, то, что я обычно делаю для отладки подобных проблем, — это удалить exclude и test т. Д. И Использовать function для my include , который делает все вместо этого. Это помогает мне понять, что в настоящее время используется, а что нет, примерно так: ignore: [function shouldIgnore(modulePath, ...otherArgs) { const shouldIgnore = /* your ignore logic goes here */; console.log('[BABEL]', modulePath, shouldIgnore); return shouldIgnore; }]

2.В случае, если ваша babel конфигурация по умолчанию имеет некоторый конфликт с lodash-es , просто добавьте отдельный babel-loader rule с его собственной конфигурацией, возможно, просто с preset-env , чтобы позаботиться о import операторах.

3. ПРИМЕЧАНИЕ: я только что осознал свою ошибку наблюдения — негативный прогноз в вашем exclude . У меня был худший опыт использования регулярных выражений в babel exclude и include . С другой стороны, я обнаружил, что include функциональное решение всегда работало (по крайней мере, после достаточной отладки).