voxel / voxel-plugins

an API for loading and enabling plugins in voxel.js with soft dependencies
https://github.com/deathcap/voxel-plugins/wiki
9 stars 2 forks source link

Dynamic remote plugin loading? #1

Open deathcap opened 10 years ago

deathcap commented 10 years ago

Currently plugins have to be predetermined at compile time for inclusion with browserify (require('voxel-foo'), for browserify to recognize its needed). Some kind of dynamic or remote plugin loading mechanism could be worthwhile.

Notes from https://github.com/deathcap/voxpopuli/issues/23:


voxel-plugins allows for consistently and easily loading plugins, supports dynamic enable/disable, but you still need to pre-bundle the modules in package.json for browserify to include in bundle.js. Would be neat if plugins could be dynamically loaded across the web from anywhere.

NPM modules can load arbitrarily many other modules, so a resolution process is needed — some discussion here, including an idea to browserify NPM: https://github.com/substack/node-browserify/issues/314 browserify browserify

To load resources across the web: CORS, Access-Control-Allow-Origin: *. Tool for client-side OAuth to GitHub: Gatekeeper

requirebin + browserify-cdn gets very close to what I'd like, but the modules are "compiled" server-side using npm (may not be too much of a problem, though it would be nice to allow loading plugins directly from GitHub, gists, pastebin, etc. without server-side processing).


$script.get('http://example.com/foo.js', function(){}) using https://github.com/ded/script.js allows loading remote scripts (similar to script src=http://example.com/) + http://wzrd.in/ server-side compilation for dependencies?

deathcap commented 10 years ago

script src= would take care of executing the remote script (from raw.github.com, pastebin.com/raw.php, etc.) but the package information file package.json is JSON, not JSON-P, so it cannot cross domains without server-side processing or CORS

deathcap commented 10 years ago

Found this on npm: https://github.com/crcn/plugin.js "Simple plugin system for javascript" - supports remote plugin with "dnode" https://npmjs.org/package/dnode-plugin, appears to be focused on server-side node.js however.

deathcap commented 10 years ago

First part of this in https://github.com/deathcap/voxel-plugins/commit/76331741916f4bb7df3bb850598dd93737b1ce89 - instantiate() now accepts a plugin factory constructor, name, opts, can be passed in directly

deathcap commented 10 years ago

Found this for CORS: http://cors.maxogden.com/ "This API enables cross-origin requests to anywhere." via https://gist.github.com/maxogden/8795074 - demo: https://robwu.nl/cors-anywhere.html source: https://github.com/Rob--W/cors-anywhere/ - could host on https://github.com/joyent/node/wiki/Node-Hosting + and also: http://allow-any-origin.appspot.com/

kumavis commented 10 years ago

checkout requirebin.com if you haven't yet. it is using browserifyCDN. since processed modules are cached, things are fairly performant.

deathcap commented 9 years ago

http://requirebin.com https://github.com/maxogden/requirebin https://github.com/jfhbrook/browserify-cdn https://wzrd.in looks to be the closest to what I'm looking for, but maybe its possible to go further and "browserify npm" (using appropriate shims for web FS access, CORS for remote file fetching, etc.) so a remote service is not needed?

$ npm install npm
$ cat try.js 
var npm = require('npm');
npm.load();
npm.commands.install();
$ browserify try.js > b2.js

then loading into Chrome, crashes at:

/**
 * Creates a Cursor instance based off the given `writable stream` instance.
 */

function ansi (stream, options) {
  if (stream._ansicursor) {   // <-- *** cannot read property _ansicursor of undefined
    return stream._ansicursor
  } else {
    return stream._ansicursor = new Cursor(stream, options)
  }
}
module.exports = exports = ansi

from:

log.cursor = ansi(process.stderr)

process.stderr (and stdout) is undefined in browserify. Something that could be added on top of https://github.com/defunctzombie/node-process (to log to, say, the browser developer console or https://github.com/deathcap/console-widget instead)

more progress:


var Writable = require('stream').Writable;

process.stdout = new Writable();
process.stderr = new Writable();

process.stdout.write = function() {
  console.log('STDOUT', arguments);
};
process.stderr.write = function() {
  console.log('STDERR', arguments);
};

process.binding = function() {
  return {fs: ''}
};

process.argv = ['npm'];

var npm = require('npm');

npm.load();
npm.commands.install();

now crashes on fs.stat - browserify provides an empty fs object; need to replace it with https://github.com/mmckegg/web-fs . trying to replace builtins (better way to do this?):

var browserify = require('browserify');

require('browserify/lib/builtins').fs = 'node_modules/web-fs/index.js';

var b = browserify();
b.add('./try.js');
b.bundle().pipe(process.stdout);

but web-fs is not completely API-compatible with fs, it requires instantiation:

tmp $ cat create-webfs.js 
var webfs = require('web-fs');

module.exports = webfs();
require('browserify/lib/builtins').fs = 'create-webfs.js';

this now crashes in getEntry(this.entry…), where this.entry(=root parameter) is undefined, during fs.stat(p + ext …) on "/npm".

Standard error output from browserified npm is:

error reading version
 TypeError: undefined is not a function↵ info

error reading version
     at file:///tmp/b2.js:21991:25↵ info

error reading version
     at Object.<anonymous> (file:///tmp/b2.js:22414:3)↵ info

error reading version
     at Object.191.../bin/npm-cli.js (file:///tmp/b2.js:22416:4)↵ info

error reading version
     at s (file:///tmp/b2.js:1:254)↵ info

error reading version
     at file:///tmp/b2.js:1:305↵ info

error reading version
     at Object.<anonymous> (file:///tmp/b2.js:25:11)↵ info

error reading version
     at Object.1._process (file:///tmp/b2.js:30:4)↵ info

error reading version
     at s (file:///tmp/b2.js:1:254)↵ info

error reading version
     at e (file:///tmp/b2.js:1:425)↵ info

error reading version
     at file:///tmp/b2.js:1:443↵ info

error reading version
  [TypeError: undefined is not a function]↵

so there is probably much that would need to be done to make this work

edit: got a "browserified npm" to at least load (few commands work) in the browser: https://github.com/deathcap/webnpm

kumavis commented 9 years ago

I've played with these ideas before, a lot of exciting potential! see this long standing issue https://github.com/npm/npm-registry-couchapp/issues/108#issuecomment-73352201 and maybe sidestep it with http://cors.maxogden.com

deathcap commented 9 years ago

Ah, nice to see there is other interest in CORS-enabling the NPM registry, hopefully it happens!

Until then I tried a couple CORS proxies https://github.com/deathcap/webnpm/commit/944d03d2f865d48164ea65ef6e6d9a71c44b8704 https://github.com/deathcap/webnpm/issues/1, unfortunately there are some problems. http://cors.maxogden.com/http://registry.npmjs.org and http://cors-anywhere.herokuapp.com/http://registry.npmjs.org (which run https://github.com/Rob--W/cors-anywhere/) always return Access-Control-Allow-Origin: *, but this wildcard is not allowed in some cases; the server is supposed to echo back the same Origin header it receives, at least that is what CouchDB does with enable_cors. http://npm-registry-cors-proxy.herokuapp.com (running https://github.com/zeke/npm-registry-cors-proxy) also looks promising but it doesn't pass through the OPTIONS requests, failing in modern browsers. Maybe can find a way to get NPM to (disable the "credentials" flag in XHR? Chrome complains ''A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true.'), or worst case setup a new NPM CORS proxy.