{"id":13825499,"url":"https://github.com/theKashey/devolution","last_synced_at":"2025-07-08T22:31:22.489Z","repository":{"id":34244619,"uuid":"172812876","full_name":"theKashey/devolution","owner":"theKashey","description":"🦎 -\u003e 🦖A de-evolution gun for your bundle!","archived":false,"fork":false,"pushed_at":"2023-01-03T16:55:06.000Z","size":958,"stargazers_count":195,"open_issues_count":24,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-04-24T14:57:54.506Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/theKashey.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-27T00:30:19.000Z","updated_at":"2024-04-12T10:07:42.000Z","dependencies_parsed_at":"2023-01-15T05:45:39.650Z","dependency_job_id":null,"html_url":"https://github.com/theKashey/devolution","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theKashey%2Fdevolution","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theKashey%2Fdevolution/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theKashey%2Fdevolution/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theKashey%2Fdevolution/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/theKashey","download_url":"https://codeload.github.com/theKashey/devolution/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225470631,"owners_count":17479366,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T09:01:22.246Z","updated_at":"2024-11-20T04:30:28.538Z","avatar_url":"https://github.com/theKashey.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003e🦎 -\u003e DEvolution -\u003e 🦖\u003c/h1\u003e\n  \u003cbr/\u003e\n  \u003cimg src=\"./assets/devo-logo.jpg\" alt=\"devolution\" width=\"409\" align=\"center\"\u003e\n  \u003cbr/\u003e\n  \u003cbr/\u003e\n  de-evolution gun, as seen in Mario Bros, to help you ship modern, and de-modernized bundles. \n  \u003cbr/\u003e\n  \u003cbr/\u003e\n    \u003ca href=\"https://www.npmjs.com/package/devolution\"\u003e\n      \u003cimg src=\"https://img.shields.io/npm/v/devolution.svg?style=flat-square\" /\u003e\n    \u003c/a\u003e\n\n\u003c/div\u003e\n\n\n## Why?\n- ship more modern, more compact and more fast code to 85+% of your customers\n- do not worry about transpiling node_modules - use as modern code as you can everywhere\n- don't be bound to the bundler\n- well, it's just faster than a `multi-compiler mode` and __100% customizable__.\n\n- 🚀 fast - uses [swc](https://github.com/swc-project/swc) to be a blazing 🔥 fast!\n- 📱 multi threaded - uses [jest-worker](https://github.com/facebook/jest/tree/master/packages/jest-worker) to consume all your CPU cores\n- 🗜 compact - uses [terser](https://github.com/terser-js/terser) without mangling to re-compress the result\n- 🦎 optimized - uses [rollup](https://rollupjs.org/guide/en/) to handle polyfills\n- 🦖 supports `core-js` 2 and 3 \n\n### TWO bundles to rule the world\n\n- One for \"esm\"(modern) browsers, which you may load using `type=module`\n- Another for an \"old\"(legacy) browser, which you may load using `nomodule`\n\n## Usage\n### 1. Compile your code to the `modern` target and call it a \"baseline\".\n1. Prefer [preset-modules](https://github.com/babel/preset-modules)\n```json\n{\n  \"presets\": [\n    \"@babel/preset-modules\"\n  ]\n}  \n```\n2. However, feel free to use [preset-env with esmodules](https://babeljs.io/docs/en/babel-preset-env#targetsesmodules) \n```json\n{\n  \"presets\": [\n    [\"@babel/preset-env\", {\n      \"targets\": {\n        \"esmodules\": true\n      },          \n      \"useBuiltIns\": \"usage\"\n    }]\n  ]\n}  \n```\n\u003e `useBuiltIns` are optional, and plays well with `includesPolyfills` option in `.devolutionrc`\n\n3. [sucrase](https://github.com/alangpierce/sucrase) is an option\n`sucrase` is much faster than babel(and swc), however is able to produce only \"modern\" code.\nHowever, this is what we need.\nIf your code is not using babel plugins, and non-yet-supported by the browsers code - feel free to use it.\n\n### 2. Fire devolution to produce de-modernized bundles\n\u003e the first run would create `.devolutionrc.js`, which could be used to tune some details\n```bash\nyarn devolution from to\n// like\nyarn devolution dist dist\n```\nIt will convert all files in `dist` into `esm` and `es5` targets in the same `dist`\n\n\u003e By default it will handle only files in the directory, __not__ including subdirs.\nYou might return array of files via `.devolutionrc` to handle all your files\n\n### 3  (Only webpack) setup `public-path`, somewhere close to the script start\n```js\n__webpack_public_path__ = devolutionBundle + '/'; // devolutionBundle is a predefined variable\n```\n\u003e `Parcel` will configure public path automatically.\n\n\n#### Symlink\nThen `devolution` will symlink resources to \"sub-bundles\" \n\n### 4. Ship the right script to the browser\nPlease __dont use__ code like this\n```html\n\u003cscript type=\"module\" src=\"esm/index.js\"\u003e\u003c/script\u003e\n\u003cscript type=\"text/javascript\" src=\"ie11/index.js\" nomodule\u003e\u003c/script\u003e\n```\n\nIt does not work well for the really \"old\" browsers - __IE11 will download both bundles__, but execute only the right one.\nThis syntax would made things even worse for the legacy browsers.\n\nUse feature detection to pick the right bundle:\n```js\n  var script = document.createElement('script');\n  var prefix = (!('noModule' in script)) ? \"/ie11\" : \"/esm\"; \n  script.src = prefix + \"/index.js\"; // or main? you better know\n  document.head.appendChild(script);\n```\nThis \"prefix\" is all you need.\n\n### 4, again. SSR this time\nHowever, it's much better to use Server Side logic to pick the right bundle - you might control\nwhich bundle should be shipped in which case.\n\nBut default - use the same `browsers` as they are listed in `.devolutionrc.js` `targets` for `esm`,\nhowever - you might \"raise the bar\", shipping modern code only to `Chrome 80+`,\nor introduce __more than two__ bundles - the \"language\" in the top ones could be the same, but polyfills set would be different. \n\n```js\nimport UA from 'browserslist-useragent'\n\nexport const isModernBrowser = (userAgent) =\u003e {\n  return UA.matchesUA(userAgent, {\n    _allowHigherVersions: true,\n    browsers: [\n      \"Chrome \u003e= 61\",\n      \"Safari \u003e= 10.1\",\n      \"iOS \u003e= 11.3\",\n      \"Firefox \u003e= 60\",\n      \"Edge \u003e= 16\"\n    ]\n  })\n}\n\nfunction renderApp(req, res) {\n  const userAgent = req.headers['user-agent'];\n\n  const bundleMode = isModernBrowser(userAgent) ? 'esm' : 'es5';\n  // send the right scripts\n}\n```\n\nSee [Optimising JS Delivery](https://dev.to/thekashey/optimising-js-delivery-4h6l) for details\n\n### 5. Done!\n\nA few minutes to setup, a few seconds to build\n\n\n## Tuning\nSee `.devolutionrc.js`, it contains all information you might look for\n\n## FAQ\n\n##### Why two separate folders?\nIn the most articles, you might find online, ES5 and ES6 bundles are generated independently,\nand ES5 uses `.js` extension, while ES6 uses `.mjs`.\n\nThat requires two real bundling steps as long as \"hashes\" of files and \"chunk names\", bundles inside `runtime-chunk` would be different.\nThat's why we generate two folders - to be able just to use prefix, to enable switching between bundles just using\n`__webpack_public_path__` or parcel script location autodetection.\n\n##### Drawbacks\n\n- __!!__ doesn't play well with script _prefetching_ - you have to manually specify to prefetch `esm` version,\nnot the \"original\" one.\n- may duplicate polyfills across the chunks. Don't worry much\n\n##### (default) Targets for \"esm\"\n - edge: \"16+\",\n - firefox: \"60+\",\n - chrome: \"61+\",\n - safari: \"10.1+\",\n(2017+)\n\n##### (default) Targets for \"ie5\"\n - ie: \"11-\"\n \nThat's is the oldest living browser, and can be used as a base line.  \n\n#### SWC\nSWC is much faster than babel, however not as stable and might produce broken code.\nControlled by `useSWC`, disabled by default\n\n#### Terser\nThere are two options, which control minification - `useTerser` and `useTerserForBaseline`.\nYou __have__ to enable `useTerser` if you enable `useSWC` as long as it produces non minified code.\n\n### API\nYou may file devolution manually\n```js\nimport {devolute} from 'devolution';\n\ndevolute(sourceDist, destDist, options)\n\n// for example\n\ndevolute(\n  'dist', // the default webpack output\n  'dist', // the same directory could be used as well\n  require('.devolutionrc')     \n)\n```\n\n# License\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FtheKashey%2Fdevolution","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FtheKashey%2Fdevolution","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FtheKashey%2Fdevolution/lists"}