diff --git a/README b/README
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_UkVBRE1F
--- /dev/null
+++ b/README
@@ -0,0 +1,47 @@
+# CookieCutter React TS CwClient
+
+This is a [CookieCutter](https://github.com/cookiecutter/cookiecutter) template to create
+a React, with TypeScript application, initialized with a simple example for
+[@cubicweb/Client](https://www.npmjs.com/package/@cubicweb/client)
+
+## How to use it
+
+First of all you need to
+[install CookieCutter](https://cookiecutter.readthedocs.io/en/stable/installation.html#install-cookiecutter)
+if this is not already installed:
+```bash
+python3 -m pip install --user cookiecutter
+```
+
+then you can create the new project using the CookieCutter template:
+```bash
+cookiecutter ssh://git@forge.extranet.logilab.fr/cubicweb/cookiecutter-react-ts-cwclient
+```
+
+The prompt will ask several questions about project details:
+-  `project_slug`: is the project name, which will be used as `package.json`
+   name and folder name
+- `description`: is the project description which will appear in the
+  `package.json`
+- `author`: is the project author name which will appear in the `package.json`
+- `license`: is the project license which will appear in the `package.json`
+- `cubicweb_server`: is the CubicWeb instance you want to query (/!\ this is
+  ths CubicWeb instance URL, not the API one, the `/api` will be added
+  automatically)
+
+then you can go to the folder:
+```bash
+cd {project_slug}
+```
+and install all needed dependencies:
+```bash
+yarn
+```
+you can now start the dev server using the folowing command:
+```bash
+yarn start
+```
+
+This application uses React-Router with only one route, which renders a list of
+CWUser.  This is a simple example to show how it works and what we can do. You
+can now modify everything you need to adapt to your proper needs.
diff --git a/cookiecutter.json b/cookiecutter.json
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_Y29va2llY3V0dGVyLmpzb24=
--- /dev/null
+++ b/cookiecutter.json
@@ -0,0 +1,7 @@
+{
+  "project_slug": "cubicweb-react-ts-app",
+  "description": "CubicWeb React TypeScript app",
+  "author": "Logilab",
+  "license": "LGPL-3.0-or-later",
+  "cubicweb_server": "https://www.semweb.pro"
+}
diff --git a/{{ cookiecutter.project_slug }}/.env.example b/{{ cookiecutter.project_slug }}/.env.example
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS8uZW52LmV4YW1wbGU=
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/.env.example	
@@ -0,0 +1,1 @@
+PORT=3000
diff --git a/{{ cookiecutter.project_slug }}/.hgignore b/{{ cookiecutter.project_slug }}/.hgignore
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS8uaGdpZ25vcmU=
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/.hgignore	
@@ -0,0 +1,5 @@
+/node_modules
+/build
+.env
+.env.local
+.log
diff --git a/{{ cookiecutter.project_slug }}/README.md b/{{ cookiecutter.project_slug }}/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9SRUFETUUubWQ=
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/README.md	
@@ -0,0 +1,4 @@
+# {{ cookiecutter.project_slug }}
+License: {{ cookiecutter.license }}
+
+{{ cookiecutter.description }}
diff --git a/{{ cookiecutter.project_slug }}/environement.d.ts b/{{ cookiecutter.project_slug }}/environement.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9lbnZpcm9uZW1lbnQuZC50cw==
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/environement.d.ts	
@@ -0,0 +1,9 @@
+declare global {
+  namespace NodeJS {
+    interface ProcessEnv {
+      NODE_ENV: 'development' | 'production';
+    }
+  }
+}
+
+export {};
diff --git a/{{ cookiecutter.project_slug }}/index.html b/{{ cookiecutter.project_slug }}/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9pbmRleC5odG1s
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/index.html	
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <meta name="description" content="{{ cookiecutter.description }}" />
+    <title>{{ cookiecutter.project_slug }}</title>
+  </head>
+  <body>
+    <noscript>You need to enable javascript to run this app</noscript>
+    <div id="root"></div>
+  </body>
+</html>
diff --git a/{{ cookiecutter.project_slug }}/modules.d.ts b/{{ cookiecutter.project_slug }}/modules.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9tb2R1bGVzLmQudHM=
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/modules.d.ts	
@@ -0,0 +1,28 @@
+//------------------------------------------------------------------------------
+// Decleare all custom static modules in this file
+//------------------------------------------------------------------------------
+
+declare module "*.jpg" {
+  const src: string;
+  export default src;
+}
+
+//------------------------------------------------------------------------------
+
+declare module "*.png" {
+  const src: string;
+  export default src;
+}
+
+//------------------------------------------------------------------------------
+
+declare module "*.svg" {
+  import * as React from "react";
+
+  export const ReactComponent: React.FunctionComponent<
+    React.SVGProps<SVGSVGElement> & { title?: string }
+  >;
+
+  const src: string;
+  export default src;
+}
diff --git a/{{ cookiecutter.project_slug }}/package.json b/{{ cookiecutter.project_slug }}/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9wYWNrYWdlLmpzb24=
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/package.json	
@@ -0,0 +1,64 @@
+{
+  "homepage": ".",
+  "name": "{{ cookiecutter.project_slug }}",
+  "description": "{{ cookiecutter.description }} ",
+  "license": "{{ cookiecutter.license }}",
+  "author": "{{ cookiecutter.author }}",
+  "dependencies": {
+    "@cubicweb/client": "^1.5.0",
+    "dotenv": "^16.0.1",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0",
+    "react-icons": "^4.4.0",
+    "react-router-dom": "^6.3.0"
+  },
+  "devDependencies": {
+    "@types/node": "^18.7.6",
+    "@types/react": "^18.0.17",
+    "@types/react-dom": "^18.0.6",
+    "@types/react-router-dom": "^5.3.3",
+    "@typescript-eslint/eslint-plugin": "^5.33.1",
+    "@typescript-eslint/parser": "^5.33.1",
+    "copy-webpack-plugin": "^11.0.0",
+    "css-loader": "^6.7.1",
+    "eslint": "^8.22.0",
+    "eslint-plugin-import": "^2.26.0",
+    "eslint-plugin-jsx-a11y": "^6.6.1",
+    "eslint-plugin-react": "^7.30.1",
+    "eslint-plugin-react-hooks": "^4.6.0",
+    "eslint-webpack-plugin": "^3.2.0",
+    "file-loader": "^6.2.0",
+    "html-webpack-plugin": "^5.5.0",
+    "mini-css-extract-plugin": "^2.6.1",
+    "react-dev-utils": "^12.0.1",
+    "style-loader": "^3.3.1",
+    "terser-webpack-plugin": "^5.3.5",
+    "ts-loader": "^9.3.1",
+    "typescript": "^4.7.4",
+    "webpack": "^5.74.0",
+    "webpack-cli": "^4.10.0",
+    "webpack-dev-server": "^4.10.0",
+    "webpack-manifest-plugin": "^5.0.0"
+  },
+  "scripts": {
+    "start": "webpack serve --config webpack/webpack.config.js --mode development",
+    "build": "webpack --config webpack/webpack.config.js --mode production"
+  },
+  "eslintConfig": {
+    "extends": [
+      "react-app"
+    ]
+  },
+  "browserslist": {
+    "production": [
+      ">0.2%",
+      "not dead",
+      "not op_mini all"
+    ],
+    "development": [
+      "last 1 chrome version",
+      "last 1 firefox version",
+      "last 1 safari version"
+    ]
+  }
+}
diff --git a/{{ cookiecutter.project_slug }}/src/CubicWebClient.ts b/{{ cookiecutter.project_slug }}/src/CubicWebClient.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9zcmMvQ3ViaWNXZWJDbGllbnQudHM=
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/src/CubicWebClient.ts	
@@ -0,0 +1,12 @@
+import React from "react";
+import { Client } from "@cubicweb/client";
+
+const client = new Client("http://localhost:8080/api");
+
+export const CWClientContext = React.createContext<{
+  client: Client;
+  cwserver: string;
+}>({
+  client,
+  cwserver: "http://localhost:8080",
+});
diff --git a/{{ cookiecutter.project_slug }}/src/assets/public/favicon.ico b/{{ cookiecutter.project_slug }}/src/assets/public/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..650566545d0860dcd0d96bf93b7013c5ab4dc55f
GIT binary patch
literal 34494
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

diff --git a/{{ cookiecutter.project_slug }}/src/assets/public/img/logo192.png b/{{ cookiecutter.project_slug }}/src/assets/public/img/logo192.png
new file mode 100644
index 0000000000000000000000000000000000000000..5673c0fd83ec1004e9c1d67384a1111e9318e2f8
GIT binary patch
literal 8598
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
Bgkk^y

diff --git a/{{ cookiecutter.project_slug }}/src/assets/public/manifest.json b/{{ cookiecutter.project_slug }}/src/assets/public/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9zcmMvYXNzZXRzL3B1YmxpYy9tYW5pZmVzdC5qc29u
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/src/assets/public/manifest.json	
@@ -0,0 +1,15 @@
+{
+  "short_name": "@react/starter",
+  "name": "react-starter-kit",
+  "icons": [
+    {
+      "src": "img/logo192.png",
+      "type": "image/png",
+      "sizes": "192x192"
+    }
+  ],
+  "start_url": ".",
+  "display": "standalone",
+  "theme_color": "#000000",
+  "background_color": "#ffffff"
+}
diff --git a/{{ cookiecutter.project_slug }}/src/assets/public/robots.txt b/{{ cookiecutter.project_slug }}/src/assets/public/robots.txt
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9zcmMvYXNzZXRzL3B1YmxpYy9yb2JvdHMudHh0
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/src/assets/public/robots.txt	
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow:
diff --git a/{{ cookiecutter.project_slug }}/src/components/cwusers/CWUsers.css b/{{ cookiecutter.project_slug }}/src/components/cwusers/CWUsers.css
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9zcmMvY29tcG9uZW50cy9jd3VzZXJzL0NXVXNlcnMuY3Nz
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/src/components/cwusers/CWUsers.css	
@@ -0,0 +1,3 @@
+.cwusers ul {
+  list-style: none;
+}
diff --git a/{{ cookiecutter.project_slug }}/src/components/cwusers/CWUsers.tsx b/{{ cookiecutter.project_slug }}/src/components/cwusers/CWUsers.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9zcmMvY29tcG9uZW50cy9jd3VzZXJzL0NXVXNlcnMudHN4
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/src/components/cwusers/CWUsers.tsx	
@@ -0,0 +1,29 @@
+import React from "react";
+
+import { BsPersonCircle } from "react-icons/bs";
+
+import { CWClientContext } from "../../CubicWebClient";
+import { useCWUsers, CWUser } from "../../hooks/useCWUser";
+
+import "./CWUsers.css";
+
+export const CWUsers: React.FC = () => {
+  const { cwserver } = React.useContext(CWClientContext);
+  const cwusers: CWUser[] | null = useCWUsers();
+  if (cwusers === null) {
+    return <div>Loading ...</div>;
+  }
+  return (
+    <div className="cwusers">
+      <h1>CWUsers from {cwserver}</h1>
+      <ul>
+        {cwusers.map((cwuser) => (
+          <li>
+            <BsPersonCircle /> - {cwuser.firstname} {cwuser.surname} (
+            {cwuser.login})
+          </li>
+        ))}
+      </ul>
+    </div>
+  );
+};
diff --git a/{{ cookiecutter.project_slug }}/src/configs.ts b/{{ cookiecutter.project_slug }}/src/configs.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9zcmMvY29uZmlncy50cw==
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/src/configs.ts	
@@ -0,0 +1,4 @@
+// Envirement
+export const env = {
+  mode: process.env.NODE_ENV,
+};
diff --git a/{{ cookiecutter.project_slug }}/src/hooks/useCWUser.ts b/{{ cookiecutter.project_slug }}/src/hooks/useCWUser.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9zcmMvaG9va3MvdXNlQ1dVc2VyLnRz
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/src/hooks/useCWUser.ts	
@@ -0,0 +1,40 @@
+import * as React from "react";
+
+import { ResultSet } from "@cubicweb/client";
+
+import { CWClientContext } from "../CubicWebClient";
+
+export interface CWUser {
+  eid: number;
+  login: string;
+  firstname: string | null;
+  surname: string | null;
+}
+
+export function useCWUsers(): CWUser[] | null {
+  const { client } = React.useContext(CWClientContext);
+
+  const [cwusers, setCWUsers] = React.useState<CWUser[] | null>(null);
+
+  React.useEffect(() => {
+    client
+      .execute(
+        "Any U, L, F, S WHERE U is CWUser, U login L, U firstname F, U surname S"
+      )
+      .then((data: ResultSet) => {
+        setCWUsers(
+          data.map(
+            ([eid, login, firstname, surname]) =>
+              ({
+                eid,
+                login,
+                firstname,
+                surname,
+              } as CWUser)
+          )
+        );
+      });
+  }, [client]);
+
+  return cwusers;
+}
diff --git a/{{ cookiecutter.project_slug }}/src/index.tsx b/{{ cookiecutter.project_slug }}/src/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS9zcmMvaW5kZXgudHN4
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/src/index.tsx	
@@ -0,0 +1,25 @@
+import React from "react";
+import { render as renderApp } from "react-dom";
+
+import { BrowserRouter, Routes, Route } from "react-router-dom";
+import { Client } from "@cubicweb/client";
+
+import { CWClientContext } from "CubicWebClient";
+import { CWUsers } from "./components/cwusers/CWUsers";
+
+const cwserver = "{{ cookiecutter.cubicweb_server }}";
+const client = new Client(`${cwserver}/api`);
+const contextValue = { client, cwserver };
+
+renderApp(
+  <React.StrictMode>
+    <CWClientContext.Provider value={contextValue}>
+      <BrowserRouter>
+        <Routes>
+          <Route path="/" element={<CWUsers />} />
+        </Routes>
+      </BrowserRouter>
+    </CWClientContext.Provider>
+  </React.StrictMode>,
+  document.getElementById("root")
+);
diff --git a/{{ cookiecutter.project_slug }}/tsconfig.json b/{{ cookiecutter.project_slug }}/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS90c2NvbmZpZy5qc29u
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/tsconfig.json	
@@ -0,0 +1,27 @@
+{
+  "compilerOptions": {
+    "allowUnreachableCode": false,
+    "exactOptionalPropertyTypes": true,
+    "noFallthroughCasesInSwitch": true,
+    "noImplicitAny": true,
+    "noPropertyAccessFromIndexSignature": true,
+    "noUnusedLocals": true,
+    "strict": true,
+    "baseUrl": "src",
+    "module": "commonjs",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "outDir": "./build/",
+    "removeComments": true,
+    "allowJs": true,
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true,
+    "isolatedModules": true,
+    "jsx": "react-jsx",
+    "lib": ["dom", "dom.iterable", "esnext"],
+    "target": "es6",
+    "skipLibCheck": true
+  },
+  "include": ["**/*.ts", "**/*.d.ts", "**/*.tsx"],
+  "exclude": ["node_modules"]
+}
diff --git a/{{ cookiecutter.project_slug }}/webpack/paths.js b/{{ cookiecutter.project_slug }}/webpack/paths.js
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS93ZWJwYWNrL3BhdGhzLmpz
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/webpack/paths.js	
@@ -0,0 +1,14 @@
+const path = require("path");
+
+//------------------------------------------------------------------------------
+
+const rootDir = path.resolve(__dirname, "../");
+
+module.exports = {
+  modules: path.resolve(path.join(rootDir, "node_modules")),
+  public: path.resolve(path.join(rootDir, "src/assets/public")),
+  src: path.resolve(path.join(rootDir, "src")),
+  build: path.resolve(path.join(rootDir, "build")),
+  index: path.resolve(path.join(rootDir, "src/index.tsx")),
+  index_html: path.resolve(path.join(rootDir, "index.html")),
+};
diff --git a/{{ cookiecutter.project_slug }}/webpack/plugins/build-wiper.js b/{{ cookiecutter.project_slug }}/webpack/plugins/build-wiper.js
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS93ZWJwYWNrL3BsdWdpbnMvYnVpbGQtd2lwZXIuanM=
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/webpack/plugins/build-wiper.js	
@@ -0,0 +1,13 @@
+const fs = require('fs-extra');
+
+//------------------------------------------------------------------------------
+
+module.exports = class BuildWiper {
+  constructor (buildPath) {
+    this.buildPath = buildPath;
+  }
+
+  apply() {
+    fs.emptyDirSync(this.buildPath);
+  }
+}
diff --git a/{{ cookiecutter.project_slug }}/webpack/plugins/check-port.js b/{{ cookiecutter.project_slug }}/webpack/plugins/check-port.js
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS93ZWJwYWNrL3BsdWdpbnMvY2hlY2stcG9ydC5qcw==
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/webpack/plugins/check-port.js	
@@ -0,0 +1,50 @@
+const net = require('net');
+const chalk = require('react-dev-utils/chalk');
+
+//------------------------------------------------------------------------------
+
+/**
+ * Check if a port is already in use
+ * @param {number} port Target port to check
+ * @param {(inUse: boolean) => void} result A callback that provides the result
+ */
+function portInUse(port, result) {
+  const server = net.createServer();
+
+  // Handle port is used
+  server.once('error', (error) => {
+    if (error.code === 'EADDRINUSE') {
+      result(true);
+    };
+  });
+
+  // Handle port is free to use
+  server.once('listening', () => {
+    server.close();
+    result(false);
+  });
+
+  server.listen(port);
+};
+
+//------------------------------------------------------------------------------
+
+const pluginName = 'check-port-plugin';
+
+module.exports = class CheckPortPlugin {
+  constructor(port) {
+    this.port = port;
+  }
+
+  apply(compiler) {
+    // Check port before compilation happens
+    compiler.hooks.initialize.tap(pluginName, () => {
+      portInUse(this.port, (isInUse) => {
+        if (isInUse) {
+          console.log(chalk.yellow(`Port ${this.port} is already in use`));
+          process.exit(1);
+        }
+      });
+    });
+  }
+}
diff --git a/{{ cookiecutter.project_slug }}/webpack/webpack.config.js b/{{ cookiecutter.project_slug }}/webpack/webpack.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..fe5b8f04807cd5e2c1980e28ac948aa8a1e670d7_e3sgY29va2llY3V0dGVyLnByb2plY3Rfc2x1ZyB9fS93ZWJwYWNrL3dlYnBhY2suY29uZmlnLmpz
--- /dev/null
+++ b/{{ cookiecutter.project_slug }}/webpack/webpack.config.js	
@@ -0,0 +1,253 @@
+//------------------------------------------------------------------------------
+// This app is configured to use typescript.
+// See https://webpack.js.org/concepts/ on how to configure this file.
+//------------------------------------------------------------------------------
+
+"use-strict";
+
+//------------------------------------------------------------------------------
+
+const path = require("path");
+const webpack = require("webpack");
+
+// Configure envirement (NOTE: This must be done as early as pissible)
+require("dotenv").config();
+
+//------------------------------------------------------------------------------
+
+const TerserPlugin = require("terser-webpack-plugin");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
+const CopyWebpackPlugin = require("copy-webpack-plugin");
+const ESLintPlugin = require("eslint-webpack-plugin");
+
+const CheckPortPlugin = require("./plugins/check-port");
+const BuildFolderWiper = require("./plugins/build-wiper");
+
+const paths = require("./paths");
+
+//------------------------------------------------------------------------------
+
+const checkRequiredFiles = require("react-dev-utils/checkRequiredFiles");
+const eslintFormatter = require("react-dev-utils/eslintFormatter");
+
+//------------------------------------------------------------------------------
+
+const requiredFiles = [paths.index_html, paths.index];
+
+if (!checkRequiredFiles(requiredFiles)) {
+  process.exit(1);
+}
+
+//------------------------------------------------------------------------------
+
+const mainTsRejex = /\.(ts|tsx)$/;
+const mainCssRejex = /\.(css)$/;
+const assetsRejex = /\.(jpg|jpeg|png|gif|mp3|mp4|svg)$/;
+
+//------------------------------------------------------------------------------
+
+module.exports = function (_, webpackEnv) {
+  const isDevelopment = webpackEnv.mode === "development";
+  const isProduction = webpackEnv.mode === "production";
+  const isProfile = process.argv.includes("--profile");
+  const isProductionProfile = isProduction && isProfile;
+  const port = process.env.PORT || 8080;
+
+  const getStyleLoaders = () => [
+    isDevelopment ? "style-loader" : MiniCssExtractPlugin.loader,
+    {
+      loader: "css-loader",
+      options: {
+        sourceMap: isDevelopment,
+      },
+    },
+  ];
+
+  return {
+    target: "web",
+    entry: paths.index,
+    output: {
+      path: paths.build,
+      filename: isDevelopment
+        ? "static/js/[name].js"
+        : isProduction && "static/js/[name].[contenthash:8].js",
+      chunkFilename: isDevelopment
+        ? "static/js/[name].chunk.js"
+        : isProduction && "static/js/[name].[contenthash:8].chunk.js",
+      publicPath: "/",
+      assetModuleFilename: "assets/[hash][ext]",
+    },
+    devtool: isDevelopment ? "source-map" : false,
+    devServer: {
+      client: {
+        logging: "none",
+        overlay: {
+          // Show only errors on the browser
+          errors: true,
+          warnings: false,
+        },
+      },
+      headers: [
+        // Security headers
+        {
+          key: "Strict-Transport-Security",
+          value: "max-age=63072000; preload",
+        },
+        {
+          key: "X-XSS-Protection",
+          value: "1; mode=block",
+        },
+        {
+          key: "X-Frame-Options",
+          value: "deny",
+        },
+        {
+          key: "X-Content-Type-Options",
+          value: "nosniff",
+        },
+        {
+          key: "Referrer-Policy",
+          value: "origin-when-cross-origin",
+        },
+      ],
+      historyApiFallback: true, // Nessessary for react router to work
+      host: "0.0.0.0", // Allow local network access to the server
+      hot: true,
+      port,
+    },
+    module: {
+      rules: [
+        {
+          test: mainTsRejex,
+          exclude: /node_modules/,
+          use: ["ts-loader"],
+        },
+        {
+          test: mainCssRejex,
+          exclude: /node_modules/,
+          use: getStyleLoaders(),
+        },
+        {
+          test: assetsRejex,
+          exclude: [
+            /node_modules/,
+            /\.(js|mjs|jsx|ts|tsx)$/,
+            /\.html$/,
+            /\.json$/,
+          ],
+          use: [
+            {
+              loader: "file-loader",
+              options: {
+                outputPath: "static/media",
+                name: "[name].[contenthash:8].[ext]",
+              },
+            },
+          ],
+        },
+        {
+          test: /\.(woff|woff2|eot|ttf|otf)$/i,
+          type: "asset/resource",
+        },
+      ],
+    },
+    resolve: {
+      extensions: ["*", ".js", ".ts", ".tsx"],
+      modules: [paths.src, paths.modules],
+      fallback: {
+        contentBase: paths.build,
+        events: false,
+        url: false,
+      },
+    },
+    optimization: {
+      minimize: isProduction,
+      minimizer: [
+        new TerserPlugin({
+          terserOptions: {
+            parse: {
+              ecma: 8,
+            },
+            compress: {
+              ecma: 5,
+              warnings: false,
+              comparisons: false,
+              inline: 2,
+            },
+            mangle: {
+              safari10: true,
+            },
+            keep_classnames: isProductionProfile,
+            keep_fnames: isProductionProfile,
+            output: {
+              ecma: 5,
+              comments: false,
+              ascii_only: true,
+            },
+            sourceMap: false,
+          },
+        }),
+      ],
+    },
+    plugins: [
+      new ESLintPlugin({ formatter: eslintFormatter }),
+      new HtmlWebpackPlugin({
+        template: paths.index_html,
+        minify: {
+          removeComments: true,
+          collapseWhitespace: true,
+          removeRedundantAttributes: true,
+          useShortDoctype: true,
+          removeEmptyAttributes: true,
+          removeStyleLinkTypeAttributes: true,
+          keepClosingSlash: true,
+          minifyJS: true,
+          minifyCSS: true,
+          minifyURLs: true,
+        },
+      }),
+      isProduction &&
+        new MiniCssExtractPlugin({
+          filename: "static/css/[name].[contenthash:8].css",
+          chunkFilename: "static/css/[name].[contenthash:8].chunk.css",
+        }),
+      new WebpackManifestPlugin({
+        fileName: "asset-manifest.json",
+        publicPath: "/",
+        generate: (seed, files, entrypoints) => {
+          const manifestFiles = files.reduce((manifest, file) => {
+            manifest[file.name] = file.path;
+            return manifest;
+          }, seed);
+
+          const entrypointFiles = entrypoints.main.filter((fileName) => {
+            return !fileName.endsWith(".map");
+          });
+
+          return {
+            files: manifestFiles,
+            entrypoints: entrypointFiles,
+          };
+        },
+      }),
+      new CopyWebpackPlugin({
+        patterns: [
+          {
+            from: paths.public,
+            to: ".",
+          },
+        ],
+      }),
+      isDevelopment && new CheckPortPlugin(port),
+      isProduction && new BuildFolderWiper(paths.build),
+      new webpack.DefinePlugin({
+        "process.env.NODE_ENV": JSON.stringify(webpackEnv.mode),
+      }),
+      new webpack.EnvironmentPlugin([
+        // Pass all public env variables here
+      ]),
+    ].filter(Boolean),
+  };
+};