How to do cytoscape initialization with Typescript?
Asked Answered
D

2

6

I built a React component with Typescript that uses cytoscape (and its types) as a headless model. My objective is to create an NPM package so I can import it in other projects directly.

My library:

  1. Works when I import cytoscape.js in my index.html as a static asset
  2. Does not when I npm install my component in a new es6 app: Uncaught ReferenceError: cytoscape is not defined at new WbsLayout. This, despite cytoscape being automatically installed in node_modules when installing my component.

Typescript source

WbsLayout.tsx:

export class WbsLayout  {
    //...
    public cy: Cy.Core;
    constructor(graph: any, Options?: Options) {           
        this.cy = cytoscape({ // <-- Works or fails, see cases #1 and #2 explained above
            headless: true,
            elements: graph
        });
        //...
    }
    //...
}

Note that I don't import cytoscape from 'cytoscape' (or any similar ways) anywhere in my component source, as I didn't have any as it seems that with Typescript and the way cytoscape's typings are defined, I didn't need it to make my component work with cytoscape imported in html. Maybe it's an issue for an NPN package though...

Wbs.tsx:

A simple React component that receives the prop WbsLayout.

cytoscape's typings (only relevant part)

declare namespace Cy {
    //...
    interface Core { }

    //...
    interface Static {
        (options?: Cy.CytoscapeOptions): Core;
        (extensionName: string, foo: string, bar: any): Core;
        version: string;
    }
}

declare module "cytoscape" {
    export = cytoscape;
}
declare var cytoscape: Cy.Static;

Webpack 2 config

Wbs.tsx, WbsLayout.tsx and others are bundled into @nodeject/wbs-react using Webpack 2:

module.exports = {
  entry: {
    'test/index': './src/test.tsx',
    'dist/index': './src/index.tsx'
  },

  output: {
    filename: "./[name].js",
    libraryTarget: 'umd',
    library: 'wbs-react',
    umdNamedDefine: true
  },

  devtool: "source-map",

  resolve: {
    extensions: [".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
  },

  node: {
    fs: "empty",
    child_process: "empty"
  },

  module: {
    rules: [    
      {
        enforce: 'pre',
        test: /\.js$/,
        loader: "source-map-loader"
      },
           
      {
        test: /\.tsx?$/,
        loader: "ts-loader"
      },
      {
        test: /\.css$/,
        loader: "style-loader!css-loader"
      },
    ]
  },
  
  externals: {
    "react": {
      root: 'React',
      commonjs2: 'react',
      commonjs: 'react',
      amd: 'react'
    },

    "react-dom": {
      root: 'ReactDOM',
      commonjs2: 'react-dom',
      commonjs: 'react-dom',
      amd: 'react-dom'
    },

    "cytoscape": {
      root: 'Cy',
      commonjs2: 'cytoscape',
      commonjs: 'cytoscape',
      amd: 'cytoscape'
    },

    "chai": {
      root: 'chai',
      commonjs2: 'chai',
      commonjs: 'chai',
      amd: 'chai'
    }
  }
};

tsconfig.json

{
  "compilerOptions": {
    "outDir": "./temp/",
    "sourceMap": false,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es2015",
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "node",
    "jsx": "react"
  },
  "exclude": [
    "node_modules",
    "dist",
    "temp",
    "test"
  ]
}

New es6 app source

import React from 'react';
import ReactDOM from 'react-dom';
import {Wbs, WbsLayout} from '@nodeject/wbs-react';

let graph = {
  nodes: [
    { data: { id: '1' } },
    { data: { id: '1.1', parent: '1' } }
  ],
  edges: [
    { data: { id: 'e1', source: '1', target: '1.1' } }
  ]
};

layout: new WbsLayout(graph); // <-- Fails here (case #2)

ReactDOM.render(<Wbs layout={layout}/>, document.querySelector('#wbs_example'));
Deutzia answered 14/6, 2017 at 14:37 Comment(4)
Seeing as the typings does export a module, import * as cytoscape from "cytoscape"; should work.Biplane
Thanks for answering. I had tried before to add that in WbsLayout.tsx, but got an Uncaught TypeError: cytoscape is not a function error at the very same spot (this.cy = cytoscape({ .....}).Deutzia
Have you tried changing your module target to es2015, your moduleResolution option to node and setting the allowSyntheticDefaultImports option to true?Prestidigitation
Hey @DanielRosenwasser, same error again. Can that be related to webpack? I read some options such as library=your-app-name and others, but I haven't been successful with those either.Deutzia
D
1

I found the answer after noticing that if I removed completely cytoscape from the externals in webpack, it worked. Here's what I needed to do:

"cytoscape": {
  root: 'Cy',
  commonjs2: 'cytoscape',
  commonjs: 'cytoscape',
  amd: 'cytoscape'
 }

had to be:

"cytoscape": {
  root: 'cytoscape', <--------
  commonjs2: 'cytoscape',
  commonjs: 'cytoscape',
  amd: 'cytoscape'
 }
Deutzia answered 18/6, 2017 at 14:18 Comment(0)
F
0

Have you tried importing it as ..

const cytoscape = require('cytoscape');

If yes then can you post your tsconfig.json as well.

Fulk answered 15/6, 2017 at 9:9 Comment(1)
You meant import cytoscape = require('cytoscape') ? Same error, it does not even work as a static asset anymore: cytoscape is not a function. I'll edit my first post to add tsconfig.jsonDeutzia

© 2022 - 2024 — McMap. All rights reserved.