uuidjs / uuid

Generate RFC-compliant UUIDs in JavaScript
MIT License
14.61k stars 902 forks source link

NODE uuid vs BROWSER uuid #128

Closed coolaj86 closed 7 years ago

coolaj86 commented 9 years ago

We just need to have three files - one for browser, one for node, one for transpilers.

Of the 200 lines of the file over 50 of them are trying to deal with this and that environment and whatnot.

I'm running code on arm devices and mobile phones and you would be amazed at how garbage code like that really does go from milliseconds of parse time to milliseconds of compile time and eventually whole seconds as you include more and more modules.

This is such a simple task and #126 has made it very clear that there's just as much complexity in the tooling as in the code.

Proposed solution:

package.json

{ "version": "v2.0.0"
// ...
, "browser": {
    "./node-uuid.js": "./transpilers-uuid.js"
  }
}

We could maybe have a mustache template that runs on npm publish to copy similar code into the browser version and then another that copies amd/browserify/etc version.

Or just say "This is a node package, damn it! Can't you read!?!?" and be done with it. :-)

broofa commented 9 years ago

If/when this project starts having separate files for the different versions of uuid (v1 .vs. v3. vs. v4, etc), there will be a rather atrocious M x N problem in the number of files being generated. "Did you want v1 uuids in the browser? ... or v4 uuids for node? ... or something else?"

Packaging is hard. :-/

coolaj86 commented 8 years ago

Well node is super easy to solve for: disk reads take very little time - especially on newer file systems that put files <4k into the directory metadata, so your best best is always to break up the files into smaller pieces and you save memory on not having functions you're not actually using.

The browser is more difficult because you're trying to solve problems of latency, file size, and v8 / nitro parse and compile time all at once. Loading code you're not going to use is critical on mobile because it noticeably slows load time. Countable milliseconds. Waiting to load code that you will probably use is also bad because ping times are 300ms, you're better to include it. Loading code that you might not use is bad because the parse and compile times are horrific - especially on Android.

For the browser I would want to use the crazy 140-character v4 uuid generator - unless I was generating dozens of uuids (read: countable milliseconds) at a time in which case I would want node-uuid but without all the extra muck.

Which brings me back to the point of this seems like it should just be a node module, create another module that's meant for the browser with optional load files like how Unibabel.js optionally handles base32 and hex encoding if you include / concatenate the extra files.

coolaj86 commented 8 years ago

Or, an even smarter idea if you want to remain browser compatible is to use the dependency injection pattern (not to be confused with Angular's implementation) and no environment shims whatsoever.

function uuidv1(hrtime) {
   // ... call hrtime expecting the results in a particular 
}

function uuidv4(rng) {
   // ... call rng expecting n bytes
}

If you need a module, simply require it to be passed in.

Then in your examples you would specify

// for the browser
function rng(n) {
  var _crypto = window.crypto || window.msCrypto || window.webkitCrypto;
  if (_crypto) {
    return _crypto.getRandomValues(new Uint8Array(n));
  }

  var _rnds = [];
  _rnds.length = n; // instead of new Array()
  for (var i = 0, r; i < n; i++) {
    if ((i & 0x03) === 0) { r = Math.random() * 0x100000000; }
    _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
  }

  return _rnds;
}

// for node
function rng(n) {
  return require('crypto').randomBytes(n);
}

Then you're exporting your algorithm and the declaring your dependencies without relying on any environment and from there you could create node-uuid, browser-uuid, and transpiler-uuid as separate packages that wrap the algorithm with a function that supplies the dependencies and exports that for general use.

BAM! Isomorphic code without any complications.

eldarshamukhamedov commented 8 years ago

+1 to this idea. There are really three major use cases:

The first one is the single-file case that is only useful in browsers, so you could package it without all the Node-specific code. The second and third cases are the same since NPM is the defacto source of packages for both. All that's left is to split out uuidv1 and uuidv4, as per @coolaj86's DI suggestion.