{"id":16190581,"url":"https://github.com/linusborg/vue-mixable","last_synced_at":"2025-04-06T09:10:09.008Z","repository":{"id":62805701,"uuid":"560578698","full_name":"LinusBorg/vue-mixable","owner":"LinusBorg","description":"Turn Vue Mixins into Composables with a simple wrapper function","archived":false,"fork":false,"pushed_at":"2022-11-17T21:16:11.000Z","size":282,"stargazers_count":285,"open_issues_count":2,"forks_count":10,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-10-11T07:43:37.052Z","etag":null,"topics":["composables","mixins","vue"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/LinusBorg.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}},"created_at":"2022-11-01T20:01:58.000Z","updated_at":"2024-07-22T12:55:23.000Z","dependencies_parsed_at":"2023-01-22T16:01:18.436Z","dependency_job_id":null,"html_url":"https://github.com/LinusBorg/vue-mixable","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LinusBorg%2Fvue-mixable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LinusBorg%2Fvue-mixable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LinusBorg%2Fvue-mixable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LinusBorg%2Fvue-mixable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LinusBorg","download_url":"https://codeload.github.com/LinusBorg/vue-mixable/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247457803,"owners_count":20941906,"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":["composables","mixins","vue"],"created_at":"2024-10-10T07:43:37.376Z","updated_at":"2025-04-06T09:10:08.950Z","avatar_url":"https://github.com/LinusBorg.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![current npm version](https://img.shields.io/npm/v/vue-mixable) ![npm bundle size (min+gzip)](https://badgen.net/bundlephobia/minzip/vue-mixable) ![npm downloads per month](https://img.shields.io/npm/dm/vue-mixable)\n\n![NPM](https://img.shields.io/npm/l/vue-mixable) ![CI checks](https://badgen.net/github/checks/linusborg/vue-mixable) ![types included](https://badgen.net/npm/types/vue-mixable)\n\n# 🌪 `vue-mixable`\n\n\u003e Convert mixins into composables to reuse them in Composition API\n\n* helpful during Options API -\u003e Composition API migrations / in mixed code-bases\n* simple API - one function call is all you need\n* TS Support (with small caveats)\n* small footprint: ![npm bundle size (min+zip)](https://badgen.net/bundlephobia/minzip/vue-mixable)\n\n## Quick Intro\n\n```js\n// given an existing mixin such as this:\nexport const messageMixin  = {\n    data() {\n        return {\n            msg: 'Hello World'\n        }\n    },\n    computed: {\n        loudMsg() {\n            return this.capitalize(this.msg) + '!!!1eleven!'\n        }\n    },\n    methods: {\n        capitalize(value) { return value.toUpperCase() }\n    } \n}\n\n// we can create a composable from it with a single function call\nimport { createComposableFromMixin } from 'vue-mixable'\nexport const useMessage = createComposableFromMixin(messageMixin)\n```\n\nThis composable can then be used in  `setup()`/ `\u003cscript setup\u003e`:\n\n```html\n\u003cscript setup\u003e\nconst {\n    msg, // ref\n    loudMsg, // computed() ref\n    capitalize // function\n} = useMessage()\n\u003c/script\u003e\n```\n\n## Use cases\n\nThis library is primarily useful for developers trying to migrate a Options-API codebase using Mixins for code sharing to Composition API using composables for code sharing.\n\nOne of the challenges in such a migration is that one often cannot rewrite a mixin into a composable and replace all of that mixin's usage instances in the app at once, epsecially when mixins depend on one another, which is often the case in larger code-bases.\n\nThis is where `vue-mixable` can help: The team can keep all their mixins for the time of the migration, but convert each of them into composables with one line of code. You get have your cake, and eat it to, in a way. \n\nThen they can migrate individual components from the mixin to the composable at their own pace, and once the migration is done, they can rewrite the mixin into a proper standalone composable and finally remove the mixin from your codebase.\n\n\n## Installation\n\n```bash\nnpm install vue-mixable\n```\n\n## Usage Notes\n\n### Supported APIs\n\n`vue-mixable` provides full support for mixins that use the following Options APIs\n\n* `data`\n* `computed`\n* `methods`\n* `watch`\n* `provide`\n* `inject`\n* `props` (see Note in the next section)\n* `emits` (see Note in the next section)\n\nOptions with no direct support (currently):\n\n* `inheritAttrs`\n* `components`\n* `directives`\n* `name`\n\nIf you use any of the above options, you would have to set them manually in any component that uses the generated composable instead of the original mixin.\n\n### `props` \u0026 `emits`  options\n\nMixins can contain props definitions. Composables cannot, as they are functions invoked during component initialization (in `setup()`, at which point props must have been defined already.\n\n`vue-mixable` solves with in the following way:\n\n```js\nconst mixin = {\n    props: ['modelValue', 'age', 'street', 'city'],\n    emits: ['modelValue:update', 'certified']\n    // ...and other mixin content, i.e.:\n    data: () =\u003e ({\n        //...\n    })\n}\n\nexport const usePerson = createComposableFromMixin(mixin)\n// props and emits options will be available \n// as properties on the composable function(!)\nusePerson.props // =\u003e ['modelValue', 'age', 'street', 'city']\nusePerson.emits // =\u003e ['modelValue:update', 'certified']\n```\nUsage\n```js\nimport { usePerson } from '...'\n\nexport default defineComponent({\n    props: ['firstname', 'lastname', ...usePerson.props],\n    emits: usePerson.emits,\n    setup(props, { emit }) {\n        const person = usePerson()\n\n        return {\n\n        }\n    }\n})\n\n```\n### Shape of the composable's return value\n\nThe shape of the return value is essentially a flattened version of the mixins `data`, `computed` and `methods` properties, with `data` and `computed` being `ref()`'s. All other supported properties (lifecylces, `watch`) have nothing to expose externally.\n\n```js\nconst mixin = {\n    data: () =\u003e({\n        a: 'A',\n        b: 'B',\n    }),\n    computed: {\n        c() { return this.A },\n        d() { return this.B }\n    },\n    methods: {\n        e() {\n            return callSomething(this.a, this.c)\n        }\n    }\n}\n\nconst useComposable = createComposableFromMixin(mixin)\n```\nwould be turned into:\n```js\n\nconst {\n    a, // ref('A')\n    b, // ref('B')\n    c, // computed ref \n    d, // computed ref\n    e, // normal function\n} = useComposable()\n```\n\n### Feature Roadmap\n\n- [ ] Support Mixins that implicitly depend on properties/APIs from other mixins.\n- [ ] Support Nested Mixins.\n- [ ] Exclude specific properties from composables return value (essentially making some mixin properties private in the composable).\n\nOut of scope / not planned\n\n- [ ] mixins with implicit circular dependencies on one another.\n\n## Caveats\n\n### `this.$watch()` in created\n\ncreating a watcher imperatively in `created` will likely not work as expected, because in the created composable, that hooks is run before `setup()` returns, so any data properties declared in the mixin/composable will be missing on `this`.\n\nPossible workarounds:\n\n- use the normal `watch:`option\n- create the watcher in `beforeMount`.\n\n## Typescript Support\n\nTypescript support is still considered unstable as we plan on improving the types, possibly introduction breaking changes to the types.\n\n**Caveats:** \n\n* For Type inference to work, each mixin object *must* have a `props` key. If your mixin does not include any props, set it to an empty object.\n* props always need to be defined in object style. array style is currently not supported and will break type inference.\n* the `emits` option cannot be provided in its array form, it must take the more verbose object form.\n```ts\nconst mixin = defineMixin({\n    props: {} // needed for proper tyep inference for now,\n    emits: {\n        'update:modelValue': (v?: any) =\u003e true, // this validator can be a NOOP returning `true`\n    },\n    data: () =\u003e ({\n        // ...\n    })\n})\n\nconst composable = createCopmposableFromMixin(mixin)\n```\n\n### `defineMixin()`\n\nThis function does not do anything at runtime, it's just providing tpe inferrence for your mixins:\n\n```ts\nconst mixin = {\n    data: () =\u003e ({\n        msg: 'Hello World',\n    }),\n    methods: {\n        test() {\n            this.msg // not inferreed correctly\n        }\n    }\n}\n\n// better:\nimport { defineMixin } from 'vue-mixable'\nconst mixin = defineMixin({\n    props: {}, // needed, see caveat explained further up.\n    data: () =\u003e ({\n        msg: 'Hello World',\n    }),\n    methods: {\n        test() {\n            this.msg // properly inferred.\n        }\n    }\n}\n```\n\n### `createComposableFromMixin()`\n\nThis function will offer full type inference for any mixin passed to it.\n\n\n## Developer Instructions\n\n\n### Compile and Minify for Production, create Type Declarations\n\n```sh\npnpm build\n```\n\n### Run Unit Tests with [Vitest](https://vitest.dev/)\n\n```sh\npnpm test:unit\n```\n\n### Lint with [ESLint](https://eslint.org/)\n\n```sh\npnpm lint\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinusborg%2Fvue-mixable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flinusborg%2Fvue-mixable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinusborg%2Fvue-mixable/lists"}