语法错误:dockerizing react app时出现意外令牌'<'

时间:2020-02-16 10:42:14

标签: node.js reactjs docker webpack docker-compose

我有一个Webpack的react项目,我正在尝试在this article之后对其进行docker化。

Dockerfile:

FROM node:12.14.1

RUN npm install webpack -g

WORKDIR /tmp
COPY package.json /tmp/
RUN npm config set registry http://registry.npmjs.org/ && npm install

WORKDIR /usr/app
COPY . /usr/app/
RUN cp -a /tmp/node_modules /usr/app/

RUN webpack

ENV NODE_ENV=production
ENV PORT=4000

CMD [ "/usr/local/bin/node", "--experimental-modules", "./src/index.js" ]

EXPOSE 4000

docker-compose.yml:

ex_dashboard:
  build: .
  ports:
  - "80:4000"
  volumes:
  - .:/usr/app/:rw
  environment:
  - NODE_ENV=dev
  command: >
    sh -c '
      if test -d node_modules; 
      then 
        echo node_modules_exists ; 
      else 
        cp -a /tmp/node_modules /usr/app/dashboard; 
      fi && 
      npm install && 
      /usr/local/bin/node --experimental-modules ./src/index.js
    '

运行docker-compose up时,一切正常,直到遇到此错误为止:

ex_dashboard_1  | (node:18) ExperimentalWarning: The ESM module loader is experimental.
ex_dashboard_1  | file:///usr/app/src/index.js:9
ex_dashboard_1  |   <Provider store={store}>
ex_dashboard_1  |   ^
ex_dashboard_1  | 
ex_dashboard_1  | SyntaxError: Unexpected token '<'
ex_dashboard_1  |     at Loader.moduleStrategy (internal/modules/esm/translators.js:66:18)
ex_dashboard_1  |     at async link (internal/modules/esm/module_job.js:37:21)

如果我仅通过npm start运行它或通过npm run build构建它,整个项目就可以很好地工作。但我不知道为什么这会在docker容器中发生。

这里是package.json

{
  "name": "explore_dashboard",
  "version": "1.0.0",
  "description": "A dashboard for the Explore project",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "cross-env NODE_ENV=development webpack-dev-server --hot",
    "build": "cross-env NODE_ENV=production webpack",
    "lint": "eslint ./src",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://gitlab.basalam.dev/data/explore_dashboard.git"
  },
  "author": "Gh.Sherafati",
  "license": "ISC",
  "resolve": {
    "alias": {
      "react-dom": "@hot-loader/react-dom"
    }
  },
  "devDependencies": {
    "@babel/core": "^7.8.4",
    "@babel/plugin-transform-runtime": "^7.8.3",
    "@babel/preset-env": "^7.8.4",
    "@babel/preset-react": "^7.8.3",
    "babel-eslint": "^10.0.3",
    "babel-loader": "^8.0.6",
    "cross-env": "^7.0.0",
    "css-loader": "^3.4.2",
    "eslint": "^6.1.0",
    "eslint-config-airbnb": "^18.0.1",
    "eslint-loader": "^3.0.3",
    "eslint-plugin-import": "^2.20.0",
    "eslint-plugin-jsx-a11y": "^6.2.3",
    "eslint-plugin-react": "^7.18.0",
    "eslint-plugin-react-hooks": "^1.7.0",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.13.1",
    "sass-loader": "^8.0.2",
    "style-loader": "^1.1.3",
    "webpack": "^4.41.5",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.10.2"
  },
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "^1.2.27",
    "@fortawesome/free-solid-svg-icons": "^5.12.1",
    "@fortawesome/react-fontawesome": "^0.1.8",
    "@hot-loader/react-dom": "^16.11.0",
    "@types/react": "^16.9.19",
    "axios": "^0.19.2",
    "react": "^16.12.0",
    "react-beautiful-dnd": "^12.2.0",
    "react-dom": "^16.12.0",
    "react-hot-loader": "^4.12.19",
    "react-redux": "^7.1.3",
    "redux": "^4.0.5",
    "redux-saga": "^1.1.3"
  }
}

还有webpack.config.js

const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const { env } = process;

module.exports = {
  entry: './src/index.js',
  mode: env.NODE_ENV,
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ['babel-loader', 'eslint-loader'],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          // Creates `style` nodes from JS strings
          'style-loader',
          // Translates CSS into CommonJS
          'css-loader',
          // Compiles Sass to CSS
          'sass-loader',
        ],
      },
    ],
  },
  resolve: {
    extensions: ['*', '.js', '.jsx'],
  },
  output: {
    path: path.join(__dirname, '/build'),
    publicPath: '/',
    filename: 'bundle.js',
  },
  plugins: [
    new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV) }),
    new HtmlWebpackPlugin({
      template: path.resolve('./src/index.html'),
    }),
  ],
  devServer: {
    contentBase: './build',
    hot: true,
  },
  devtool: env.NODE_ENV === 'development' ? 'cheap-module-eval-source-map' : undefined,
};

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './redux/store';
import App from './components/App';
import './scss/main.scss';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('app'),
);

module.hot.accept();

3 个答案:

答案 0 :(得分:0)

--experimental-modules标志可用于启用对ECMAScript模块(ES模块)的支持。

选项1

CMD ["sh", "-c", "exec node --experimental-modules index.js"]

选项2

CMD exec node --experimental-modules index.js

Full Docs:

答案 1 :(得分:0)

在您的docker-compose.yml文件中,删除volumes:和冗长的command:

重要的是,您的index.js文件实际上包含React使用的JSX构造;它不是纯Javascript,而node不能直接运行它。在您的Dockerfile中,您RUN webpack将其转换为纯Javascript。 volumes:会用您本地系统上的任何内容覆盖所有这些内容;其中不包含转换步骤,因此您没有有效的Javascript来运行Node。

(在该webpack中对command:进行显式调用可能会起作用。这假定您的主机和容器node_modules是兼容的,并且实际上是在复制{{1 }}会在Dockerfile文件中建立图片。)

没有什么可以阻止您在主机系统上安装Node(如果尚未安装),使用Webpack开发服务器进行实时开发,然后使用Docker进行最终打包的步骤。这可能比引入像Docker这样的隔离系统然后尝试利用其所有功能来模拟本地开发设置要方便得多。

答案 2 :(得分:0)

最后,我已经通过使用nginx并在容器中运行构建版本来解决问题,并从this great article获得帮助。

新的Dockerfiledocker-compose.yml如下:

Dockerfile:

FROM node:12.14.1 as build
WORKDIR /app
COPY . /app
ENV PATH /app/node_modules/.bin:$PATH
RUN npm install
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

docker-compose.yml:

version: "3"
services:
  explore_dashboard:
    container_name: explore_dashboard
    build:
      context: .
      dockerfile:
        Dockerfile
    ports:
      - "8081:80"

nginx.conf:

server {
    listen 80;
    location / {
      root   /usr/share/nginx/html;
      index  index.html index.htm;
      try_files $uri $uri/ /index.html;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
      root   /usr/share/nginx/html;
    }
  }

现在,我可以在http://127.0.0.1:8081/上运行该应用程序了:

docker-compose up