├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ └── nodejs.yml ├── .gitignore ├── .npmignore ├── .prettierrc ├── .sgcrc ├── .travis-deprecated.yml ├── LICENSE ├── README.md ├── jest.config.json ├── package.json ├── release.config.js ├── rollup.config.js ├── src ├── component.ts ├── index.ts ├── mixins.ts └── tsconfig.json ├── test ├── decorator.spec.ts └── mediator.ts ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | ; EditorConfig file 2 | 3 | ; Tab space indentation 4 | [*] 5 | indent_style = tab 6 | insert_final_newline = true 7 | 8 | ; Tab space indentation 9 | [{package.json,*.yml}] 10 | indent_style = space 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | ignore: 13 | # Otherwise Node typings get out of sync 14 | - dependency-name: "@types/node" 15 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ dev, master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ dev ] 20 | schedule: 21 | - cron: '39 13 * * 4' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'javascript' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [10.x, 12.x, 14.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - name: Install Yarn 21 | run: | 22 | npm i -g yarn 23 | - name: yarn install, build, and test 24 | run: | 25 | yarn install 26 | yarn build 27 | yarn test 28 | env: 29 | CI: true 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### macOS template 2 | # General 3 | .DS_Store 4 | .AppleDouble 5 | .LSOverride 6 | 7 | # Icon must end with two \r 8 | Icon 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear in the root of a volume 14 | .DocumentRevisions-V100 15 | .fseventsd 16 | .Spotlight-V100 17 | .TemporaryItems 18 | .Trashes 19 | .VolumeIcon.icns 20 | .com.apple.timemachine.donotpresent 21 | 22 | # Directories potentially created on remote AFP share 23 | .AppleDB 24 | .AppleDesktop 25 | Network Trash Folder 26 | Temporary Items 27 | .apdisk 28 | 29 | ### Node template 30 | # Logs 31 | logs 32 | *.log 33 | npm-debug.log* 34 | yarn-debug.log* 35 | yarn-error.log* 36 | 37 | # Runtime data 38 | pids 39 | *.pid 40 | *.seed 41 | *.pid.lock 42 | 43 | # Directory for instrumented libs generated by jscoverage/JSCover 44 | lib-cov 45 | 46 | # Coverage directory used by tools like istanbul 47 | coverage 48 | 49 | # nyc test coverage 50 | .nyc_output 51 | 52 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 53 | .grunt 54 | 55 | # Bower dependency directory (https://bower.io/) 56 | bower_components 57 | 58 | # node-waf configuration 59 | .lock-wscript 60 | 61 | # Compiled binary addons (http://nodejs.org/api/addons.html) 62 | build/Release 63 | 64 | # Dependency directories 65 | node_modules/ 66 | jspm_packages/ 67 | 68 | # Typescript v1 declaration files 69 | typings/ 70 | 71 | # Optional npm cache directory 72 | .npm 73 | 74 | # Optional eslint cache 75 | .eslintcache 76 | 77 | # Optional REPL history 78 | .node_repl_history 79 | 80 | # Output of 'npm pack' 81 | *.tgz 82 | 83 | # Yarn Integrity file 84 | .yarn-integrity 85 | 86 | # dotenv environment variables file 87 | .env 88 | 89 | # IntelliJ 90 | .idea/ 91 | 92 | lib 93 | src/*.js 94 | *.map 95 | test/*.spec.js 96 | dist 97 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### macOS template 3 | # General 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | # Thumbnails 12 | ._* 13 | 14 | # Files that might appear in the root of a volume 15 | .DocumentRevisions-V100 16 | .fseventsd 17 | .Spotlight-V100 18 | .TemporaryItems 19 | .Trashes 20 | .VolumeIcon.icns 21 | .com.apple.timemachine.donotpresent 22 | 23 | # Directories potentially created on remote AFP share 24 | .AppleDB 25 | .AppleDesktop 26 | Network Trash Folder 27 | Temporary Items 28 | .apdisk 29 | 30 | ### Node template 31 | # Logs 32 | logs 33 | *.log 34 | npm-debug.log* 35 | yarn-debug.log* 36 | yarn-error.log* 37 | 38 | # Runtime data 39 | pids 40 | *.pid 41 | *.seed 42 | *.pid.lock 43 | 44 | # Directory for instrumented libs generated by jscoverage/JSCover 45 | lib-cov 46 | 47 | # Coverage directory used by tools like istanbul 48 | coverage 49 | 50 | # nyc test coverage 51 | .nyc_output 52 | 53 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 54 | .grunt 55 | 56 | # Bower dependency directory (https://bower.io/) 57 | bower_components 58 | 59 | # node-waf configuration 60 | .lock-wscript 61 | 62 | # Compiled binary addons (https://nodejs.org/api/addons.html) 63 | build/Release 64 | 65 | # Dependency directories 66 | node_modules/ 67 | jspm_packages/ 68 | 69 | # Typescript v1 declaration files 70 | typings/ 71 | 72 | # Optional npm cache directory 73 | .npm 74 | 75 | # Optional eslint cache 76 | .eslintcache 77 | 78 | # Optional REPL history 79 | .node_repl_history 80 | 81 | # Output of 'npm pack' 82 | *.tgz 83 | 84 | # Yarn Integrity file 85 | .yarn-integrity 86 | 87 | # dotenv environment variables file 88 | .env 89 | 90 | # next.js build output 91 | .next 92 | ### GPG template 93 | secring.* 94 | 95 | ### JetBrains template 96 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 97 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 98 | 99 | # User-specific stuff: 100 | .idea/**/workspace.xml 101 | .idea/**/tasks.xml 102 | .idea/dictionaries 103 | 104 | # Sensitive or high-churn files: 105 | .idea/**/dataSources/ 106 | .idea/**/dataSources.ids 107 | .idea/**/dataSources.xml 108 | .idea/**/dataSources.local.xml 109 | .idea/**/sqlDataSources.xml 110 | .idea/**/dynamic.xml 111 | .idea/**/uiDesigner.xml 112 | 113 | # Gradle: 114 | .idea/**/gradle.xml 115 | .idea/**/libraries 116 | 117 | # CMake 118 | cmake-build-debug/ 119 | cmake-build-release/ 120 | 121 | # Mongo Explorer plugin: 122 | .idea/**/mongoSettings.xml 123 | 124 | ## File-based project format: 125 | *.iws 126 | 127 | ## Plugin-specific files: 128 | 129 | # IntelliJ 130 | out/ 131 | 132 | # mpeltonen/sbt-idea plugin 133 | .idea_modules/ 134 | 135 | # JIRA plugin 136 | atlassian-ide-plugin.xml 137 | 138 | # Cursive Clojure plugin 139 | .idea/replstate.xml 140 | 141 | # Crashlytics plugin (for Android Studio and IntelliJ) 142 | com_crashlytics_export_strings.xml 143 | crashlytics.properties 144 | crashlytics-build.properties 145 | fabric.properties 146 | 147 | ### VisualStudioCode template 148 | .vscode/* 149 | !.vscode/settings.json 150 | !.vscode/tasks.json 151 | !.vscode/launch.json 152 | !.vscode/extensions.json 153 | 154 | ### Project 155 | !dist 156 | !lib 157 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "singleQuote": true, 4 | "useTabs": true, 5 | "overrides": [ 6 | { 7 | "files": [".prettierrc", ".sgcrc"], 8 | "options": { 9 | "parser": "json" 10 | } 11 | }, 12 | { 13 | "files": "package.json", 14 | "options": { 15 | "useTabs": false 16 | } 17 | }, 18 | { 19 | "files": "**/*.yml", 20 | "options": { 21 | "useTabs": false 22 | } 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /.sgcrc: -------------------------------------------------------------------------------- 1 | { 2 | "emoji": true 3 | } 4 | -------------------------------------------------------------------------------- /.travis-deprecated.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - ~/.npm 5 | notifications: 6 | email: false 7 | node_js: 8 | - '8' 9 | - '10' 10 | - '12' 11 | before_install: 12 | - npm install -g npm 13 | - npm install -g greenkeeper-lockfile@1 14 | before_script: greenkeeper-lockfile-update 15 | after_script: greenkeeper-lockfile-upload 16 | after_success: 17 | - npm run travis-deploy-once "npm run build && npm run semantic-release" 18 | branches: 19 | except: 20 | - /^v\d+\.\d+\.\d+$/ 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 kaorun343 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue Mixin Decorator 2 | 3 | 4 | [![Build Status](https://travis-ci.org/justrhysism/vue-mixin-decorator.svg?branch=master)](https://travis-ci.org/justrhysism/vue-mixin-decorator) 5 | [![npm](https://img.shields.io/npm/v/vue-mixin-decorator.svg)](https://www.npmjs.com/package/vue-mixin-decorator) 6 | [![Greenkeeper badge](https://badges.greenkeeper.io/justrhysism/vue-mixin-decorator.svg)](https://greenkeeper.io/) 7 | [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) 8 | 9 | 10 | > PLEASE NOTE: As Vue3 has moved away from class components, this project is archived and will not be recieving further updates. 11 | 12 | This library fully depends on [vue-class-component](https://github.com/vuejs/vue-class-component). 13 | 14 | Most ideas and code are ~~stolen~~ _borrowed_ from [@HerringtonDarkholme](https://github.com/HerringtonDarkholme) 15 | and his **[av-ts](https://github.com/HerringtonDarkholme/av-ts)** project. Also from 16 | [@JsonSong89](https://github.com/JsonSong89)'s 17 | [comment](https://github.com/vuejs/vue-class-component/issues/91#issuecomment-312534798), who suggested that the idea 18 | should be extracted into a separate project _which is why I've begrudgingly done so_. 19 | 20 | Project template shamelessly stolen from **[vue-property-decorator](https://github.com/kaorun343/vue-property-decorator)**. 21 | 22 | **Best case scenario** is this project/implementation/concept 23 | gets merged/provided into/by an officially supported project 24 | and this one can be deprecated. 25 | 26 | ## License 27 | 28 | MIT License 29 | 30 | ## Install 31 | 32 | ```bash 33 | npm install --save-dev vue-mixin-decorator 34 | ``` 35 | 36 | ## Usage 37 | 38 | There are 2 decorators: 39 | 40 | * `@Mixin` 41 | * `@Component` 42 | 43 | and an extension class: 44 | 45 | * `Mixins` 46 | 47 | _Note: `@Mixin` is `@Component` exported from `vue-class-component`._ 48 | 49 | ### Single Mixin 50 | 51 | ```typescript 52 | import Vue from 'vue'; 53 | import { Component, Mixin, Mixins } from 'vue-mixin-decorator'; 54 | 55 | @Mixin 56 | class MyMixin extends Vue { 57 | created() { 58 | console.log('Mixin created()'); 59 | } 60 | 61 | mixinMethod() { 62 | console.log('Mixin method called.'); 63 | } 64 | } 65 | 66 | @Component 67 | class MyComponent extends Mixins(MyMixin) { 68 | created() { 69 | this.mixinMethod(); 70 | } 71 | } 72 | ``` 73 | 74 | ### Multiple Mixins 75 | ```typescript 76 | import Vue from 'vue'; 77 | import { Component, Mixin, Mixins } from 'vue-mixin-decorator'; 78 | 79 | @Mixin 80 | class MyMixin extends Vue { 81 | created() { 82 | console.log('Mixin created()'); 83 | } 84 | 85 | mixinMethod() { 86 | console.log('Mixin method called.'); 87 | } 88 | } 89 | 90 | @Mixin 91 | class MyOtherMixin extends Vue { 92 | created() { 93 | console.log('Other mixin created()'); 94 | } 95 | 96 | otherMixinMethod() { 97 | console.log('Other mixin method called.'); 98 | } 99 | } 100 | 101 | // Create an interface extending the mixins to provide 102 | interface IMixinInterface extends MyMixin, MyOtherMixin {} 103 | 104 | @Component 105 | class MyComponent extends Mixins(MyMixin, MyOtherMixin) { 106 | created() { 107 | this.mixinMethod(); 108 | this.otherMixinMethod(); 109 | } 110 | } 111 | ``` 112 | 113 | ## See also 114 | 115 | * [vue-property-decorator](https://github.com/kaorun343/vue-property-decorator) 116 | * [vuex-class](https://github.com/ktsn/vuex-class/) 117 | -------------------------------------------------------------------------------- /jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "transform": { 3 | "^.+\\.tsx?$": "ts-jest", 4 | "^.+\\.jsx?$": "babel-jest" 5 | }, 6 | "transformIgnorePatterns": ["/node_modules/(?!lodash-es)"], 7 | "testRegex": "(/test/.*(spec))\\.(jsx?|tsx?)$", 8 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node", "vue"], 9 | "moduleDirectories": ["node_modules"], 10 | "moduleNameMapper": { 11 | "^@/(.*)$": "./src/$1" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-mixin-decorator", 3 | "version": "0.0.0-development", 4 | "description": "Mixin decorators for Vue Component", 5 | "main": "dist/vue-mixin-decorator.umd.js", 6 | "typings": "./lib/index.d.ts", 7 | "keywords": [ 8 | "vue", 9 | "typescript", 10 | "decorator" 11 | ], 12 | "author": "justrhysism", 13 | "license": "MIT", 14 | "directories": { 15 | "test": "test" 16 | }, 17 | "scripts": { 18 | "build": "run-s build:*", 19 | "build:ts": "tsc -p src/tsconfig.json", 20 | "build:umd": "rollup -c", 21 | "test": "jest --config=jest.config.json --notify --coverage", 22 | "commit": "sgc", 23 | "semantic-release": "semantic-release -e ./release.config.js", 24 | "travis-deploy-once": "travis-deploy-once", 25 | "clean-dist": "del dist && del lib", 26 | "check-git": "git diff-index --quiet HEAD --", 27 | "update-dev": "git checkout dev && git pull", 28 | "merge-to-master": "git checkout master && git pull && git merge dev", 29 | "master-to-dev": "git checkout dev && git merge master", 30 | "prerelease": "yarn run check-git && yarn run clean-dist && yarn run update-dev && yarn test && yarn run build", 31 | "release": "yarn run merge-to-master && git push", 32 | "postrelease": "yarn run master-to-dev && git push" 33 | }, 34 | "babel": { 35 | "presets": [] 36 | }, 37 | "files": [ 38 | "lib", 39 | "dist" 40 | ], 41 | "devDependencies": { 42 | "@semantic-release/commit-analyzer": "^6.1.0", 43 | "@semantic-release/github": "^5.4.2", 44 | "@semantic-release/npm": "^5.1.13", 45 | "@semantic-release/release-notes-generator": "^7.2.1", 46 | "@types/jest": "^24.0.0", 47 | "@types/node": "^10.5.0", 48 | "@vue/test-utils": "^1.0.0-beta.28", 49 | "browser-env": "^3.2.4", 50 | "conventional-changelog-eslint": "^3.0.0", 51 | "del-cli": "^3.0.0", 52 | "jest": "^24.1.0", 53 | "npm-run-all": "^4.1.2", 54 | "prettier": "^1.18.2", 55 | "require-extension-hooks": "^0.3.2", 56 | "require-extension-hooks-babel": "^1.0.0", 57 | "rollup": "^1.17.0", 58 | "rollup-plugin-commonjs": "^10.0.1", 59 | "rollup-plugin-node-resolve": "^5.2.0", 60 | "rollup-plugin-typescript": "^1.0.1", 61 | "semantic-git-commit-cli": "^3.0.2", 62 | "semantic-release": "^15.13.18", 63 | "travis-deploy-once": "^5.0.0", 64 | "ts-jest": "^24.0.0", 65 | "tslib": "^2.0.1", 66 | "typescript": "^4.0.2", 67 | "vue": "^2.5.13", 68 | "vue-template-compiler": "^2.5.16" 69 | }, 70 | "dependencies": { 71 | "vue-class-component": "^7.1.0" 72 | }, 73 | "peerDependencies": { 74 | "typescript": ">= 2 < 4", 75 | "vue": "^2.5.0" 76 | }, 77 | "repository": "git+https://github.com/justrhysism/vue-mixin-decorator.git", 78 | "bugs": { 79 | "url": "https://github.com/justrhysism/vue-mixin-decorator/issues" 80 | }, 81 | "homepage": "https://github.com/justrhysism/vue-mixin-decorator#readme", 82 | "private": false 83 | } 84 | -------------------------------------------------------------------------------- /release.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-useless-escape */ 2 | module.exports = { 3 | plugins: [ 4 | [ 5 | '@semantic-release/commit-analyzer', 6 | { 7 | preset: 'eslint', 8 | parserOpts: { 9 | // optional, only you want to have emoji commit support 10 | headerPattern: /^(?::([\w-]*):)?\s*(\w*):\s*(.*)$/, 11 | headerCorrespondence: ['emoji', 'tag', 'message'], 12 | }, 13 | releaseRules: [ 14 | { tag: 'Breaking', release: 'major' }, 15 | { tag: 'Fix', release: 'patch' }, 16 | { tag: 'Feat', release: 'minor' }, 17 | { tag: 'New', release: 'minor' }, 18 | { tag: 'Perf', release: 'patch' }, 19 | { tag: 'Update', release: 'minor' }, 20 | ] 21 | }, 22 | ], 23 | [ 24 | '@semantic-release/release-notes-generator', 25 | { 26 | preset: 'eslint', 27 | parserOpts: { 28 | // optional, only you want to have emoji commit support 29 | headerPattern: /^(?::([\w-]*):)?\s*(\w*):\s*(.*)$/, 30 | headerCorrespondence: ['emoji', 'tag', 'message'], 31 | } 32 | }, 33 | ], 34 | '@semantic-release/npm', 35 | '@semantic-release/github' 36 | ], 37 | }; 38 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | input: 'lib/index.js', 3 | output: { 4 | name: 'vue-mixin-decorator', 5 | file: 'dist/vue-mixin-decorator.umd.js', 6 | format: 'umd', 7 | globals: { 8 | vue: 'Vue', 9 | 'vue-class-component': 'VueClassComponent', 10 | }, 11 | exports: 'named', 12 | }, 13 | external: ['vue', 'vue-class-component'], 14 | }; 15 | -------------------------------------------------------------------------------- /src/component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Component Decorator 3 | * @see https://github.com/vuejs/vue-class-component/issues/91#issuecomment-312534798 4 | */ 5 | 6 | // Third Party Dependencies 7 | import Vue, { ComponentOptions } from 'vue'; 8 | import VCComponent from 'vue-class-component'; 9 | 10 | /** 11 | * Component decorator 12 | * @param {ComponentOptions | V} options 13 | * @returns {any} 14 | * @constructor 15 | */ 16 | export function Component( 17 | options: ComponentOptions | V, 18 | ): any { 19 | return VCComponent(options); 20 | } 21 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Vue Mixin Decorator 3 | */ 4 | 5 | // Third Party Dependencies 6 | import VCComponent from 'vue-class-component'; 7 | 8 | // Exports 9 | export { VCComponent as Mixin }; 10 | export { Mixins } from './mixins'; 11 | export { Component } from './component'; 12 | -------------------------------------------------------------------------------- /src/mixins.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Mixins Vue Extension 3 | * @see https://github.com/vuejs/vue-class-component/issues/91#issuecomment-312534798 4 | */ 5 | 6 | // Third Party Dependencies 7 | import Vue from 'vue'; 8 | import { VueClass } from 'vue-class-component/lib/declarations'; 9 | 10 | /** 11 | * Extend a Vue Component with mixins 12 | * @param {VueClass} parent 13 | * @returns {VueClass} 14 | * @constructor 15 | */ 16 | export function Mixins(parent: VueClass): VueClass; 17 | 18 | /** 19 | * Extend a Vue Component with mixins 20 | * @param {VueClass} parent 21 | * @param {VueClass} mixins 22 | * @returns {VueClass} 23 | * @constructor 24 | */ 25 | export function Mixins( 26 | parent: VueClass, 27 | ...mixins: VueClass[] 28 | ): VueClass; 29 | 30 | /** 31 | * 32 | * @param {typeof Vue} parent 33 | * @param {typeof Vue} mixins 34 | * @returns {VueClass} 35 | * @constructor 36 | */ 37 | export function Mixins( 38 | parent: typeof Vue, 39 | ...mixins: (typeof Vue)[] 40 | ): VueClass { 41 | return parent.extend({ mixins }) as any; 42 | } 43 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "../lib", 4 | "target": "es5", 5 | "module": "es2015", 6 | "moduleResolution": "node", 7 | "isolatedModules": false, 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true, 10 | "declaration": true, 11 | "noImplicitAny": true, 12 | "removeComments": false, 13 | "strictNullChecks": true, 14 | "typeRoots": ["../node_modules/@types/"], 15 | "allowSyntheticDefaultImports": true, 16 | "lib": ["dom", "es2015", "es2016", "es2017"] 17 | }, 18 | "include": ["./*.ts"], 19 | "exclude": ["node_modules", "lib"], 20 | "compileOnSave": false 21 | } 22 | -------------------------------------------------------------------------------- /test/decorator.spec.ts: -------------------------------------------------------------------------------- 1 | import 'jest'; 2 | import { mount } from '@vue/test-utils'; 3 | 4 | import { Vue, Component, Mixin, Mixins } from './mediator'; 5 | 6 | const componentOptions = { 7 | template: '
' 8 | }; 9 | 10 | describe('@Component', () => { 11 | it('should call `created()` and `destroyed()` lifecycle methods', () => { 12 | // Arrange 13 | const first = Symbol('first'); 14 | const second = Symbol('second'); 15 | const third = Symbol('third'); 16 | const spy = jest.fn(); 17 | 18 | @Component(componentOptions) 19 | class MyComp extends Vue { 20 | 21 | created() { 22 | spy(first); 23 | } 24 | 25 | destroyed() { 26 | spy(third); 27 | } 28 | } 29 | 30 | // Act 31 | const wrapper = mount(MyComp); 32 | spy(second); 33 | wrapper.destroy(); 34 | 35 | // Assert 36 | expect(wrapper.isVueInstance()).toBe(true); 37 | expect(spy).toHaveBeenNthCalledWith(1, first); 38 | expect(spy).toHaveBeenNthCalledWith(2, second); 39 | expect(spy).toHaveBeenNthCalledWith(3, third); 40 | }); 41 | }); 42 | 43 | describe('@Mixin', () => { 44 | test('decorator test', () => { 45 | // Arrange 46 | @Mixin(componentOptions) 47 | class MyMixin extends Vue {} 48 | 49 | // Act 50 | const wrapper = mount(MyMixin); 51 | 52 | // Assert 53 | expect(wrapper.isVueInstance()).toBe(true); 54 | }); 55 | 56 | it('should bind a single mixin', () => { 57 | // Arrange 58 | const mixinOneCreated = jest.fn(); 59 | const mixinOneCalledFromComponent = jest.fn(); 60 | 61 | @Mixin 62 | class MyMixinOne extends Vue { 63 | created() { 64 | mixinOneCreated(); 65 | } 66 | 67 | mixinOneMethod() { 68 | mixinOneCalledFromComponent(); 69 | } 70 | } 71 | 72 | @Component(componentOptions) 73 | class MyComponent extends Mixins(MyMixinOne) { 74 | created() { 75 | this.mixinOneMethod(); 76 | } 77 | } 78 | 79 | // Act 80 | const wrapper = mount(MyComponent); 81 | 82 | // Assert 83 | expect(wrapper.isVueInstance()).toBe(true); 84 | expect(mixinOneCreated).toBeCalledTimes(1); 85 | expect(mixinOneCalledFromComponent).toBeCalledTimes(1); 86 | }); 87 | 88 | it('should bind multiple mixins', () => { 89 | // Arrange 90 | const mixinOneCreated = jest.fn(); 91 | const mixinTwoCreated = jest.fn(); 92 | const mixinOneCalledFromComponent = jest.fn(); 93 | const mixinTwoCalledFromComponent = jest.fn(); 94 | 95 | @Mixin 96 | class MyMixinOne extends Vue { 97 | created() { 98 | mixinOneCreated(); 99 | } 100 | 101 | mixinOneMethod() { 102 | mixinOneCalledFromComponent(); 103 | } 104 | } 105 | 106 | @Mixin 107 | class MyMixinTwo extends Vue { 108 | created() { 109 | mixinTwoCreated(); 110 | } 111 | 112 | mixinTwoMethod() { 113 | mixinTwoCalledFromComponent(); 114 | } 115 | } 116 | 117 | /* 118 | To handle multiple mixins (complete with Typescript completion) 119 | an interface needs to be created extending your mixins. 120 | */ 121 | interface IMixinInterface extends MyMixinOne, MyMixinTwo {} 122 | 123 | /* 124 | Provide `Mixins` with the interface 125 | */ 126 | @Component(componentOptions) 127 | class MyComponent extends Mixins(MyMixinOne, MyMixinTwo) { 128 | created() { 129 | this.mixinOneMethod(); 130 | this.mixinTwoMethod(); 131 | } 132 | } 133 | 134 | // Act 135 | const wrapper = mount(MyComponent); 136 | 137 | // Assert 138 | expect(wrapper.isVueInstance()).toBe(true); 139 | expect(mixinOneCreated).toBeCalledTimes(1); 140 | expect(mixinTwoCreated).toBeCalledTimes(1); 141 | expect(mixinOneCalledFromComponent).toBeCalledTimes(1); 142 | expect(mixinTwoCalledFromComponent).toBeCalledTimes(1); 143 | }); 144 | }); 145 | -------------------------------------------------------------------------------- /test/mediator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Mediator is required to prevent circular dependencies in the test file: 3 | * https://github.com/Microsoft/TypeScript/issues/11576#issuecomment-367641255 4 | */ 5 | 6 | import Vue from 'vue'; 7 | export { Vue }; 8 | export { Component, Mixin, Mixins } from '../src'; 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es2015", 4 | "target": "es2015", 5 | "sourceMap": true, 6 | "moduleResolution": "node", 7 | // "isolatedModules": false, 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true, 10 | "noImplicitAny": true, 11 | "removeComments": false, 12 | "strictNullChecks": true, 13 | // "typeRoots": ["../node_modules/@types/"], 14 | "allowSyntheticDefaultImports": true, 15 | "esModuleInterop": true, 16 | "lib": ["dom", "es2015", "es2016", "es2017"] 17 | }, 18 | "compileOnSave": false 19 | } 20 | --------------------------------------------------------------------------------