npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

reun

v0.2.7

Published

client-side nodejs-like require using unpkg

Downloads

20

Readme

website github travis npm

TODO: unit testing TODO: documentaiton, - merge into source

REUN - require(unpkg)

Reun is:

  • 100% client-side nodejs-like require for the browser.
  • using https://unpkg.com/.
  • dynamic, just require(...) whatever module you want from your source file. No need for package.json, - versions can be passed to require, i.e. require('[email protected]').
  • pretending to be a synchronous, even though it is asynchrounous. Tries to work in the typical cases, and will always fail in certain documented edge cases. Pragmatic, and not standard compliant.
  • able to directly load many nodejs modules, that has not been packaged for the browser.
  • adding custom functionality when desired, i.e. module.meta

API

  • reun.eval(code, [opt]) execute code, where code is either a function, or the string source of a module. require() is available and is pretending to be synchronous, and done relative to the opt.uri. Returns a promise of the function result or module-exports.
  • reun.require(module, [opt]) loads a module, path is relative to the location.href if available. Returns a promise.

Usage example

index.html:

<!DOCTYPE html>
<html>
  <body>
    <script src=https://unpkg.com/reun></script>
    <script>reun.require('./example.js');</script>
  </body>
</html>

example.js:

var uniq = require('uniq');
console.log(uniq([1,4,2,8,4,2,1,3,2]));

Extensions

  • require('[email protected]') allows you to require a specific version
  • module.meta allows you to set meta information about your module, - this may later be used to automatically package the module for npm, cordova, ...

Incompatibilities

The implementation is a hack. We want to pretend to be synchronous, but we also do not want to block the main thread. Instead require throws an exception when a module is not loaded yet. When we run a file, we catch this exception, load the module asynchrounously, and then rerun the file. Later on we might also search the source for require("..."), or require('...') and try to preload these modules, but this is not implemented yet.

Also we just resolve the module name as 'https://unpkg.com/' + module_name. To be more compatible with node modules, we may check the package.json in the future to make sure that the relative paths in the require works.

  • Custom exceptions from require should not caught.
  • Code before a require, may be executed multiple times, - should be side-effect free.
  • require may fail within callbacks, if the module has not been loaded before.
  • If the source lives in a subdirectory, and the module is not packaged for the web, and contains relative paths, - the paths are wrongly resolved. A workaround is to require('module/lib/index.js') instead of require('module').
  • It does obviously not work with every module.

In spite of these limitations, it is still possible to require many nodejs module directly to the web.

Project setup

(function() { 'use strict';
  var da = typeof direape !== 'undefined' ? direape : require('direape');
  da.testSuite('reun');
  var reun = self.reun || {};
  var modules = {
    reun: reun,
    direape: da
  };

reun.eval(src|fn, opt);

Functions will be called as a module with require, exports, and module as parameters, - similar to http://requirejs.org/docs/commonjs.html

  var runQueue = new Promise((resolve) => da.ready(() => resolve()));

  reun.eval = (fn, opt) => {
    runQueue = runQueue.then(() => do_eval(fn, opt))
      .catch((e)  => da.nextTick(() => { throw e; }));
    return runQueue;
  };

  da.handle('reun:eval', (fn, opt) => 
      reun.eval(fn, opt).then(da.jsonify));

reun.require(module-name, opt);

  reun.require = (name, opt) => 
    reun.eval('module.exports = require("' + name + '",' +
          JSON.stringify(opt || {}) + ');',
        Object.assign({uri: self.location && self.location.href || './'}, opt));

  da.handle('reun:require', reun.require);

Implementation details

moduleUrl

Convert a require-address to a url. path is baseurl used for mapping relative file paths (./hello.js) to url.

  function moduleUrl(module, opt) {
    var path = opt.uri || '';

    if(module.slice(0,4) === 'reun') {
      return 'reun';
    }

    if(module.startsWith('https:') ||
        module.startsWith('http:')) {
      return module;
    }
    path = path.replace(/[?#].*/, '');
    path = (module.startsWith('.')
        ? path.replace(/[/][^/]*$/, '/')  
        : 'https://unpkg.com/');
    path = path + module;
    while(path.indexOf('/./') !== -1) {
      path = path.replace('/./', '/');
    }
    var prevPath;
    do {
      prevPath = path;
      path = path.replace(/[/][^/]*[/][.][.][/]/g, '/');
    } while(path !== prevPath);
    return path;
  }

do_eval

  function do_eval(fn, opt) {
    opt = opt || {};
    if(typeof fn === 'string') {
      fn = stringToFunction(fn, opt);
    }
    return executeModule(fn, opt);
  }

executeModule

  function executeModule(fn, opt) {
    opt.uri = opt.uri || '';
    var require = (name, opt) => reun_require(name, opt, module);
    var module = {
      require: require,
      uri: opt.uri,
      id: opt.uri.replace('https://unpkg.com/', '').replace(/@[^/]*/, ''),
      exports: {}
    };
    if(opt.main) {
      require.main = module;
    }

    return rerunModule(fn, module);
  }

rerunModule

  function rerunModule(fn, module) {
    var result;
    try {
      fn(module.require, module.exports, module);
      result = module.exports;
    } catch (e) {
      if(e.constructor !== RequireError) {
        throw e;
      }
      return da.call(da.nid, 'da:GET', e.url)
        .catch(() => {
          throw new Error('require could not load "' + e.url + '" ' +
              'Possibly module incompatible with http://reun.solsort.com/'); })
        .then((moduleSrc) => executeModule(stringToFunction(moduleSrc, e.opt), 
              e.opt))
        .then((exports) => assignModule(e.url, exports))
        .then(() => rerunModule(fn, module));
    }
    return Promise.resolve(result);
  }

shortName(uri)

  function assignModule(uri, exports) {

    modules[uri] = exports;

Find the short name of the module, and remember it by that alias, to make sure that later requires for the module without version/url returns the already loaded module.

    if(exports.meta && exports.meta.id) {
      modules[exports.meta.id] = exports;
    }

    var name = uri
      .replace('https://unpkg.com/', '')
      .replace(/[@/].*/, '');
    if(!modules[name]) {
      modules[name] = exports;
    }
  }

reun_require

  function reun_require(name, opt, parentModule) {
    if(modules[name]) {
      return modules[name];
    }
    var url = moduleUrl(name, parentModule);
    if(!modules[url]) {
      throw new RequireError(name, url, opt);
    } 
    return modules[url];
  }

stringToFunction

  function stringToFunction(src, opt) {
    var wrappedSrc = '(function(require,exports,module){' +
      src + '})//# sourceURL=' + opt.uri;
    return eval(wrappedSrc);
  }

RequireError

When trying to load at module, that is not loaded yet, we throw this error:

  function RequireError(module, url, opt) { 
    this.module = module; 
    this.url = url;
    opt = opt || {};
    opt.uri = url;
    this.opt = opt;
  }
  RequireError.prototype.toString = function() {
    return 'RequireError:' + this.module +
      ' url:' + this.url;
  };

Main / test runner

  da.ready(() => {
    if((da.isNodeJs() && require.main === module && process.argv[2] === 'test') ||
        (self.REUN_RUN_TESTS)) {
      da.runTests('reun')
        .then(() => da.isNodeJs() && process.exit(0))
        .catch(() => da.isNodeJs() && process.exit(1));
    }
  });
  if(typeof module === 'object') {
    module.exports = reun;
  } else {
    self.reun = reun;
  }

end

})();

License

This software is copyrighted solsort.com ApS, and available under GPLv3, as well as proprietary license upon request.

Versions older than 10 years also fall into the public domain.