Open JerrySievert opened 5 years ago
This makes it difficult to bundle the script into a sharable patch file.
I realize that, and it was copying/pasting modules into my script to share that made me make the proposal.
while it does make that use-case a bit more difficult, it makes clean code and extendability much easier.
I'll create a PR with the change to add modules so we can have a more "informed" discussion. there would still be a few questions to work out, but might as well get the discussion moving forward.
amusingly, QuickJS has 2 modes of interpretation:
import
worksI've sent an email to the devel list, to hopefully get a sane response back about, well, you know, being able to both import modules and run code.
I'm not really interested in the script engine implementation but the Prototype Module
implementation. Scripts are included in patches so people can share their patch so others can try their script. Including multiple scripts, each with a path, in the module data would be a mess.
the overall issue is that without having a mechanism for modules to be loaded, there ends up being a ton of bespoke "modules" and code duplication that becomes very unmaintainable very quickly.
it's possible to build complex and powerful audio applications in javascript, and vcv-prototype is definitely fast enough to be able to run them, but hampering the implementation makes this much more difficult. it becomes the same as removing "#include" from c/c++: possible to do, but untenable in the end.
being able to access the huge module ecosystem, and share audio or vcvrack specific modules (like the CV module I copied/pasted in my community example) ends up making the whole community stronger.
I'm also not really interested in the motivation because I understand it well. I repeat: I'm interested in the "Prototype Module
implementation".
I'm interested in the "Prototype Module implementation".
and I'm interested in beer. without expanding on that I have to guess that you mean that you're interested solely in people making small things and sharing them.
Perhaps I'm being too vague in what I mean by that. For me to approve this proposal, I'd like to see what changes will need to be done to src/Prototype.cpp
and Prototype's JSON "data"
tag to make user-included JavaScript modules sharable. This is necessary to keep the promise "You can share your script by sharing your patch."
Separate idea: Instead of allowing users to download and include their own JavaScript modules, why not just bundle popular ones in the Prototype package? I bet we could find 10+ useful open-source BSD/MIT/GPL/etc libraries like https://mathjs.org/, https://github.com/corbanbrook/dsp.js/, etc. to include in a dep/js-modules
folder or something. Then, it's ever easier for users to use common JS libraries, because they can skip the process of downloading and including them. We can happily accept recommendations of libraries from users to eventually collect everything everyone needs.
Users could add to their script
import math from 'mathjs'
or
const math = require("mathjs")
or however we do it. Since mathjs would be included on everyone's computers, the patch would not need to include the module source code itself.
As a side-effect of this proposal, I'm happy to then allow users to add their own JS modules and import/require
them, because the action of doing this should suggest to them that their script becomes "unsharable". In other words, I accept your proposal to add JS modules, under the condition that we go out and find dozens of JS libraries to bundle so scripts remain mostly sharable.
Perhaps I'm being too vague in what I mean by that.
hence my comment :)
I'd like to see what changes will need to be done to src/Prototype.cpp and Prototype's JSON "data" tag to make user-included JavaScript modules sharable.
to use modules, nothing - the questions that arise from this are a little more subtle imo:
node_modules
or some other npm
or yarn
path?alternately, a change could be made to Prototype.cpp
to add a module path. this would still require users to install modules, but give them access to a setting to specify the path.
I'm also ok with a Prototype module distribution as part of the distribution, but it would mean added cognitive load. while I'm guessing you wouldn't be able to take that load, it wouldn't be much for me.
this issue was opened to gauge interest, not to provide a concrete proposal, just so we're on the same page. opening a PR would have solidified more of that.
which mechanisms should be allowable for including modules:
Don't include any of those. Just mathjs, etc. Anything that can run in a browser. Nothing Node-like such as os
or std
from QuickJS.
should we reinvent a packaging system?
No. Just install mathjs, etc to some directory so that import
can access them, and commit the source. You can add them by downloading them manually, use git submodules, npm, Makefile dep targets, or any other method to add them.
which module system should be supported?
Probably ECMA if possible.
a change could be made to Prototype.cpp to add a module path.
I disagree. It should be hard-coded to asset::plugin(..., "js-modules")
or something, which would include only bundled modules unless a user messes with the directory.
I disagree. It should be hard-coded to asset::plugin(..., "js-modules") or something, which would include only bundled modules unless a user messes with the directory.
my issue with this is if there is work in progress by the user, and it is overwritten due to a module update. if module updates were completely non-destructive, this wouldn't be an issue (I have not investigated whether module updates remove the old module and install the new one, or if it is an overwrite preserving content).
Probably ECMA if possible.
actual lowers the number of usable modules, since ECMA is about a decade behind the module curve.
Anything that can run in a browser. Nothing Node-like such as os or std from QuickJS.
you might want to qualify that a little more, browserjs exists, and having been neck deep in plv8 I'm way too aware of how easy it is to wedge a package.
No. Just install mathjs, etc to some directory so that import can access them, and commit the source. You can add them by downloading them manually, use git submodules, npm, Makefile dep targets, or any other method to add them.
I'd love a canonical answer on my first point without having to trace through code.
my issue with this is if there is work in progress by the user
The plugin updater merely overwrites modules, so it's fine. Question: Can the module loader escape from the module path, e.g. require("../x.js")
or require("/x.js")
?
actual lowers the number of usable modules
Okay, then CommonJS. I figured there was maybe something built-in that transformed module.exports
to ECMA exports.
you might want to qualify that a little more
My clarification is "Modules should be pure JS, not native". os
obviously needs to be native because you can't break out of the sandbox with pure JS. Mathjs, etc are written in pure JS.
I'd love a canonical answer on my first point without having to trace through code.
I don't know what you mean by this, but can't you just find the library script and copy is to js-modules
manually? For mathjs, that'd be https://unpkg.com/mathjs@6.2.3/dist/math.min.js.
The plugin updater merely overwrites modules, so it's fine.
great, that's what I was worried about.
I don't know what you mean by this
the first point is ^^^ so answered.
Okay, then CommonJS. I figured there was maybe something built-in that transformed module.exports to ECMA exports.
ha. no. that would be too easy. browserjs manages to convert between node and browser modules, and babel does a lot of transformation as part of a build system, but if you're looking for any real commonality then prepare to sigh and roll your eyes.
Can the module loader escape from the module path, e.g. require("../x.js") or require("/x.js")?
yes, no. depends on how the module loader is written: it looks like you're wanting a sandbox - no problem, it's just a matter of checking paths.
fine with pure js modules, and not including os
or std
: that's been my default for a long time.
Okay, do we have all the answers we need then? This should require 0 changes to src/Prototype.cpp or src/ScriptEngine.cpp. Let's go crazy with bundling JS libraries then. Could you add one as an example?
Okay, do we have all the answers we need then?
probably not, but enough to proceed.
Let's go crazy with bundling JS libraries then. Could you add one as an example?
only one? geez. yeah, I'll get a couple added and some documentation. it'll take a day or so for me to get something I'm happy enough with for a require
but I'll get a PR open when I'm done.
I've just found this project and it looks like a lot of fun to experiment with!
Edit: I think I missed some of the nuances of this convo so I've deleted my message below. I see now that you're opting for a system where if you include a third party module you aren't able to share the script which seems like a good compromise!
Is this still in the works?
@gridsystem
I wonder also if you'd consider something similar to webpack
Can you give me a 60 second pitch of this. Explain what it is, how users would use it, etc.
Sure. (For anyone reading later, that quote is from my original comment where I wrongly assumed only modules included in the core would be supported)
Dev writes script. Dev includes module import Foo from 'foo'
Task runner (e.g. webpack, gulp, grunt or other) is running in terminal during dev. It watches for file changes and runs a compilation task when code changes. The compilation task bundles code and all dependencies into an executable script.
A separate final production script could be run after dev, e.g. npm run build
or gulp build
which would minify the bundled code, strip out console.log
s or run any other desired optimisation to prepare for distribution.
@gridsystem I think you're confusing VCV Prototype for Node or something. Users don't use a terminal when running VCV Prototype.
I've edited the original post to reflect the current proposal that @JerrySievert and I converged on (if I'm interpreting our old discussion above correctly).
Those task runners do run using node.js. You could leverage node.js as a development tool, it's a very common way to work.
Applying a normal JS workflow to this, it would work this way.
vcv-js-sdk
repo, containing
mymodule.js
filedist
directorypackage.json
containing npm run dev
and npm run build
commandsgit clone vcv-js-sdk
mymodule.js
npm run dev
in a terminal windowdist/mymodule.js
in Prototype modulenpm run build
dist/mymodule.js
file and share online, which includes all of the dependencies your heart desires.That's way complicated. I think the current proposal for modules is fine.
Do you know if any progress has been made or planned with the current proposal?
That idea is achievable without changing the module. I'm not sure if I have time coming up to throw it together, but it could be done as an independent project.
Do you know if any progress has been made or planned with the current proposal?
No. I'd guess it'd be a 10-20 line feature so it would be done in one sitting.
That idea is achievable without changing the module
I don't know what this sentence means. What is "the module"?
Cool! That's awesome that it's so achievable.
Sorry - module is a pretty generic term in this context I guess. I mean VCV eurorack module.
jeez, try to sleep in one morning and you miss all sorts of conversations!
I'll try to jump in with a couple of comments:
Do you know if any progress has been made or planned with the current proposal?
I've had other projects (software and hardware, and other things that I make my living doing) going on, so I haven't given this that much priority, and it hasn't jumped up in priority due to a couple of things:
while fully featured (I can build javascript copies of most any module within the confines of the inputs, outputs, and knobs), this is still very much a prototyping tool. I don't have install/usage numbers, but I've seen very few people using it in the wild - more adoption would mean higher priority toward features
it's slow and cpu intensive. and I'm not talking about the javascript engine - in order to work, there's a lot of movement between c++ and javascript. while quickjs is faster than v8 crossing the c++/javascript membrane, it's still a bit of a hit. running an oscillator takes very little cpu time, but there's still all that overhead, contributing to lower adoption
what's actually required: deciding whether to use commonjs or es6 modules and writing a loader.
and here's a module that I wrote for a similar module to this from 0.6 (see https://github.com/JerrySievert/ProtoTools) - an LFO that I am hereby contributing as GPLv3 code (or whatever specific license that this repo adopts in the future):
function LowFrequencyOscillator ( ) {
this.phase = 0;
this.pw = 0.5;
this.freq = 1;
}
LowFrequencyOscillator.prototype.setPitch = function setPitch (pitch) {
pitch = Math.min(pitch, 8.0);
this.freq = 261.626 * Math.pow(2, pitch);
};
LowFrequencyOscillator.prototype.setFrequency = function setFrequency (frequency) {
this.freq = frequency;
};
LowFrequencyOscillator.prototype.setPulseWidth = function setPulseWidth (pw) {
this.pw = Math.max(Math.min(pw, 0.99), 0.01);
};
LowFrequencyOscillator.prototype.step = function step (dt) {
var deltaPhase = Math.min(this.freq * dt, 0.5);
this.phase += deltaPhase;
if (this.phase >= 1) {
this.phase -= 1;
}
};
LowFrequencyOscillator.prototype.sin = function sin ( ) {
return Math.sin(2 * Math.PI * this.phase);
};
LowFrequencyOscillator.prototype.tri = function tri ( ) {
var x = this.phase - 0.75;
return (4 * Math.abs(x - Math.round(x))) - 1;
};
LowFrequencyOscillator.prototype.saw = function saw ( ) {
return 2 * (this.phase - Math.round(this.phase));
};
LowFrequencyOscillator.prototype.sqr = function sqr ( ) {
return (this.phase < this.pw) ? 1 : -1;
};
LowFrequencyOscillator.prototype.noise = function noise ( ) {
return (Math.random() - 0.5) * 2;
};
module.exports = exports = LowFrequencyOscillator;
note that this is in commonjs - I just haven't taken the time to write the 10-20 line loader, which would be a welcome addition if you wanted to take it on inside the realm of the proposal @gridsystem
Haha I hope the snooze was worth it!
I would love to help with this, but I don’t know a thing about C.
I could put together a js bundler as I described, but it wouldn’t be worth doing if a native module loader is going to be included. If it becomes a more realistic choice, let me know and I can try to find some time. I relate completely to you having other commitments.
Really cool to see the osc code! Thanks for sharing. Would be cool to get it published on npm, I would gladly set up a repo and do that if you’d want.
wondering what you think about adding a javascript module loader?
modules would be able to be loaded from either a specific defined path, or from a configured path, either absolute or relative.
a decision would likely need to be made whether to use CommonJS or ES6 modules.
alternately, enabling the
std
andos
modules in QuickJS would allow access to the filesystem, local machine, etc.Current proposal as of 2020-06-11 (edit by @AndrewBelt )
js_modules/
or something in Prototype's plugin directory.JSModuleLoaderFunc
or however it works in order to allow scripts to useimport "whatever"
to load a script/module fromjs_modules/
.