magjac/d3-graphviz

d3.graphviz not a function

AntoineTB opened this issue · 12 comments

Hey,
New to your package, super excited to render animated SVG like your demo.
Having some kind of a trouble setting it up in my react+typescript app though, not sure if related.
Here's what I'm doing at the moment:

Inside index.html:

<head>
    ...
    <script src="./node_modules/viz.js/viz.js" type='javascript/worker'></script>
    <script src="./node_modules/d3/build/d3.js"></script>
    <script src="./node_modules/d3-graphviz/build/d3-graphviz.js"></script>

then inside my component :

export class DotViewer extends React.Component<DotViewerProps,DotViewerState>{
  // ...
 render(){
    return <div className='DotViewer'>
      <div className={'dot_view_port_'+this.state.uniqueid}></div>
    </div>
  }
  renderGraph(){
     d3.graphviz(".dot_view_port_"+this.state.uniqueid)
     .renderDot(
      this.props.dotString
    );
  }
  componentDidMount(){this.renderGraph()}

Which throws the following error :
DotViewer.tsx:31 Uncaught TypeError: d3.graphviz is not a function at DotViewer.renderGraph
I've tried using the other syntax I saw around too : d3.select(...).graphviz().renderDot( but I get the same issue : d3.select(...).graphviz() is not a function

I'm assuming it means I'm not properly setting up d3-graphviz as a plugin of d3, but it seems to never be done explicitly in the examples I saw so I'm a bit confused here. Also, I get no other error prior to this one, nothing at all If I do not call renderGraph.

Here's are some probably/maybe relevant package versions:

    "d3": "^4.13.0",
    "d3-graphviz": "^2.5.1",
    "d3-array": "^1.2.1",
    "d3-axis": "^1.0.8",
    "d3-brush": "^1.0.4",
    "d3-geo": "^1.9.1",
    "d3-scale": "^2.0.0",
    "d3-selection": "^1.3.0",
    "d3-shape": "^1.2.0",
    "d3-svg-legend": "^2.25.6",
    "d3-tip": "^0.7.1",
    "d3-transition": "^1.1.1",
    "viz.js": "^2.0.0"

Any idea?
Thanks in advance,
Antoine

I just saw you were using d3 ^5.0.0. I upgraded my version and am now using 5.7.0. This didn't resolve the issue.

I guess you solved it since you closed the issue. If not, let me know.

Would you mind sharing the solution?

Thanks for submitting this issue. If you are using this library, don't forget to Watch it to stay tuned for updates or even Star it if you like it.

You can also use Stack Overflow tags d3.js & graphviz or the d3-graphviz Slack to ask for help. Although I make an effort to assist everyone that asks, I am not always available to provide help promptly or directly.

Hey, thanks for the quick reply!
I was making good headway so I closed it to make sure you wouldn't work on it also and lose your time.
I just managed to make it work and learned a few things along the way. Hopefully this can be useful to someone else.

My approach was first to remove all the loading in the index.html except for viz.js as I wanted the worker, and let webpack&friends manage imports, as I would normally do with other dependancies. This led to the following discoveries/understandings:

First, npm wasn't very bright on this as I had conflicting requirements between d3-graphviz dependancies and explicit dependancies and it didn't make it obvious to me. For example, viz.js was at 2.0.0 in my package.json, which didn't work with d3-graphviz (requesting ^1.8.2) and led to an obscure error : Uncaught Cannot call a class as a function (worker.onmessage @ d3-graphviz.js:1178) .

To debug this I created a bare project and made the most simple package.json until I could get it to work, then compared the package-lock.json with my main project. By begin very careful about this I found the viz.js discrepancies but also that my main d3 was still on major version 4, plus some minor versions for various d3 packages. With some manual work and by running a few npm updates I eventually got close enough that I could render using this syntax d3Graphviz.graphviz(".graph").renderDot() but it would still fail with the same error as above when using d3 directly with this syntax : d3.select(".graph").graphviz().renderDot(). So that was the first headway.

Then (and please excuse my limited vocabulary as I'm not familar with the jargon here), there's something about webpack not eagerly loading packages that are imported in the file. I spent a good chunk of time wondering why it would work on my minimal debug project, but not on my main project, which used the "same" code. For reference, code would look more or less like this:

import * as React          from 'react'
import * as d3             from 'd3'
import * as d3Graphviz     from 'd3-graphviz'
class DotViewer extends React.component<any,any>{
  //...
  render(){
    const className = 'dot_view_port_'+this.state.uniqueid
    return <div className='DotViewer'>
      <div key={className+this.props.dotString} className={className}></div>
    </div>
  }
  renderGraph(){
    console.log("rendering dot string")
    //console.log(d3Graphviz.graphviz) // object, contains a function named graphviz
    d3.select(".dot_slide_view_port_"+this.state.uniqueid).graphviz().renderDot(this.props.dotString)
    console.log("done rendering dot string")
  }
  componentDidMount(){this.renderGraph()}
  //...

Which wouldn't work, it turns out, because d3Graphviz was simply never bundled in the application, as it was never referenced. In my working minimal project the console.log(d3Graphviz.graphviz) was not commented out, which meant it got bundled. To force d3-graphviz to be bundled I added the following line in my index.tsx :

const _ = d3Graphviz.graphviz //Preload d3Graphiz so it register itself in d3 as a plugin

Which did the trick.

The minimal project can be found here : https://github.com/AntoineTB/debugD3GraphViz, with a similar summary of problems encountered and solutions found.

Thanks again for the quick reply, i'll probably be back on SO with some actual API questions next time!

Thanks for the extensive write-up. Bundlers are black magic to me. You might want to compare to how I've done in https://github.com/magjac/graphviz-visual-editor/blob/master/src/Graph.js. Don't ask me to explain it though. It was mostly trial and error until it worked.

Hey!

I'm trying to solve an issue with import too. I'm using create-react-app, and on current stage it falls because of memory leaking after doing this:

import * as d3Graphviz from 'd3-graphviz'
const _ = d3Graphviz.graphviz

or this:

import 'd3-graphviz'

I got:

==== JS stack trace =========================================

    0: ExitFrame [pc: 0x7a3274dbe1d]
Security context: 0x04299e01e6e1 <JSObject>
    1: popContext [0x1ee096cf6571] [/home/netup/src/multimedia/js/multigraph/fro
ntend/node_modules/@babel/traverse/lib/path/context.js:~195] [pc=0x7a327dba9bd](
this=0x306c619a8779 <NodePath map = 0x139393e28379>)
    2: visitQueue [0x1ee096cf5149] [/home/netup/src/multimedia/js/multigraph/fro
ntend/node_modules/@babel/traverse/lib/context.js:~96] [pc=0x7a327db...

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaS
cript heap out of memory

I'm fighting.

@magjac, interesting thing is that completely the same issue happened when I cloned your project graphviz-visual-editor and tried to run it with npm start. Any ideas what is going on?

==== JS stack trace =========================================

    0: ExitFrame [pc: 0x7a3274dbe1d]
Security context: 0x04299e01e6e1 <JSObject>
    1: popContext [0x1ee096cf6571] [/home/netup/src/multimedia/js/multigraph/fro
ntend/node_modules/@babel/traverse/lib/path/context.js:~195] [pc=0x7a327dba9bd](
this=0x306c619a8779 <NodePath map = 0x139393e28379>)
    2: visitQueue [0x1ee096cf5149] [/home/netup/src/multimedia/js/multigraph/fro
ntend/node_modules/@babel/traverse/lib/context.js:~96] [pc=0x7a327db...

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaS
cript heap out of memory

Looks like the issue is related to babel. It can be solved by increasing heap memory limit:

NODE_OPTIONS=--max_old_space_size=4096 npm run build

@madconst, that works for me. Thank you

Great! Thanks @madconst, that solved magjac/graphviz-visual-editor#95 as well.

@magjac @AntoineTB , I am facing the same issue while trying to call d3.graphviz() . Tried
adding const _ = d3Graphviz.graphviz. It didn't solve the problem. Still the error says d3.graphviz() is not a function.

@magjac @AntoineTB , I am facing the same issue while trying to call d3.graphviz() . Tried adding const _ = d3Graphviz.graphviz. It didn't solve the problem. Still the error says d3.graphviz() is not a function.

Same here, I'm getting d3Graphviz is not defined w/ the const _ = d3Graphviz.graphviz method. Does that mean we also need to load a lower version of viz.js?

In case it helps someone, in Angular I was able to do this:

import {graphviz} from 'd3-graphviz';
...
graphviz('#graph').renderDot('digraph { a -> b }')

Where 'graph' is the id of div element in the html template.