{"id":19226384,"url":"https://github.com/untool/mixinable","last_synced_at":"2025-06-13T04:07:04.225Z","repository":{"id":26889564,"uuid":"109185940","full_name":"untool/mixinable","owner":"untool","description":"A functional JavaScript mixin library that is at the core of untool's core. Supports synchronous and asynchronous mixin method implementations.","archived":false,"fork":false,"pushed_at":"2025-05-10T17:37:24.000Z","size":1726,"stargazers_count":6,"open_issues_count":15,"forks_count":2,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-05-25T06:48:03.169Z","etag":null,"topics":["composition","delegation","framework","mixins","roles","traits"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/mixinable","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/untool.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-01T21:28:50.000Z","updated_at":"2024-12-17T17:09:19.000Z","dependencies_parsed_at":"2024-01-02T11:08:56.736Z","dependency_job_id":"22ad12fd-74a6-49cd-8ac3-279eb8b1276a","html_url":"https://github.com/untool/mixinable","commit_stats":{"total_commits":284,"total_committers":6,"mean_commits":"47.333333333333336","dds":0.4154929577464789,"last_synced_commit":"ef5f3f2d7a1aee62482990b2d008ae16f14199f9"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/untool/mixinable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/untool%2Fmixinable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/untool%2Fmixinable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/untool%2Fmixinable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/untool%2Fmixinable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/untool","download_url":"https://codeload.github.com/untool/mixinable/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/untool%2Fmixinable/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259578215,"owners_count":22879215,"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":["composition","delegation","framework","mixins","roles","traits"],"created_at":"2024-11-09T15:18:30.814Z","updated_at":"2025-06-13T04:07:04.197Z","avatar_url":"https://github.com/untool.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mixinable\n\n[![travis](https://img.shields.io/travis/untool/mixinable/master.svg)](https://travis-ci.org/untool/mixinable)\u0026nbsp;[![npm](https://img.shields.io/npm/v/mixinable.svg)](https://www.npmjs.com/package/mixinable) \u003cbr/\u003e\n\n`mixinable` is a small functional utility library allowing you to use [mixins](https://addyosmani.com/resources/essentialjsdesignpatterns/book/#mixinpatternjavascript) in your code. More specifically, it allows you to create mixin containers that apply mixin method application strategies to mixin method implementations.\n\nMixins are usually classes that can easily be shared, modified and extended using standard language features. `mixinable` supports asynchronous methods returning [`Promises`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promises).\n\n### Installation\n\nUsing [NPM](https://www.npmjs.com/get-npm):\n\n```text\nnpm install -S mixinable\n```\n\nUsing [Yarn](https://yarnpkg.com/en/):\n\n```text\nyarn add mixinable\n```\n\nTo be able to use `mixinable`, you will have to make sure your environment supports [`Promise`](https://kangax.github.io/compat-table/es6/#test-Promise): if, for example, you need to support IE11, you will have to [polyfill](https://polyfill.io/v2/docs/) `Promise`.\n\n### API\n\n#### `define(definition, mixins)`\n\nThe main export of `mixinable` is a `define()` function accepting a mixin container definition and an array of mixins, i.e. classes. The definition hash is made up of `strategy` functions prescribing how to handle different mixin methods you provide. It returns a `create()` function that accepts whatever arguments your mixins' `constructor()`s accept.\n\n##### Example\n\n```javascript\nimport define from 'mixinable';\n\nconst create = define(\n  {\n    // mixin strategy function\n    bar(functions, arg) {\n      return functions.pop()(arg);\n    },\n  },\n  [\n    // mixin implementation\n    class {\n      bar(arg) {\n        console.log(arg);\n      },\n    }\n  ]\n);\n\nconst foo = create();\n\nfoo.bar('yeah!');\n// yeah!\n```\n\nSpeaking of which: `mixinable` creates a separate hidden mixin instance for every mixin you provide. When used inside a mixin method, `this` refers to this hidden instance.\n\nMixin methods not included in your definition are only accessible from within the mixin instance itself, but not from the outside.\n\n##### Example\n\n```javascript\nimport define from 'mixinable';\n\nconst create = define(\n  {\n    bar(functions, arg) {\n      return functions.pop()(arg);\n    },\n  },\n  [\n    // mixin implementations\n    class {\n      // public mixin method\n      bar(arg) {\n        this.qux(arg);\n      },\n      // private mixin method\n      qux(arg) {\n        console.log(arg);\n      },\n    }\n  ]\n);\n\nconst foo = create();\n\nfoo.bar('yeah!');\n// yeah!\n\nconsole.log(typeof foo.bar, typeof foo.qux);\n// function undefined\n```\n\nMixin definitions can be (or contain) custom constructors. These functions are being passed the `create()` function's arguments upon creation.\n\n##### Example\n\n```javascript\nimport define from 'mixinable';\n\nconst create = define({}, [\n  // mixin contructors\n  class {\n    constructor(arg) {\n      console.log(arg);\n    }\n  },\n]);\n\ncreate('yee-hah!');\n// yee-hah!\n```\n\n#### `define.override`\n\n`override` is a helper implementating a mixin strategy that resembles classical inherintance (`class ... extends`): it simply calls the last method implementation.\n\n##### Example\n\n```javascript\nimport define, { override } from 'mixinable';\n\nconst create = define({\n  // mixin strategy function\n  bar: override,\n}, [\n  // mixin implementations\n  class {\n    bar() {\n      console.log(1);\n    }\n  },\n  class {\n    bar() {\n      console.log(2);\n    }\n  },\n]);\n\nconst foo = create();\n\nfoo.bar();\n// 2\n```\n\n`override` returns a `Promise` if one of its implementations does. If you want it to always return a `Promise`, i.e. if you can not be sure whether one of your implementations might return one, please use `define.async.override`.\n\n`override` is aliased to `callable` in all places, enabling implementers to communicate their intentions more clearly: `override` is often used to provide callable (utility) methods.\n\n#### `define.parallel`\n\n`parallel` is a helper implementating a mixin strategy that executes all defined implementations in parallel. This is probably most useful if asynchronous implementations are involved.\n\n##### Example\n\n```javascript\nimport define, { parallel } from 'mixinable';\n\nconst create = define({\n  // mixin strategy function\n  bar: parallel,\n}, [\n  // mixin implementations\n  class {\n    bar(val, inc) {\n      return val + inc;\n    }\n  },\n  class {\n    bar(val, inc) {\n      return val + inc;\n    }\n  },\n]);\n\nconst foo = create();\n\nfoo.bar(0, 1).then(res =\u003e console.log(res));\n// [1, 1]\n```\n\n`parallel` returns a `Promise` if one of its implementations does. If you want it to always return a `Promise`, i.e. if you can not be sure whether one of your implementations might return one, please use `define.async.parallel`.\n\nIf you want to make sure `parallel` never returns a `Promise`, please use it as `define.sync.parallel` - or, better yet, use it as `define.sync.sequence`, for that is technically correct.\n\n#### `define.pipe`\n\n`pipe` is a helper implementating a strategy that passes each implementation's output to the next, using the first argument as the initial value. All other arguments are being passed to all implementations as-is.\n\n##### Example\n\n```javascript\nimport define, { pipe } from 'mixinable';\n\nconst create = define({\n  // mixin strategy function\n  bar: pipe,\n}, [\n  // mixin implementations\n  class {\n    bar(val, inc) {\n      return val + inc;\n    }\n  },\n  class {\n    bar(val, inc) {\n      return val + inc;\n    }\n  },\n]);\n\nconst foo = create();\n\nfoo.bar(0, 1).then(res =\u003e console.log(res));\n// 2\n```\n\n`pipe` returns a `Promise` if one of its implementations does. If you want it to always return a `Promise`, i.e. if you can not be sure whether one of your implementations might return one, please use `define.async.pipe`.\n\n#### `define.compose`\n\n`compose` works essentially identically as `pipe`, but in reverse order: the very last implementation receives the initial value and the first implementation returns the final output.\n\n##### Example\n\n```javascript\nimport define, { compose } from 'mixinable';\n\nconst mixin = define({\n  // mixin strategy function\n  bar: compose,\n}, [\n  // mixin implementations\n  // ...\n]);\n```\n\n`compose` returns a `Promise` if one of its implementations does. If you want it to always return a `Promise`, i.e. if you can not be sure whether one of your implementations might return one, please use `define.async.compose`.\n\n#### Custom Strategies\n\nYou can supply your own mixin strategies: such strategies are plain functions that receive the defined implementations as their first argument. The following example shows a naïve re-implementation of the `override` strategy.\n\n##### Example\n\n```javascript\nimport define from 'mixinable';\n\nconst mixin = define({\n  // mixin strategy function\n  bar(functions, ...args) {\n    functions.pop().apply(null, args);\n  },\n}, [\n  // mixin implementation\n  class {\n    bar(arg) {\n      console.log(arg);\n    },\n  },\n]);\n\nconst foo = create();\n\nfoo.bar(1);\n// 1\n```\n\n#### Utilities\n\n##### `define.async`\n\nAll of the strategies described above return a `Promise` if one of their implementations does. If you want them to always return a `Promise` please use `define.async.{override/callable,parallel,pipe,compose}`.\n\n##### `define.sync`\n\nIf you want to make sure one of the strategies never returns a `Promise` please use `define.sync.{override/callable,parallel/sequence,pipe,compose}`. If you do, an `Error` will be thrown if a `Promise` is returned.\n\n### Contributing\n\nIf you want to contribute to this project, create a fork of its repository using the GitHub UI. Check out your new fork to your computer:\n\n```text\nmkdir mixinable \u0026\u0026 cd $_\ngit clone git@github.com:user/mixinable.git\n```\n\nAfterwards, you can `yarn install` the required dev dependencies and start hacking away. When you are finished, please do go ahead and create a pull request.\n\n`mixinable` itself is (almost) entirely written in ECMAScript 5 and adheres to semistandard code style. Please make sure your contribution does, too.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funtool%2Fmixinable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funtool%2Fmixinable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funtool%2Fmixinable/lists"}