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

devolution

v2.3.1

Published

A devolution gun for your bundle

Downloads

542

Readme

Why?

  • ship more modern, more compact and more fast code to 85+% of your customers

  • do not worry about transpiling node_modules - use as modern code as you can everywhere

  • don't be bound to the bundler

  • well, it's just faster than a multi-compiler mode and 100% customizable.

  • 🚀 fast - uses swc to be a blazing 🔥 fast!

  • 📱 multi threaded - uses jest-worker to consume all your CPU cores

  • 🗜 compact - uses terser without mangling to re-compress the result

  • 🦎 optimized - uses rollup to handle polyfills

  • 🦖 supports core-js 2 and 3

TWO bundles to rule the world

  • One for "esm"(modern) browsers, which you may load using type=module
  • Another for an "old"(legacy) browser, which you may load using nomodule

Usage

1. Compile your code to the modern target and call it a "baseline".

  1. Prefer preset-modules
{
  "presets": [
    "@babel/preset-modules"
  ]
}  
  1. However, feel free to use preset-env with esmodules
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "esmodules": true
      },          
      "useBuiltIns": "usage"
    }]
  ]
}  

useBuiltIns are optional, and plays well with includesPolyfills option in .devolutionrc

  1. sucrase is an option sucrase is much faster than babel(and swc), however is able to produce only "modern" code. However, this is what we need. If your code is not using babel plugins, and non-yet-supported by the browsers code - feel free to use it.

2. Fire devolution to produce de-modernized bundles

the first run would create .devolutionrc.js, which could be used to tune some details

yarn devolution from to
// like
yarn devolution dist dist

It will convert all files in dist into esm and es5 targets in the same dist

By default it will handle only files in the directory, not including subdirs. You might return array of files via .devolutionrc to handle all your files

3 (Only webpack) setup public-path, somewhere close to the script start

__webpack_public_path__ = devolutionBundle + '/'; // devolutionBundle is a predefined variable

Parcel will configure public path automatically.

Symlink

Then devolution will symlink resources to "sub-bundles"

4. Ship the right script to the browser

Please dont use code like this

<script type="module" src="esm/index.js"></script>
<script type="text/javascript" src="ie11/index.js" nomodule></script>

It does not work well for the really "old" browsers - IE11 will download both bundles, but execute only the right one. This syntax would made things even worse for the legacy browsers.

Use feature detection to pick the right bundle:

  var script = document.createElement('script');
  var prefix = (!('noModule' in check)) ? "/ie11" : "/esm"; 
  script.src = prefix + "/index.js"; // or main? you better know
  document.head.appendChild(script);

This "prefix" is all you need.

4, again. SSR this time

However, it's much better to use Server Side logic to pick the right bundle - you might control which bundle should be shipped in which case.

But default - use the same browsers as they are listed in .devolutionrc.js targets for esm, however - you might "raise the bar", shipping modern code only to Chrome 80+, or introduce more than two bundles - the "language" in the top ones could be the same, but polyfills set would be different.

import UA from 'browserslist-useragent'

export const isModernBrowser = (userAgent) => {
  return UA.matchesUA(userAgent, {
    _allowHigherVersions: true,
    browsers: [
      "Chrome >= 61",
      "Safari >= 10.1",
      "iOS >= 11.3",
      "Firefox >= 60",
      "Edge >= 16"
    ]
  })
}

function renderApp(req, res) {
  const userAgent = req.headers['user-agent'];

  const bundleMode = isModernBrowser(userAgent) ? 'esm' : 'es5';
  // send the right scripts
}

See Optimising JS Delivery for details

5. Done!

A few minutes to setup, a few seconds to build

Tuning

See .devolutionrc.js, it contains all information you might look for

FAQ

Why two separate folders?

In the most articles, you might find online, ES5 and ES6 bundles are generated independently, and ES5 uses .js extension, while ES6 uses .mjs.

That requires two real bundling steps as long as "hashes" of files and "chunk names", bundles inside runtime-chunk would be different. That's why we generate two folders - to be able just to use prefix, to enable switching between bundles just using __webpack_public_path__ or parcel script location autodetection.

Drawbacks
  • !! doesn't play well with script prefetching - you have to manually specify to prefetch esm version, not the "original" one.
  • may duplicate polyfills across the chunks. Don't worry much
(default) Targets for "esm"
  • edge: "16+",
  • firefox: "60+",
  • chrome: "61+",
  • safari: "10.1+", (2017+)
(default) Targets for "ie5"
  • ie: "11-"

That's is the oldest living browser, and can be used as a base line.

SWC

SWC is much faster than babel, however not as stable and might produce broken code. Controlled by useSWC, disabled by default

Terser

There are two options, which control minification - useTerser and useTerserForBaseline. You have to enable useTerser if you enable useSWC as long as it produces non minified code.

API

You may file devolution manually

import {devolute} from 'devolution';

devolute(sourceDist, destDist, options)

// for example

devolute(
  'dist', // the default webpack output
  'dist', // the same directory could be used as well
  require('.devolutionrc')     
)

License

MIT