Как успешно собрать проект NodeJS с движком шаблонов pug с помощью webpack 4 - PullRequest
2 голосов
/ 03 августа 2020

Это будет немного долго, и я подумал, что, возможно, мне следует разделить его на два вопроса, но я чувствую, что все это связано вместе и дает лучший контекст, так что вот ... Мои попытки сборки были неудачными почти два дней, и я не могу найти никаких решений. Мой проект - это в основном REST API со страницей индекса, доступной через localhost:5000. Структура проекта следующая:

controllers
init
  - db.js
models
public
  - css
routes
views
  - layout.pug
  - index.pug
.babelrc
.env
app.js
start.js
package.json
webpack.server.config.js
webpack.prod.config.js

Точкой входа в проект является start.js, который импортирует функцию из файла db.js и константу app из файла app.js. Файл app.js импортирует и настраивает express const app = express(), добавляет все соответствующее промежуточное ПО и экспортирует константу app. Файл db.js в папке init экспортирует функцию (export default function { ... }), которая инициализирует базу данных. app.js и db.js объединяются внутри start.js, что выглядит так ...

import InitializeDB from "./init/db";
import app from "./app";

InitializeDB();
app.set("port", process.env.PORT || 3000);
const server = app.listen(app.get("port"), () => {
  console.log(`Express running → PORT ${server.address().port}`);
});

И поскольку я хотел использовать импорт из ES6, я установил babel npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node и добавил это в .babelrc

{   
  "presets": [
    "@babel/preset-env"   
  ] 
}

Мои package.json сценарии:

  "scripts": {
    "build": "rm -rf dist && webpack --mode production --config webpack.server.config.js && webpack --mode production --config webpack.prod.config.js",
    "start": "node ./dist/server.bundle.js",
    "watch": "nodemon --exec babel-node start.js --ignore public/",
    "start:dev": "concurrently \"npm run test\" \"npm run watch\"",
    "test": "echo \"Testing...\""
  },

Локально, npm run start:dev все работает нормально. Однако на этом мой успех заканчивается.

Моя первая проблема в том, что когда я пытаюсь запустить node ./start.js, я получаю сообщение об ошибке (node:149228) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. Тогда я добавляю "type": "module" в свой package.json и запускаю npm run start:dev снова, но затем появляется ошибка ...

internal/modules/run_main.js:54
    internalBinding('errors').triggerUncaughtException(
                              ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'G:\apps\myproject\init\db' imported from G:\apps\myproject\start.js
Did you mean to import ../init/db.js?

Это моя первая проблема. Теперь о веб-пакете ...

My webpack.server.config.js ...

const path = require("path");
const webpack = require("webpack");
const nodeExternals = require("webpack-node-externals");

module.exports = {
  entry: {
    server: "./start.js",
  },
  output: {
    path: path.join(__dirname, "dist"),
    publicPath: "/",
    filename: "[name].bundle.js",
  },
  target: "node",
  node: {
    __dirname: false,
    __filename: false,
  },
  externals: [nodeExternals()],
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },
};

My webpack.prod.config.js file ...

const path = require("path");
const webpack = require("webpack");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = {
  entry: {
    app: "./start.js",
  },
  output: {
    path: path.join(__dirname, "dist"),
    publicPath: "/",
    filename: "[name].bundle.js",
  },
  target: "node",
  devtool: "source-map",
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourceMap: true,
      }),
      new OptimizeCSSAssetsPlugin({}),
    ],
  },
  module: {
    rules: [
      {
        test: /\.pug$/,
        use: ["pug-loader"],
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: ["file-loader"],
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
      },
    ],
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: "./views/index.pug",
    }),
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css",
    }),
  ],
};

Для webpack.prod.config.js, некоторые предлагают изменить target на web вместо node, но у меня это не сработало.

Когда я запускаю npm run build с "type": "module", установленным в package.json, webpack выдает ошибку

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: G:\apps\myproject\webpack.server.config.js
require() of ES modules is not supported.
require() of G:\apps\myproject\webpack.server.config.js from G:\apps\myproject\node_modules\webpack-cli\bin\utils\convert-argv.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename webpack.server.config.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from G:\apps\myproject\package.json.

Итак, я удаляю "type": "module" из package.json и снова запускаю npm run build, и на этот раз я получаю кучу ... Critical dependency: the request of a dependency is an expression и Module not found: Error: Can't resolve 'net' in 'G:\apps\myproject\node_modules\... для нескольких пакеты.

Эти проблемы меня немного грызут. Мы будем благодарны за вашу помощь.

1 Ответ

1 голос
/ 12 августа 2020

Это моя первая проблема. Теперь о веб-пакете ...

Ваша первая проблема должна обновить вашу кодовую базу, поскольку ESM требует:

  • все ваши файлы *.js должны быть переименованы в *.mjs. Добавление {type: 'module'} в ваш package.json позволяет избежать этой утомительной операции, так как она включит / переопределит эту проверку
  • , если вы используете node> = 8 <13, вы должны запустить свой сервер с <code>node --experimental-modules start.js
  • ESM не поддерживает require, exports, module.exports, __filename, __dirname, поэтому вам нужно изменить __dirname на:
import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));

...
app.use(express.static(path.join(__dirname, 'public')))
...
  • , весь относительный импорт ESM должен иметь расширение, поэтому вам нужно изменить весь свой импорт на:
import Routes from './routes/index.js' // added .js
// etc.. for all the files

В конце node --experimental-modules start.js начнется:

экран

Для части webpack вместо этого вам нужно:

npm i @open-wc/webpack-import-meta-loader -D

И добавить его в свой webpack.prod.config.js файл (я просто удаляю UglifyJsPlugin, так как он не работал):

{
  test: /\.js$/,
  loader: require.resolve('@open-wc/webpack-import-meta-loader')
}
...