skip to Main Content

When d3.js moved to version 4, there was a significant rewrite of the Api, and also a change to modular packaging.

I don’t want to upgrade to version 4 of d3.js but I have to rewrite the react/redux web app portion of my project using Vite, and version 3 of d3.js and it’s use of global is not compatible with the JavaScript modular system used by Vite.

The error I’m getting is that "global" is not defined, and version 3 of d3.js uses "global."

This is what it says it node_modules/d3/index.js (index.js:4:13)

var globals = {};

// Stash old global.
if ("d3" in global) globals.d3 = global.d3;

module.exports = require("./d3");

// Restore old global.
if ("d3" in globals) global.d3 = globals.d3; else delete global.d3;

Therefore, I commented out that reference to global in the d3/index.js file to see what would happen, and I got a similar property, "cannot read properties of undefined (document)" This is the source code

var d3_document = this.document;
function d3_documentElement(node) {
  return node && (node.ownerDocument || node.document || node).documentElement;
}

I’m guessing this has something to do with the shift to modular d3 in version 4, but d3.js version 3.5.5 works fine when I don’t use it with Vite.js. For example, if I just source it in an index.html file and run it with an http-server.

Question: is there a way to configure Vite.js to make it work with d3.js version 3.5.5? or can I alter the d3.js version 3.5.5 source code to make it run on Vite.js?

Update

I added this to the Vite.config.js

  define: {
    global: {},
    document: {}
  }

Now Vite complains that the property document cannot be set on #<Window>.

3env.ts:24 Uncaught TypeError: Cannot set property document of #<Window> which has only a getter

This StackOverflow question helps but it doesn’t explain when "document" is undefined and it can’t be "set" on Window.

Probably irrelevant but here’s the source code for d3 version 3.5.5 on unpkg.com
d3 version 3.5.5`

Here’s the source code for d3 version 4 on unpkg.com
d3 version 4

2

Answers


  1. In order to make D3.js version 3.5.5 compatible with Vite, you need to address the "global is not defined" issue and manage the document object properly within the Vite environment. Here’s a step-by-step solution:

    1. Polyfill Global Object: In Vite, global is not defined by default. Create a polyfill to make global refer to the window object.
    // init.js
    window.global ||= window;
    

    Import this script at the beginning of your main file.

    1. Modify D3.js Source for Document Object: Since you can’t set document on the Window object in Vite, modify the D3.js source code to bind this to window. Wrap the D3.js code in an IIFE:
    (function() {
        // D3.js code
    }).call(window);
    

    For the vite config Be cautious with using the define option in vite.config.js for variables. According to vite’s official documentation, it’s recommended for constants only. Variables like global should be shimmed or polyfilled.

    Login or Signup to reply.
  2. The main issue is that node_modules/d3/d3.js file uses the this keyword.
    Specifically it reads this.document, instead of window.document or simply directly accessing the global document variable.
    So the actual question should be: Is it possible to define this for d3 in Vite.

    You saw that d3 worked fine when you tested with your http-server where you load the script from a index.html file.

    It worked because you loaded it using <script type="text/javascript">.
    This is because in old script files the default value for top-level this is window.

    If you tried loading it using <script type="module">, like Vite does, it wouldn’t have worked, since for module scripts the top-level this variable is undefined.

    Since Vite processes the d3 module it technically could assign a custom value for this, but I checked and I could not find anything in docs about that, so it is likely not possible to solve the problem using Vite config. I’m guessing they didn’t want to go against the official default.

    And unfortunately this is not something you can polyfill.


    This leaves us only with the solution I mentioned yesterday in the comments, that is editing the d3 source. At least the change is very minor.

    First copy node_modules/d3/d3.js file to your source folder.
    (you should never edit files in node_modules since npm or yarn might overwrite those changes; and pnpm even uses symbolic links, so it shared the same file for all projects)

    Then you’d have to replace the first and last lines from:

    !function() {
      // ...
    }()
    

    to:

    (function() {
      // ...
    }).call(window)
    

    And of course you have to replace your import statements to import this new file instead of importing d3 from node_modules.


    As for node_modules/d3/index.js you can actually skip that file instead of adding polyfills for it.
    In later patches like 3.5.17 it was even removed.
    All it does is handle the global d3 variable.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search