reactjsdockernext.jspodman

Module not found: Can't resolve 'js-cookie'


I have a podman container with NextJs React app and I recently update a page.js adding 'js-cookie' module. I update it with these steps:

  1. Stop the app container
  2. Remove the app container
  3. Launch a system prune
  4. Install the module via 'npm install js-cookie'
  5. Clean '.nextjs', '/node_modules', 'package-lock.json'
  6. Build the app via 'podman-compose build app'
  7. Run the pod via 'podman compose up -d'

But I am still getting the following error:

Build Error

Module not found: Can't resolve 'js-cookie'
./src/app/crea-personaggio/page.js (7:1)

Module not found: Can't resolve 'js-cookie'
   5 | import { useRouter } from 'next/navigation';
   6 | import MenuBar from '@/components/MenuBar';
>  7 | import Cookies from 'js-cookie';
     | ^
   8 |
   9 | export default function CreateCharacter() {
  10 |   const [formData, setFormData] = useState({

https://nextjs.org/docs/messages/module-not-found

This is my page.js:

'use client'

import { useState, useEffect } from 'react';
import jwt from 'jsonwebtoken';
import { useRouter } from 'next/navigation';
import MenuBar from '@/components/MenuBar';
import Cookies from 'js-cookie';

export default function CreateCharacter() {
  const [formData, setFormData] = useState({
    nome: '',
    razza: '',
    classe: '',
    forza: '',
    destrezza: '',
    intelligenza: '',
    vitalita: '',
  });
  const [loading, setLoading] = useState(false);
  const [message, setMessage] = useState('');
  const [user, setUser] = useState(null);
  const router = useRouter();

  useEffect(() => {
    const fetchToken = async () => {
      try {
        const token = Cookies.get('token'); // Use js-cookie to get the token

        if (token) {
          try {
            const decodedUser = jwt.verify(token, process.env.JWT_SECRET);
            setUser(decodedUser);
          } catch (error) {
            console.error(error);
            // router.push('/login');
          }
        } else {
          console.error("Token non trovato");
          // router.push('/login');
        }
      } catch (error) {
        console.error("Errore nel recupero del cookie:", error);
      }
    };

    fetchToken();
  }, [router]);

  const handleChange = (e) => {
    setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    setMessage('');

    try {
      const res = await fetch('/api/character/create', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(formData),
      });
      const data = await res.json();
      if (res.ok) {
        setMessage('Scheda creata con successo!');
      } else {
        setMessage(data.error || 'Errore durante la creazione della scheda');
      }
    } catch (error) {
      console.error(error);
      setMessage('Errore di rete');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="container mt-5">
      <MenuBar user={user} />
      <div className="container mt-5">
        <h1>Crea la tua Scheda Personaggio - The Witcher</h1>
        <p>Compila i campi seguenti per creare la tua scheda personaggio.</p>
        <form onSubmit={handleSubmit}>
          <div className="mb-3">
            <label htmlFor="nome" className="form-label">Nome Personaggio</label>
            <input
              type="text"
              id="nome"
              name="nome"
              className="form-control"
              value={formData.nome}
              onChange={handleChange}
              required
            />
          </div>
          <div className="mb-3">
            <label htmlFor="razza" className="form-label">Razza</label>
            <input
              type="text"
              id="razza"
              name="razza"
              className="form-control"
              value={formData.razza}
              onChange={handleChange}
              required
            />
          </div>
          <div className="mb-3">
            <label htmlFor="classe" className="form-label">Classe</label>
            <input
              type="text"
              id="classe"
              name="classe"
              className="form-control"
              value={formData.classe}
              onChange={handleChange}
              required
            />
          </div>
          <div className="row">
            <div className="col">
              <label htmlFor="forza" className="form-label">Forza</label>
              <input
                type="number"
                id="forza"
                name="forza"
                className="form-control"
                value={formData.forza}
                onChange={handleChange}
                required
              />
            </div>
            <div className="col">
              <label htmlFor="destrezza" className="form-label">Destrezza</label>
              <input
                type="number"
                id="destrezza"
                name="destrezza"
                className="form-control"
                value={formData.destrezza}
                onChange={handleChange}
                required
              />
            </div>
            <div className="col">
              <label htmlFor="intelligenza" className="form-label">Intelligenza</label>
              <input
                type="number"
                id="intelligenza"
                name="intelligenza"
                className="form-control"
                value={formData.intelligenza}
                onChange={handleChange}
                required
              />
            </div>
            <div className="col">
              <label htmlFor="vitalita" className="form-label">Vitalità</label>
              <input
                type="number"
                id="vitalita"
                name="vitalita"
                className="form-control"
                value={formData.vitalita}
                onChange={handleChange}
                required
              />
            </div>
          </div>
          <div className="mt-3">
            <button type="submit" className="btn btn-primary" disabled={loading}>
              {loading ? 'Creazione in corso...' : 'Crea Scheda'}
            </button>
          </div>
        </form>
        {message && <div className="alert alert-info mt-3">{message}</div>}
      </div>
    </div>
  );
}

This is my package.json:

{
  "name": "the-witcher-fight-app",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "webpack serve --mode development",
    "build": "webpack --mode production"
  },
  "dependencies": {
    "bcryptjs": "^3.0.2",
    "bootstrap": "^5.3.3",
    "js-cookie": "^3.0.5",
    "jsonwebtoken": "^9.0.2",
    "next": "^15.2.4",
    "pg": "^8.14.1",
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  },
  "devDependencies": {
    "@babel/core": "^7.26.10",
    "@babel/preset-env": "^7.26.9",
    "@babel/preset-react": "^7.26.3",
    "babel-loader": "^10.0.0",
    "css-loader": "^7.1.2",
    "style-loader": "^4.0.0",
    "webpack": "^5.38.1",
    "webpack-cli": "^6.0.1",
    "webpack-dev-server": "^5.2.1"
  }
}

This is my Dockerfile:

# Usa un'immagine base di Node.js
FROM node:20-alpine

# Imposta la directory di lavoro
WORKDIR /app

# Copia il package.json e il package-lock.json
COPY package.json ./ 
COPY package-lock.json ./ 

# Installa le dipendenze
RUN npm install -g npm@11.2.0 && npm install

# Installa nodemon come dipendenza globale
RUN npm install -g nodemon

# Copia il resto dei file dell'applicazione
COPY . .

# Imposta i permessi corretti per i file
RUN chmod -R 755 /app

# Espone la porta su cui l'applicazione sarà in ascolto
EXPOSE 3000

# Comando per avviare l'applicazione in modalità sviluppo con nodemon
CMD ["nodemon", "--watch", ".", "--exec", "next", "dev"]

Thanks in advance for any suggestions on how to fix this issue.


Solution

  • I guess the error occurs because the Docker Compose configuration mounts anonymous volumes (/app/node_modules and /app/.next), which override the container's internal directories.