I was humming along in a TypeScript Data Viz project and thought I’d use the p5.js
noise function to save some time. Instead I’ve encountered a problem that I can’t fully understand. There seems to be something different with the p5
module compared to d3
and three.js
I’m using in the project. Breaking it down to the very basic elements, I need some help interpreting what’s going on with this module.
import * as p5 from "p5" p5.noise() // Returns the error: // Property 'noise' does not exist on type 'typeof import("/Users/.../ts-node-server/node_modules/@types/p5/index.d.ts")'. ts(2339)
If I try to use the function directly I get a different error.
import { noise } from "p5" // Returns the error: // Module '"p5"' has no exported member 'noise'.ts(2305)
I dug down into the node_modules
and confirmed everything is there. Researching the problem, I noticed other packages had this same error, but all the offered solutions were very specific to the package and project, and when applied did not fit my issue or resolve my problem. I suspect this has something to do with global.d.ts file but nothing looked out of place when I looked. If there are any suggestions on what is happening I will take them.
//Package.json { "name": "ts-node-server", "version": "1.0.0", "description": "project", "main": "build/server.js", "scripts": { "compile": "tsc && node build/server.js", "dev": "./node_modules/nodemon/bin/nodemon.js -e ts --exec "npm run compile"" }, "author": "..", "license": "ISC", "dependencies": { "d3": "^6.6.2", "dotenv": "^8.2.0", "express": "^4.17.1", "p5": "^1.3.1", "three": "^0.127.0" }, "devDependencies": { "@types/d3": "^6.3.0", "@types/three": "^0.127.1", "@types/express": "^4.17.11", "@types/node": "^14.14.37", "@types/p5": "^0.9.1", "nodemon": "^2.0.7" } } //tsconfig.json { "compilerOptions": { "outDir": "./build", "rootDir": "./src", "module": "commonjs", "moduleResolution": "node", "noEmit": false, "esModuleInterop": true, "strict": true, "target": "ES6" }, "include": ["src/**/*"], "exclude": ["**/node_modules", "**/config", "**/build", "**/*.md"] }
Advertisement
Answer
If you must run p5.js functions in a Node.js application written in typescript, here’s one way to do it:
- Add npm dependencies: p5, window, canvas
- Add npm devDependencies: @types/p5
- Inject certain JSDOM window properties into the global scope:
window
,document
,screen
,navigator
Note: This works for the noise
function, but I have no idea what the behavior of any functions that actually attempt to create or draw to a canvas would be.
Here’s a working example in Repl.it.
Here’s my package.json:
{ "name": "p5js-test", "version": "1.0.0", "description": "Test p5.js Node.js app.", "scripts": { "p5js-test": "ts-node --files src/main.ts", "compile": "tsc" }, "bin": { "p5js-test": "./build/src/main.js" }, "author": "Paul Wheeler", "license": "MIT", "dependencies": { "canvas": "^2.7.0", "p5": "^1.3.1", "window": "^4.2.7" }, "devDependencies": { "@types/node": "^15.0.1", "@types/p5": "^0.9.1", "ts-node": "^9.1.1", "typescript": "^4.2.4" } }
And here’s my main.ts:
import Window from 'window'; // globals expected to exist by p5js (must come before the import) // Note: doing this may not be entirely kosher according to the JSDOM documentation // but it gets p5.js working (<any> global).window = new Window(); (<any> global).document = global.window.document; (<any> global).screen = global.window.screen; (<any> global).navigator = global.window.navigator; import p5 from 'p5'; const inst = new p5(p => { // I'm not totally sure this is actually needed p.setup = function() { }; }); console.log('using noise from instance: ' + inst.noise(1, 2, 3));