Skip to content

Is Webpack + JSDOM + Canvas 2.x a supported environment? #1314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
MeoMix opened this issue Nov 30, 2018 · 4 comments
Closed

Is Webpack + JSDOM + Canvas 2.x a supported environment? #1314

MeoMix opened this issue Nov 30, 2018 · 4 comments
Labels

Comments

@MeoMix
Copy link

MeoMix commented Nov 30, 2018

Hey there. I'm struggling to get node-canvas working well with Webpack and JSDOM in a testing environment.

My goal is to test some code which is dependent on the result of context.getImageData in a headless browser environment. I am trying to achieve this using a combination of Mocha + Webpack + JSDOM + node-canvas.

Currently running in a Windows 10 environment with the following:

  • "canvas": "^2.1.0",
  • "jsdom": "^13.0.0",
  • "webpack": "^4.23.1",
  • "mocha-webpack": "^2.0.0-beta.0",

I have read through the provided installation guide. Here are some other resources I've looked at which were illuminating, but ultimately not helpful:


My current understanding is:

  • JSDOM 13.0 adds support for Canvas 2.x and will attempt to require the canvas module if possible.
  • Canvas 2.x dropped the need for pre-built binaries (canvas-prebuilt) and should "just work" out of the box. Canvas is built to run in a node environment.
  • Webpack must be told that it is executing in a node environment and configuring externals to exclude canvas may or may not be helpful.

and where I'm at with my code is:

  • I am able to load JSDOM from within my Webpack/Mocha environment without issue.
  • If I attempt import Canvas from 'canvas' or const Canvas = require('canvas') I error out with the message: Error in ./node_modules/canvas/build/Release/canvas.node: Module parse failed: Unexpected character '�' (1:0)

I am wondering if this is a supported scenario and, if it is, what steps I should be taking? If not, is the only solution to either A) only use Mocha + JSDOM + Canvas without webpack or B) test in a real browser environment using Karma?

Thanks for your time. Sorry, I know you get this question a lot in many different permutations, but I didn't see a canonical response and, given the recent update to 2.x, I thought things might have changed from what I read online.

@MeoMix
Copy link
Author

MeoMix commented Nov 30, 2018

Ah yeah, of course, shortly after taking the time to write up my issue I think I've figured it out. The solution was right under my nose.

Need to install and configure https://webpack.js.org/loaders/node-loader/ and now I see things loading better. Will update in a moment once I've confirmed I'm off to the races.

@zbjornson
Copy link
Collaborator

Your three bullet points are all correct. To get webpack to not bundle canvas, your webpack.config.js should look like this:

const path = require("path");
module.exports = {
	target: "node", // Important (1)
	entry: "./test.js",
	output: {
		path: path.resolve(__dirname, "dist"),
		filename: "output.js"
	},
	externals: {
		canvas: "commonjs canvas" // Important (2)
	}
};

(1) indicates the bundle needs to be made to run with Node.js. (2) indicates that canvas shouldn't be bundled, and instead be loaded with a normal require("canvas") statement.

Your (A) alternative (don't use webpack) might be a nice simplification, depending on what your motivations are for using webpack in the first place.

See also https://jlongster.com/Backend-Apps-with-Webpack--Part-I.

@zbjornson
Copy link
Collaborator

🙂 Glad you found a fix. node-loader achieves roughly the same thing -- I think that makes only .node files external, and will still bundle the JS parts of canvas.

@MeoMix
Copy link
Author

MeoMix commented Nov 30, 2018

:) Yeah. I see now that they're roughly equivalent for my needs and that I was expressing externals improperly the first time.

I have the node-canvas library working well. I am able to run the following within a Mocha + Webpack environment:

import { createCanvas } from 'canvas';

const canvas = createCanvas(200, 200);
const ctx = canvas.getContext('2d');
console.log('Context exists?', !!ctx); // true

However, I still have not figured out why JSDOM isn't interfacing with it properly:

const jsdom = require('jsdom'); // import works identically
const { JSDOM } = jsdom;
const window = (new JSDOM(``, { pretendToBeVisual: true })).window;
const canvas2 = window.document.createElement('canvas'); // Throws error
const context2 = canvas2.getContext('2d');
console.log('context2 exists?', !!context2); // false

This may be environment-specific though, I'll update this in a bit when I've resolved this last issue.

The issue is that JSDOM isn't find the canvas module. Still unclear why. I tried remove the "canvas": false from the "browser" declaration in JSDOM's package.json, but didn't see any effect.

Looks like declaring just canvas as external wasn't sufficient.

Changing my code to use externals: [nodeExternals()],, being mindful of webpackMerge (ensuring it doesn't overwrite settings I think I'm using), and ensuring the target is node was sufficient.

Thanks for the sanity check! Good luck to anyone else struggling :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants