From 545fd75c2bd07a538988baabcfe7aa4c7365bc12 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lo=C3=AFs=20Poujade?= <lois.poujade@tetras-libre.fr>
Date: Tue, 27 Sep 2022 11:57:53 +0200
Subject: [PATCH] Add a dev server

- all local files (sources, dependencies, config, etc) are mounted in
  the devserver container
- devserver watch sources & rebuild/reload on each change
- "prod" server was updated to allow devserver to access its ressources
- "prod" server now use npm ci instead of install and webpack
  `production` mode for build
---
 .dockerignore        |  1 +
 .env.template        | 13 +++++++++++--
 Caddyfile            |  5 +++--
 Dockerfile           |  2 +-
 Dockerfile.devserver |  6 ++++++
 README.md            | 10 ++++++++++
 devserver.yml        | 13 +++++++++++++
 docker-compose.yml   |  3 +++
 package.json         |  5 +++--
 webpack.config.js    | 29 +++++++++++++++++++++++------
 10 files changed, 74 insertions(+), 13 deletions(-)
 create mode 100644 .dockerignore
 create mode 100644 Dockerfile.devserver
 create mode 100644 devserver.yml

diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..c2658d7
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+node_modules/
diff --git a/.env.template b/.env.template
index dea774d..684fbda 100644
--- a/.env.template
+++ b/.env.template
@@ -1,7 +1,16 @@
-# host port
+# docker-compose components. Append ':devserver.yml' to also run the dev server (default is prod only)
+COMPOSE_FILE=docker-compose.yml
+# prod server host port
 PORT=8080
-# container restart policy
+# prod container restart policy
 RESTART=unless-stopped
+# dev server host port
+DEV_PORT=9000
+# dev server container restart policy
+DEV_RESTART=unless-stopped
 # HTTP folder, will be served at http://localhost:$PORT/data
 # use absolute path or relative path starting with ./
 HTTP_FOLDER=./www
+# hosts allowed to access ressources from $HTTP_FOLDER
+# * to allow all, http://localhost:$DEV_PORT to allow only devserver
+CORS_ALLOWED_HOSTS=http://localhost:$DEV_PORT
diff --git a/Caddyfile b/Caddyfile
index f3c6a8f..17dfffa 100644
--- a/Caddyfile
+++ b/Caddyfile
@@ -1,4 +1,5 @@
 :80 {
-  root * /srv
-  file_server browse
+	root * /srv
+	file_server browse
+	header Access-Control-Allow-Origin "{env.CORS_ALLOWED_HOSTS}"
 }
diff --git a/Dockerfile b/Dockerfile
index d89317f..78ed874 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,7 +3,7 @@ RUN apk add npm git
 
 COPY . /opt
 WORKDIR /opt
-RUN npm install
+RUN npm ci
 RUN npm run build
 
 FROM caddy:latest as httpd
diff --git a/Dockerfile.devserver b/Dockerfile.devserver
new file mode 100644
index 0000000..f50507a
--- /dev/null
+++ b/Dockerfile.devserver
@@ -0,0 +1,6 @@
+FROM node:16-alpine
+RUN apk add git
+EXPOSE 9000
+WORKDIR /opt
+USER node
+CMD npm run serve
diff --git a/README.md b/README.md
index cedf460..155805f 100644
--- a/README.md
+++ b/README.md
@@ -7,3 +7,13 @@ Run `docker-compose up -d`, which will serve a mirador instance at `http://local
 The `$HTTP_FOLDER` (`./www` by default) directory will be accessible via HTTP at `http://localhost:$PORT/data` and can be used to store manifests and theirs ressources and see them in Mirador.  
 
 If sources files are modified, run `docker-compose up -d --build` to update Mirador  
+
+
+#### Use the development server
+
+Follow the previous instructions if you want to access ressources from `$HTTP_FOLDER` via HTTP in the devserver.  
+
+Edit `.env` (copy it from `.env.template` if needed), set `COMPOSE_FILE=docker-compose.yml:devserver.yml` and adapt `DEV_*` variables to your needs.  
+
+Run `docker-compose up -d --build devserver`, which will serve a mirador instance at `http://localhost:$DEV_PORT` (default port is 9000) with live rebuild/reload enabled on each `src/` and `public/` files modifications.  
+
diff --git a/devserver.yml b/devserver.yml
new file mode 100644
index 0000000..8d6b8bf
--- /dev/null
+++ b/devserver.yml
@@ -0,0 +1,13 @@
+version: "3.9"
+services:
+  devserver:
+    build:
+      context: .
+      dockerfile: Dockerfile.devserver
+    restart: $DEV_RESTART
+    ports:
+      - $DEV_PORT:9000
+    volumes:
+      - ./:/opt
+    environment:
+      - WEBPACK_MODE=development
diff --git a/docker-compose.yml b/docker-compose.yml
index 5c7b261..31ae053 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -5,6 +5,9 @@ services:
     restart: $RESTART
     ports:
       - $PORT:80
+    environment:
+      - CORS_ALLOWED_HOSTS
+      - WEBPACK_MODE=production
     volumes:
       - $HTTP_FOLDER:/srv/data
       - ./Caddyfile:/etc/caddy/Caddyfile
diff --git a/package.json b/package.json
index c247ee3..a3cc34e 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
   "private": true,
   "scripts": {
     "build": "webpack --config webpack.config.js",
-    "serve": "webpack serve --config webpack.config.js"
+    "serve": "npm install && webpack serve --config webpack.config.js"
   },
   "author": "",
   "license": "ISC",
@@ -18,6 +18,7 @@
   },
   "devDependencies": {
     "webpack": "^4.43.0",
-    "webpack-cli": "^4.3.12"
+    "webpack-cli": "^4.3.12",
+    "webpack-dev-server": "^4.11.1"
   }
 }
diff --git a/webpack.config.js b/webpack.config.js
index d487af5..4fc1dec 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,10 +1,27 @@
 const path = require('path');
+const webpack = require('webpack');
 
 module.exports = {
-  entry: './src/index.js',
-  output: {
-    filename: 'main.js',
-    path: path.resolve(__dirname, 'public/dist'),
-    publicPath: './dist/',
-  }
+    mode: process.env.WEBPACK_MODE,
+    entry: './src/index.js',
+    output: {
+      filename: 'main.js',
+      path: path.resolve(__dirname, 'public/dist'),
+      publicPath: '/dist/',
+    },
+    devServer: {
+      hot: true,
+      watchFiles: ['src/**/*'],
+      static: {
+        directory: path.join(__dirname, 'public'),
+        watch: true
+      },
+      port: 9000
+    },
+    plugins: [
+      new webpack.IgnorePlugin({
+        /* cf https://gitlab.tetras-libre.fr/iiif/mirador-video-annotation/-/blob/annotation-on-video/webpack.config.js#L42 */
+        resourceRegExp: /@blueprintjs\/(core|icons)/, // ignore optional UI framework dependencies
+      })
+    ]
 };
-- 
GitLab