Search code examples
javascriptdate-fns

Date-fns issue failed to resolve module specifier


I'm trying to practice using date-fns library (not using React just simple HTML page) But when i'm trying to load it it's saying

TypeError: Failed to resolve module specifier "date-fns". Relative references must start with either "/", "./", or "../".

html.index:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Transaction Dates</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      line-height: 1.6;
      padding: 20px;
    }
    .transaction {
      border: 1px solid #ccc;
      padding: 10px;
      margin-bottom: 10px;
    }
    .transaction span {
      font-weight: bold;
    }
  </style>
</head>
<body>
  <h1>Transaction Dates</h1>
  <div id="transactions"></div>

  <!-- Load date-fns library -->
  <script src="https://cdn.jsdelivr.net/npm/date-fns@latest"></script>

  <!-- Load our JavaScript file -->
  <script src="script.js" type="module"></script>
</body>
</html>

script.js:

import { differenceInDays, differenceInHours } from 'date-fns';

const transactions = [
  { amount: 200, date: new Date('2024-07-16T21:31:17.178Z') },
  { amount: 455.23, date: new Date('2024-07-16T07:42:02.383Z') },
  { amount: -306.5, date: new Date('2024-07-15T09:15:04.904Z') },
  { amount: 25000, date: new Date('2024-07-14T10:17:24.185Z') },
  { amount: -642.21, date: new Date('2024-07-13T14:11:59.604Z') },
  { amount: -133.9, date: new Date('2024-07-12T17:01:17.194Z') },
  { amount: 79.97, date: new Date('2024-07-11T23:36:17.929Z') },
  { amount: 1300, date: new Date('2024-07-10T23:51:36.790Z') }
];

const container = document.getElementById('transactions');

function formatTransactionDate(date) {
  const now = new Date();
  const daysPassed = differenceInDays(now, date);
  const hoursPassed = differenceInHours(now, date);

  // Check if it's within the last 24 hours
  if (daysPassed === 0 && hoursPassed < 24) {
    return "Today";
  } else if (daysPassed === 1) {
    return "Yesterday";
  } else {
    return `${daysPassed} days ago`;
  }
}

function displayTransactions() {
  transactions.forEach(transaction => {
    const formattedDate = formatTransactionDate(transaction.date);
    const transactionElement = document.createElement('div');
    transactionElement.classList.add('transaction');
    transactionElement.innerHTML = `
      <span>Amount:</span> ${transaction.amount.toFixed(2)}<br>
      <span>Date:</span> ${formattedDate}<br>
    `;
    container.appendChild(transactionElement);
  });
}

// Display transactions on page load
displayTransactions();

package.json :

{
  "dependencies": {
    "date-fns": "^3.6.0"
  }
}

package-lock.json :

{
  "name": "test dates",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "dependencies": {
        "date-fns": "^3.6.0"
      }
    },
    "node_modules/date-fns": {
      "version": "3.6.0",
      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
      "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
      "funding": {
        "type": "github",
        "url": "https://github.com/sponsors/kossnocorp"
      }
    }
  }
}

What's the issue with that?

nothing helped, seeking for a help


Solution

  • It seems you confused a few things when working with various module formats.

    In your HTML page, your reference date-fns via jsdelivr.com. When following the URL to the script, it returns a so called UMD script, short for Universal Module Definition which is not the same as a ES2015 module. The UMD module attaches a dateFns namespace to the window object. Therefor, change the line

    import { differenceInDays, differenceInHours } from 'date-fns';
    

    to

    const { differenceInDays, differenceInHours } = window.dateFns;
    

    and it should work.

    Please note that I used https://cdn.jsdelivr.net/npm/[email protected]/cdn.min.js instead of https://cdn.jsdelivr.net/npm/date-fns@latest because SO does not accept the resource otherwise. However, both URLs point to the same file.

    const { differenceInDays, differenceInHours } = window.dateFns;
    
    const transactions = [
      { amount: 200, date: new Date('2024-07-16T21:31:17.178Z') },
      { amount: 455.23, date: new Date('2024-07-16T07:42:02.383Z') },
      { amount: -306.5, date: new Date('2024-07-15T09:15:04.904Z') },
      { amount: 25000, date: new Date('2024-07-14T10:17:24.185Z') },
      { amount: -642.21, date: new Date('2024-07-13T14:11:59.604Z') },
      { amount: -133.9, date: new Date('2024-07-12T17:01:17.194Z') },
      { amount: 79.97, date: new Date('2024-07-11T23:36:17.929Z') },
      { amount: 1300, date: new Date('2024-07-10T23:51:36.790Z') }
    ];
    
    const container = document.getElementById('transactions');
    
    function formatTransactionDate(date) {
      const now = new Date();
      const daysPassed = differenceInDays(now, date);
      const hoursPassed = differenceInHours(now, date);
    
      // Check if it's within the last 24 hours
      if (daysPassed === 0 && hoursPassed < 24) {
        return "Today";
      } else if (daysPassed === 1) {
        return "Yesterday";
      } else {
        return `${daysPassed} days ago`;
      }
    }
    
    function displayTransactions() {
      transactions.forEach(transaction => {
        const formattedDate = formatTransactionDate(transaction.date);
        const transactionElement = document.createElement('div');
        transactionElement.classList.add('transaction');
        transactionElement.innerHTML = `
          <span>Amount:</span> ${transaction.amount.toFixed(2)}<br>
          <span>Date:</span> ${formattedDate}<br>
        `;
        container.appendChild(transactionElement);
      });
    }
    
    // Display transactions on page load
    displayTransactions();
    body {
      font-family: Arial, sans-serif;
      line-height: 1.6;
      padding: 20px;
    }
    .transaction {
      border: 1px solid #ccc;
      padding: 10px;
      margin-bottom: 10px;
    }
    .transaction span {
      font-weight: bold;
    }
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/cdn.min.js"></script>
    
    
      <h1>Transaction Dates</h1>
      <div id="transactions"></div>
    
     

    TL;DR explanation

    ES2015 modules work differently in the browser than they do in Node. The rules for the browser are more strict, meaning you must provide a real URL to the resource. This is the reason for the

    Failed to resolve module specifier "date-fns". Relative references must start with either "/", "./", or "../"

    error you receive. The browser simply does not know where to look for the resource, because it does not "magically" know what date-fns refers to.

    In Node on the other hand, you can get away with using the NPM package key "date-fns", because node's resolution algorithm works the way it works: It looks into the local node_modules folder and searches for a package that has the name date-fns written into it's package.json file and uses the package if it finds it.

    If you feel that you want/need to read about the way Node works, have a look at the docs. Contrast that with MDN's documentation about JS modules usage in the browser.