Webpack

#php #webpack #proxy #localhost #remote-server

Вопрос:

Я уже некоторое время борюсь с проблемой:
Я хотел бы иметь локальную среду разработки webpack/encore с HMR (замена горячих модулей), которая использует индексный файл, созданный сервером (из Contao-CMS/PHP/MySQL).

Подробно:
У меня есть удаленный сервер, доступный по протоколу HTTPS (порт 443). Я использую пакет управляемого хостинга, поэтому, к сожалению, я не могу запустить сервер webpack на альтернативном порту, мне приходится придерживаться портов 80 и 443.
На этом удаленном сервере работает база данных MySQL и PHP (Composer). Содержимое страницы управляется CMS Contao.
Этот удаленный сервер генерирует структуру PHP/HTML/DOM и данные/содержимое.

У меня также есть локальная среда программирования с Webpack, Encore и моими стилями/javascripts. Я не хочу дублировать свою текущую удаленную установку Contao/MySQL там. Я бы предпочел получить структуру с удаленного сервера для использования в моей локальной среде.

Моей любимой настройкой была бы:

Удаленный сервер доступен под https://myserver.de:443
(Порт 443, HTTPS, построен npm run build , работает стабильно, общедоступен)

Локальный сервер работает под https://localhost:443 (самозаверяющий) для разработки новых стилей/скриптов: он должен взять все с удаленного сервера (HTML-код/структура/дом, скрипты, изображения, контент), но и обслуживать локальные стили/макетов/скриптов и активировать горячая замена модуля (ГРМ) для автоматической перезагрузки на изменения. В принципе, я хотел бы редактировать/переписывать свой стиль в реальном времени, используя фреймворк (и содержимое) с существующей удаленной страницы.

Моя проблема в том, что:

Это должно быть управляемо с помощью прокси-сервера.
Однако обычно вы просто отправляете API-запрос, чтобы получить только данные с удаленного сайта.
На локальном хосте (обычно) у вас есть индекс(.html/php), сценарии/стили и все остальное, и вы получаете только данные/контент через прокси-запрос API.

В моей любимой настройке это не работает. Структура и содержимое включены в мой (PHP/MySQL/Contao, сгенерированный и шаблонизированный) index.php, и здесь должны быть введены правильные стили/скрипты/HMR с локального хоста. (какой веб-пакет должен быть готов из коробки)

Так что, в принципе, я должен запросить/загрузить/прокси-сервер index.php с удаленного сервера и внедрите туда мои стили/скрипты webpack/encore, чтобы внести изменения в стиль и работать с HMR.

Мой план таков:

  1. прокси-сервер все с удаленного сервера, кроме /layout/ (где хранятся сгенерированные на бис стили/скрипты)
  2. скачать/прокси-сервер index.php (сгенерированная сервером корневая страница со всей структурой из шаблонов Contao-CMS, данными из базы данных MySQL и ссылками на скрипты/стили) из https://myserver.de
  3. Webpack должен «как-то» заменить файлы стилей/сценариев (начиная с /layout/ in index.php) и добавьте скрипт HMR и доставьте его как корневую страницу с https://localhost
  4. На https://localhost, структура HTML/DOM/Данных/шаблонов с удаленного сервера доставляется, но стиль и сценарии (/макет/) из локальной среды используются и динамически перезагружаются HMR.

This can’t be this difficult, can it? I assumed that this (server-proxy-localhost-thing) would be a quite common application of Webpack/Encore with HMR?

What I’ve tried so far:

  • I managed to make a dev-server on localhost npm run local and a second dev-server on remote npm run server . The server at https://mydomain.de loads the /layout/ files from the local server https://localhost:443/layout/. When saving local files, they are instantly uploaded to the remote server. Both webpack servers are rebuilt and the HMR on https://mydomain.de is reloaded. Yes, this is ugly, and yes, it works! However, if anybody NOT using the local webpack server wants to view the page, the styles can’t be loaded (as loading from https://localhost:443/layout of course leads to a timeout).
  • I managed to proxy everything (except /layout/) to localhost. On the root page (/), I disabled compression (otherwise, gzip would lead to problems, even when using the new onProxyRes: responseInterceptor() function from http-proxy-middleware , which still seems a bit buggy to me). Then, I am able to regex-replace the files from https://mydomain.de/layout/xyz.hexcode.css to https://localhost/layout/xyz.css. However, webpack generates (and combines) the files dynamically and I can’t be sure if the xyz files have exact counterparts on localhost. Also, I did not yet manage to inject the HMR code.

But «somehow» this must be achieved easier without manually «searchamp;replace»? Webpack/Encore/HMR already know which files are needed, so they «somehow» should just inject them into the (proxied/downloaded) root page!?
What am I missing?

My files:

part of index.php from https://myserver.de

 <!-- these layouts/scripts are from Contao and not served via Webpack/Encore -->
<link rel="stylesheet" href="assets/css/layout.min.css,responsive.min.css,colorbox.min.css,tablesorter.m...-0568d07b.css"> 
<script src="assets/js/jquery.min.js-55e8b57b.js"></script>

<!-- these layout/scripts are generated via Encore and should be served lokally with HMR -->
<link rel="stylesheet" href="/layout/css/base.a195259b3b6d1c0ea116.css">
<link rel="stylesheet" href="/layout/css/3.c051ea961ca07ebe9603.css">
<link rel="stylesheet" href="/layout/css/fonts.1cad325240e751e2982a.css">
<link rel="stylesheet" href="/layout/css/news.25dbe26df9e81062227b.css">
<link rel="stylesheet" href="/layout/css/player.ab38e835c60a5569740b.css">
<link rel="stylesheet" href="/layout/css/nav.bae121a49552892e1ec9.css">
<link rel="stylesheet" href="/layout/css/search.424f2d130fdf3fc848dd.css">
<link rel="stylesheet" href="/layout/css/image.8f8b30f024d7de8ae5dd.css">
<link rel="stylesheet" href="/layout/css/accordion.3167aff65e6d694465f3.css">
<link rel="stylesheet" href="/layout/css/lyrics_chords.b7c98d9b5fe8443510de.css">
<script src="/layout/js/runtime.de3cb55993caa785606e.js"></script>
<script src="/layout/js/0.68475f916dd09a70f2ed.js"></script>
<script src="/layout/js/1.b0717b92f1874e527d29.js"></script>
<script src="/layout/js/base.5b6ff652693c4338465a.js"></script>
<script src="/layout/js/3.a860c6c3271280c8338d.js"></script>
<script src="/layout/js/fonts.fd828da43496442d0bd4.js"></script>
<link rel="stylesheet" href="/layout/css/code.59f6e92a9deeff93d3c4.css">
<script src="/layout/js/nav.f5f43f643bacbe9a2b0f.js"></script>

 

part of package.json:

   "https": {
    "cert": "key/private.pem",
    "key": "key/private.key",
    "port": "443"
  },
  "scripts": {
    "local": "encore dev-server --hot --watch --disable-host-check --https --port 443 --cert key/private.pem --key key/private.key",
    "server": "encore dev-server --hot --watch --disable-host-check --host localhost --https --port 443",
    "dev": "encore dev-server --hot --disable-host-check --host localhost --proxy https://mydomain.de",
    "build": "encore production --progress --profile",

     ... and some other test scripts
     ... and lots of dependencies
  },
 

(unfortunately, —https, —cert and —key have to be added explicitely by CLI. Otherwise, it tries to load the /layout/ — files via HTTP. But let’s concentrate on npm run dev here)

(very small) part of webpack.config.js

 Encore
  .setOutputPath("web/layout/")
  .setPublicPath("/layout")
  .addEntry("base", "./src/assets/config/base.js")
  .addEntry("fonts", "./src/assets/config/fonts.js")
  .addEntry("code", "./src/assets/config/code.js")
  ...
  .configureDevServerOptions((options) => {
    //options.http2 = true;
    options.port = process.env.npm_package_https_port;
    options.https = {
      key: process.env.npm_package_https_key,
      cert: process.env.npm_package_https_cert,
    };
    options.compress = true;
    options.hot = true;
    options.publicPath = "./web";
    options.public = "https://localhost:443"; //process.env.npm_package_remote_proxy;
    options.historyApiFallback = true;

    //options.injectHot = true;
    //options.inline = false;
    options.index = "index.php";
    options.writeToDisk = true;

    // if proxy is activated: build proxy (does not work correctly: HMR is not injected)
    if (process.env.npm_lifecycle_script.match(/--proxy ([^ ] )/)) {
      const proxy = process.env.npm_lifecycle_script.match(/--proxy ([^ ] )/)[1];
      options.proxy = [
        {
          target: proxy,
          changeOrigin: true,
          context: (pathname, req) => {
            // Proxy everything except layout
            if (pathname.startsWith("/layout/")) return false;
            return true;
          },
          onProxyReq: (proxyReq, req, res) => {
            if (req.url === "/") {
              // turn off compression
              proxyReq.setHeader("accept-encoding", "identity");
            }
          },
          onProxyRes: (proxyRes, req, res) => {
            let originalBody = Buffer.from([]);
            proxyRes.on("data", (data) => {
              originalBody = Buffer.concat([originalBody, data]);
            });

            proxyRes.on("end", () => {
              let bodyString = originalBody.toString("utf8");

              if (req.url === "/") {
                var proxyRegex = new RegExp(proxy, "g");
                bodyString = bodyString.replace(proxyRegex,"https://localhost");
                bodyString = bodyString.replace(/(/layout/(. ?))(.[a-f0-9] ?)(.(js|css))/g,"$1$4");
                bodyString = bodyString.replace(/hello/g, "Goodbye");
              }
              //console.log(newBody);

              res.set({
                "content-type": "text/html; charset=utf-8",
                //"content-encoding": "gzip",
              });
              //res.write(zlib.gzipSync(newBody));
              res.write(bodyString);
              res.end();
            });
          },
          selfHandleResponse: true,
        },
      ];
    }
    options.progress = true;
  })
  .enableBuildNotifications(false)
  .copyFiles([
    {
      from: "./src/assets/fonts",
      to: "./fonts/[path][name].[ext]",
    },
    {
      from: "./src/assets/vendor",
      to: "./vendor/[path][name].[ext]",
    },
  ])
  ... and other Encore-Related stuff with generating Favicons, Versioning, Stats, Filenames, converting SCSS to CSS, etc.