javascriptfabricjsjspm

How to create jspm bundle with fabric.js v2


I'm trying to create a javascript bundle with JSPM that includes fabric.js version 2. Alas, performing jspm bundle fails.

$ jspm bundle src/main --minify --inject
       Building the bundle tree for src/main...

err  Error on fetch for @empty/lib/jsdom/living/generated/utils.js at file:///Users/dkoerner/projects/JSPMFabricTestCase/@empty/lib/jsdom/living/generated/utils.js
    Loading npm:fabric@2.0.0-rc.4/dist/fabric.js
    Loading npm:fabric@2.0.0-rc.4.js
    Loading src/bootstrap.js
    Loading src/main.js
    Error: ENOENT: no such file or directory, open '/Users/dkoerner/projects/JSPMFabricTestCase/@empty/lib/jsdom/living/generated/utils.js'

I have created a test project (https://github.com/dkoerner85/JSPMFabricTestCase) following the jspm and fabric examples and verified that bundle creation works with the current fabric stable release v1.7.22. Raising the version to the release candidate v2.0.0-rc4 however breaks bundling.

I am fairly new to javascript app development and hence do not understand the reason why this fails and how to repair it. I am grateful for any pointers or explanation.

npm: v5.5.1 node: v8.9.3


Solution

  • Solution

    Add an override section to your project's package.json:

    "overrides": {
      "npm:fabric@2.0.0-rc.4": {
        "map": {
          "canvas": "@empty",
          "fs": "@empty",
          "jsdom/lib/jsdom/living/generated/utils": "@empty",
          "jsdom/lib/jsdom/utils": "@empty",
          "jsdom": "@empty",
          "http": "@empty",
          "https": "@empty",
          "xmldom": "@empty",
          "url": "@empty"
        }
      }
    }
    

    Insight

    After reading about jspm and systemJS, I have found a solution to this problem. The critical code is in the fabric.js and package.json files of the fabric package.

    dist/fabric.js

    ...
    fabric.document = require('jsdom').jsdom(decodeURIComponent('%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'),{ features: {FetchExternalResources: ['img']}});
    fabric.jsdomImplForWrapper = require('jsdom/lib/jsdom/living/generated/utils').implForWrapper;
    fabric.nodeCanvas = require('jsdom/lib/jsdom/utils').Canvas;
    fabric.window = fabric.document.defaultView;
    DOMParser = require('xmldom').DOMParser;
    ...
    

    package.json

    ...
    "browser" : {
       "canvas": false,
       "fs":      false,
       "jsdom":   false,
       "jsdom/lib/jsdom/living/generated/utils": false,
       "jsdom/lib/jsdom/utils": false,
       "http":   false,
       "https":   false,
       "xmldom":  false,
       "url": false
    },
    ...
    

    Now, when installing the package with jspm install npm:fabric@2.0.0-rc.4, jspm will modify the require statements, resulting in the following code:

    jspm_packages/npm/fabric@2.0.0-rc.4/dist/fabric.js

    ...
    fabric.document = require('@empty').jsdom(decodeURIComponent('%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'), {features: {FetchExternalResources: ['img']}});
    fabric.jsdomImplForWrapper = require('@empty/lib/jsdom/living/generated/utils').implForWrapper;
    fabric.nodeCanvas = require('@empty/lib/jsdom/utils').Canvas;
    fabric.window = fabric.document.defaultView;
    DOMParser = require('@empty').DOMParser;
    ...
    

    When trying to create a bundle, jspm will stumble over the '@empty/lib/jsdom/living/generated/utils' require statement.

    Error: ENOENT: no such file or directory, open '/Users/dkoerner/projects/JSPMFabricTestCase/@empty/lib/jsdom/living/generated/utils.js'
    

    jspm treated the jsdom entries first as required by the package.json, thereby failing to recognize the more detailled entry 'jsdom/lib/jsdom/living/generated/utils'. It is necessary to provide the map in the correct order - long paths before short paths.

    This can be achieved with a local override as depicted in the solution above. Please note that jsdom is ordered below @empty/lib/jsdom/living/generated/utils in the override, as opposed to the original package.json.