├── .babelrc ├── .circleci └── config.yml ├── .coveralls.yml ├── .docker ├── node │ ├── Dockerfile │ ├── node_modules.sh │ ├── readme.md │ └── startup.sh └── puppeteer │ └── Dockerfile ├── .dockerignore ├── .env.example ├── .eslintrc ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── LICENSE ├── README.md ├── build ├── build.js ├── configs.js ├── release.sh └── webpack.config.js ├── dist ├── vue-injector.common.js ├── vue-injector.esm.js ├── vue-injector.js └── vue-injector.min.js ├── docker-compose.yml ├── docs ├── .vuepress │ ├── config.js │ └── public │ │ ├── _redirects │ │ ├── icons │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── apple-icon-precomposed.png │ │ ├── apple-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── ms-icon-144x144.png │ │ ├── ms-icon-150x150.png │ │ ├── ms-icon-310x310.png │ │ └── ms-icon-70x70.png │ │ ├── logo.png │ │ └── manifest.json ├── README.md ├── api │ └── README.md ├── guide │ ├── README.md │ └── essentials │ │ ├── get-service.md │ │ ├── nuxt.md │ │ ├── reg-service.md │ │ ├── vue.md │ │ └── vuex.md ├── installation.md └── ru │ ├── README.md │ ├── api │ └── README.md │ ├── guide │ ├── README.md │ └── essentials │ │ ├── get-service.md │ │ ├── nuxt.md │ │ ├── reg-service.md │ │ ├── vue.md │ │ └── vuex.md │ └── installation.md ├── examples ├── basic │ ├── app.js │ └── index.html ├── demo.mixin.js ├── demo.setup.js ├── demo.setup.vuex.js ├── demo.styles.css ├── factory │ ├── app.js │ └── index.html ├── get_service │ ├── app.js │ └── index.html ├── index.html ├── provide_error │ ├── app.js │ └── index.html ├── root │ ├── app.js │ └── index.html ├── server.js ├── service_decorator │ ├── app.js │ └── index.html ├── typescript │ ├── UserComponent.vue │ ├── app.ts │ └── index.html ├── value │ ├── app.js │ └── index.html ├── vue │ ├── app.js │ └── index.html ├── vuex │ ├── app.js │ └── index.html └── webpack.config.js ├── package.json ├── src ├── di │ ├── bindings │ │ └── binding.ts │ ├── decorators │ │ ├── inject.ts │ │ └── injectable.ts │ ├── factory │ │ ├── Factory.ts │ │ ├── Instance.ts │ │ ├── UseFactory.ts │ │ └── UseValue.ts │ ├── injector.ts │ └── provider.ts ├── enums │ ├── messages.ts │ └── metadata.ts ├── index.ts ├── install.ts ├── polyfill.ts └── util │ ├── decorator.ts │ ├── dom.ts │ ├── object.ts │ └── warn.ts ├── test └── unit │ └── specs │ ├── register-component.spec.ts │ ├── register-service.spec.ts │ └── warm.spec.ts ├── tsconfig.json ├── types ├── global.d.ts ├── vue.d.ts ├── vue.options.d.ts └── vue.options.ts └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env" 5 | ] 6 | ], 7 | "plugins": [ 8 | "@babel/plugin-syntax-dynamic-import", 9 | [ 10 | "@babel/plugin-proposal-decorators", 11 | { 12 | "legacy": true 13 | } 14 | ], 15 | "@babel/plugin-proposal-class-properties" 16 | ], 17 | "env": { 18 | "test": { 19 | "plugins": [ 20 | "istanbul" 21 | ] 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | defaults: &defaults 4 | working_directory: ~/project/vue-injector 5 | docker: 6 | - image: circleci/node:10-browsers 7 | 8 | jobs: 9 | install: 10 | <<: *defaults 11 | steps: 12 | - checkout 13 | - restore_cache: 14 | keys: 15 | - v1-vue-{{ .Branch }}-{{ checksum "yarn.lock" }} 16 | - v1-vue-{{ .Branch }}- 17 | - v1-vue- 18 | - run: yarn install 19 | - save_cache: 20 | key: v1-vue-{{ .Branch }}-{{ checksum "yarn.lock" }} 21 | paths: 22 | - node_modules/ 23 | - persist_to_workspace: 24 | root: ~/project 25 | paths: 26 | - vue-injector 27 | 28 | lint-types: 29 | <<: *defaults 30 | steps: 31 | - attach_workspace: 32 | at: ~/project 33 | - run: yarn lint 34 | - run: yarn test:types 35 | 36 | test-unit: 37 | <<: *defaults 38 | steps: 39 | - attach_workspace: 40 | at: ~/project 41 | - run: yarn test:unit 42 | - run: 43 | name: report coverage 44 | command: | 45 | if [[ -z $CI_PULL_REQUEST ]]; then 46 | yarn test:cover 47 | fi 48 | 49 | deploy: 50 | <<: *defaults 51 | steps: 52 | - attach_workspace: 53 | at: ~/project 54 | - run: 55 | name: Authenticate with registry 56 | command: echo "//registry.npmjs.org/:_authToken=$npm_TOKEN" > ~/project/vue-injector/.npmrc 57 | - run: 58 | name: Add types 59 | command: yarn types 60 | - run: 61 | name: Publish package 62 | command: | 63 | if [ "$CIRCLE_BRANCH" = "master" ]; then 64 | npm publish --tag latest || exit 0 65 | fi 66 | if [ "$CIRCLE_BRANCH" = "next" ]; then 67 | npm publish --tag next || exit 0 68 | fi 69 | 70 | workflows: 71 | version: 2 72 | install-and-parallel-test: 73 | jobs: 74 | - install 75 | - test-unit: 76 | requires: 77 | - install 78 | - lint-types: 79 | requires: 80 | - install 81 | - deploy: 82 | requires: 83 | - test-unit 84 | - lint-types 85 | filters: 86 | branches: 87 | only: master 88 | tags: 89 | only: /^v.*/ 90 | -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: fOAW4ILXcuEGYgRcLaJ0LvYE73Wo9MILD 2 | -------------------------------------------------------------------------------- /.docker/node/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | 3 | RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ 4 | && apt-get update \ 5 | && apt-get install -y apt-transport-https \ 6 | && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ 7 | && apt-get update \ 8 | && apt-get install -y yarn git make curl zip \ 9 | && mkdir -p /app 10 | 11 | WORKDIR /app 12 | 13 | COPY package.json yarn.lock ./ 14 | 15 | RUN yarn install 16 | COPY . . 17 | 18 | EXPOSE 8080 19 | 20 | COPY .docker/node/startup.sh / 21 | RUN chmod +x /startup.sh 22 | 23 | ENTRYPOINT ["/startup.sh"] 24 | -------------------------------------------------------------------------------- /.docker/node/node_modules.sh: -------------------------------------------------------------------------------- 1 | ############################################################# 2 | ## Error: A required privilege is not held by the client ## 3 | ## ## 4 | ## It work for me after open a new cmd with ## 5 | ## "Run as administrator" and then running ## 6 | ############################################################# 7 | 8 | docker cp -L ed2c8311b0ed851551b69f059da809067c94dd4c9f2c3a4bfd75e0b83a5f01aa:/app/node_modules . -------------------------------------------------------------------------------- /.docker/node/readme.md: -------------------------------------------------------------------------------- 1 | # Node base service image 2 | 3 | - node 8.11.1 4 | - npm 5 | - yarn 6 | -------------------------------------------------------------------------------- /.docker/node/startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cp /root/.ssh/tmp /root/.ssh/id_rsa 4 | chmod 400 /root/.ssh/id_rsa 5 | 6 | git config --global user.name "$GIT_USERNAME" 7 | git config --global user.email "$GIT_EMAIL" 8 | 9 | git config --global core.autocrlf false 10 | git config --global core.eol lf 11 | 12 | $@ 13 | -------------------------------------------------------------------------------- /.docker/puppeteer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10-slim 2 | 3 | # Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others) 4 | # Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer 5 | # installs, work. 6 | RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ 7 | && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \ 8 | && apt-get update \ 9 | && apt-get install -y google-chrome-unstable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf \ 10 | --no-install-recommends \ 11 | && rm -rf /var/lib/apt/lists/* 12 | 13 | # If running Docker >= 1.13.0 use docker run's --init arg to reap zombie processes, otherwise 14 | # uncomment the following lines to have `dumb-init` as PID 1 15 | # ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 /usr/local/bin/dumb-init 16 | # RUN chmod +x /usr/local/bin/dumb-init 17 | # ENTRYPOINT ["dumb-init", "--"] 18 | 19 | # Uncomment to skip the chromium download when installing puppeteer. If you do, 20 | # you'll need to launch puppeteer with: 21 | # browser.launch({executablePath: 'google-chrome-unstable'}) 22 | # ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true 23 | 24 | # Install puppeteer so it's available in the container. 25 | RUN npm i puppeteer \ 26 | # Add user so we don't need --no-sandbox. 27 | # same layer as npm install to keep re-chowned files from using up several hundred MBs more space 28 | && groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \ 29 | && mkdir -p /home/pptruser/Downloads \ 30 | && chown -R pptruser:pptruser /home/pptruser \ 31 | && chown -R pptruser:pptruser /node_modules 32 | 33 | # Run everything after as non-privileged user. 34 | USER pptruser 35 | 36 | CMD ["google-chrome-unstable"] -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules 3 | npm-debug -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | PUBLIC_PORT=8080 2 | 3 | GIT_USERNAME= 4 | GIT_EMAIL= 5 | GIT_SSH_KEY= -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb-base"], 3 | "plugins": ["@typescript-eslint", "jest"], 4 | "parser": "@typescript-eslint/parser", 5 | "rules": { 6 | "comma-dangle": ["error", "never"], 7 | "lines-between-class-members" : 0, 8 | "no-new": 0, 9 | "no-underscore-dangle": 0, 10 | "import/prefer-default-export": 0, 11 | "class-methods-use-this": 0, 12 | "import/no-extraneous-dependencies": 0, 13 | "no-param-reassign": 0, 14 | "import/extensions": 0 15 | }, 16 | "overrides": [ 17 | { 18 | "files": ["*.ts"], 19 | "rules": { 20 | "no-useless-constructor": "off", 21 | "@typescript-eslint/no-unused-vars": [2, { "args": "none" }] 22 | } 23 | } 24 | ], 25 | "env": { 26 | "jest/globals": true, 27 | "browser": true 28 | }, 29 | "settings": { 30 | "import/core-modules": [ "@scandltd/vue-injector", "vue", "vuex" ], 31 | "import/resolver": { 32 | "node": { 33 | "extensions": [".js", ".ts"] 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .env 3 | .vscode 4 | node_modules 5 | coverage 6 | test/e2e/reports 7 | test/e2e/screenshots 8 | selenium-debug.log 9 | dist/*.gz 10 | dist/*.map 11 | explorations 12 | docs-gitbook 13 | docs/.vuepress/dist 14 | src/*.d.ts 15 | test/*.d.ts 16 | examples/*.d.ts 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-injector 2 | Dependency Injection for [Vue.js](http://vuejs.org). 3 | 4 | > This is vue-injector which works only with Vue 2. For the Vue 3 see the [next version](https://www.npmjs.com/package/@scandltd/vue-injector/v/next). 5 | 6 | [![Build Status](https://img.shields.io/circleci/project/github/Scandltd/vue-injector/master.svg?longCache=true&style=flat-square)](https://circleci.com/gh/Scandltd/vue-injector) 7 | [![Coverage Status](https://img.shields.io/coveralls/github/Scandltd/vue-injector?style=flat-square)](https://coveralls.io/github/Scandltd/vue-injector) 8 | [![Size](https://img.shields.io/bundlephobia/minzip/@scandltd/vue-injector.svg?style=flat-square)](https://www.npmjs.com/package/@scandltd/vue-injector) 9 | [![Downloads](https://img.shields.io/npm/dt/@scandltd/vue-injector.svg?longCache=true&style=flat-square)](https://www.npmjs.com/package/@scandltd/vue-injector) 10 | [![Version](https://img.shields.io/npm/v/@scandltd/vue-injector.svg?longCache=true&style=flat-square)](https://www.npmjs.com/package/@scandltd/vue-injector) 11 | [![License](https://img.shields.io/npm/l/@scandltd/vue-injector.svg?longCache=true&style=flat-square)](https://www.npmjs.com/package/@scandltd/vue-injector) 12 | 13 | ### Introduction 14 | 15 | Vue Injector — Dependency Injection library for [Vue.js](https://ru.vuejs.org/). Includes the following: 16 | 17 | - Dependency injection for components 18 | - Construction of the injected services 19 | - Accessibility of Vue application from a service 20 | - Utilization of decorators for convenient operation 21 | 22 | Get started with the [documentation](https://vue-injector.netlify.com/guide/), or play with the [examples](https://github.com/Scandltd/vue-injector/tree/master/examples) (see how to run them below). 23 | 24 | ### Live Playground 25 | 26 | For examples of the plugin in action, go to [codesandbox](https://codesandbox.io/s/github/stelitsyn-sc/vue-injector-example). 27 | 28 | ### Install 29 | 30 | ```bash 31 | $ npm install @scandltd/vue-injector core-js 32 | ``` 33 | 34 | > :warning: 35 | [ECMAScript stage 1 decorators](https://github.com/wycats/javascript-decorators/blob/master/README.md). 36 | If you use Babel, [@babel/plugin-proposal-decorators](https://github.com/babel/babel/tree/master/packages/babel-plugin-proposal-decorators) is needed. 37 | If you use TypeScript, enable `--experimentalDecorators` and `--emitDecoratorMetadata` flags. 38 | 39 | Vue-injector requires a modern JavaScript engine with support for: 40 | 41 | - [Reflect](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Reflect) 42 | - [Reflect Metadata](https://rbuckton.github.io/reflect-metadata/) 43 | - [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) 44 | - [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 45 | 46 | If your environment doesn't support one of these you will need to import a shim or [polyfill](https://github.com/zloirock/core-js/). 47 | 48 | ```js 49 | // polifill.js 50 | 51 | import 'core-js/features/reflect'; 52 | import 'core-js/features/promise'; 53 | import 'core-js/features/map'; 54 | ``` 55 | 56 | > :warning: **The `reflect` polyfill should be imported only once in your entire application** because the Reflect object is meant to be a global singleton. 57 | 58 | ### Example 59 | 60 | This is a small example of using the `vue-injector` to create an `http` service using Observables: 61 | 62 | ```js 63 | // component/todoList.js 64 | 65 | /** ... */ 66 | 67 | /** 68 | * Class-style Vue components: 69 | * 70 | * @Component 71 | * class TodoListComponent extends Vue { 72 | * @Inject(Http) httpClient; 73 | * } 74 | * 75 | * 76 | * Typescript: 77 | * 78 | * @Component 79 | * class TodoListComponent extends Vue { 80 | * @Inject httpClient: Http; 81 | * } 82 | * 83 | */ 84 | 85 | import Http from '../services/http'; 86 | 87 | export default { 88 | name: 'TodoList', 89 | providers: { 90 | httpClient: Http 91 | }, 92 | created() { 93 | this.httpClient 94 | .get(URL) 95 | /** any pipes */ 96 | .subscribe( 97 | this.taskHandler 98 | ) 99 | }, 100 | methods: { 101 | taskHandler(tasks) { 102 | /** ... */ 103 | } 104 | } 105 | } 106 | 107 | /** ... */ 108 | 109 | ``` 110 | 111 | ```js 112 | // services/setup.js 113 | 114 | import Vue from 'vue'; 115 | import { VueInjector } from '@scandltd/vue-injector'; 116 | 117 | Vue.use(VueInjector); 118 | 119 | export default new VueInjector(); 120 | 121 | ``` 122 | 123 | ```js 124 | // main.js 125 | 126 | import injector from './services/setup'; 127 | 128 | /** ... */ 129 | 130 | const root = new Vue({ 131 | /** ... */ 132 | injector 133 | }); 134 | 135 | ``` 136 | 137 | ```js 138 | // services/client.js 139 | 140 | import axios from 'axios'; 141 | import { Injectable } from '@scandltd/vue-injector'; 142 | 143 | @Injectable({ 144 | useFactory: () => axios.create(/** ... */) 145 | }) 146 | class Client {} 147 | 148 | export default Client; 149 | 150 | ``` 151 | 152 | ```js 153 | // services/http.js 154 | 155 | import { Injectable, Inject } from '@scandltd/vue-injector'; 156 | import * as Observable from 'rxjs/internal/observable/fromPromise'; 157 | import { map } from 'rxjs/operators'; 158 | 159 | import Client from './Client'; 160 | 161 | @Injectable 162 | class Http { 163 | @Inject(Client) client; 164 | 165 | observableFactory(promise) { 166 | return Observable 167 | .fromPromise(promise) 168 | .pipe( 169 | map(({ data }) => data) 170 | ); 171 | } 172 | 173 | /** ... */ 174 | 175 | get(url, params) { 176 | return this.observableFactory( 177 | this.client.get(url, { params }) 178 | ); 179 | } 180 | } 181 | 182 | export default Http 183 | 184 | ``` 185 | 186 | 187 | ### Development Setup 188 | 189 | ``` bash 190 | # install deps 191 | yarn install 192 | 193 | # build dist files 194 | yarn build 195 | 196 | # serve examples at localhost:8080 197 | yarn dev 198 | 199 | # lint & run all tests 200 | yarn test 201 | 202 | # serve docs at localhost:8080 203 | yarn docs 204 | ``` 205 | 206 | ## License 207 | 208 | [GPL-2.0](https://opensource.org/licenses/GPL-2.0) 209 | 210 | Copyright (c) 2018-present Scandltd 211 | 212 | 213 | -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const zlib = require('zlib'); 4 | const webpack = require('webpack'); 5 | const WebpackConfig = require('./webpack.config'); 6 | const configs = require('./configs'); 7 | 8 | if (!fs.existsSync('dist')) { 9 | fs.mkdirSync('dist'); 10 | } 11 | 12 | function getSize(size) { 13 | return `${(size / 1024).toFixed(2)}kb`; 14 | } 15 | 16 | function blue(str) { 17 | return `\x1b[1m\x1b[34m${str}\x1b[39m\x1b[22m`; 18 | } 19 | 20 | function write(dest, size, zip) { 21 | return new Promise((resolve, reject) => { 22 | function report(extra) { 23 | // eslint-disable-next-line no-console 24 | console.log( 25 | blue(path.relative(process.cwd(), dest)), 26 | ' ', 27 | getSize(size), 28 | (extra || '') 29 | ); 30 | 31 | resolve(); 32 | } 33 | 34 | if (zip) { 35 | zlib.gzip(fs.readFileSync(dest), (err, { length }) => { 36 | if (err) return reject(err); 37 | report(` (gzipped: ${getSize(length)})`); 38 | }); 39 | } 40 | }); 41 | } 42 | 43 | function build(builds) { 44 | builds.forEach((config) => { 45 | const mode = config.env === 'production'; 46 | 47 | webpack({ 48 | ...WebpackConfig, 49 | output: { 50 | ...WebpackConfig.output, 51 | libraryTarget: config.format, 52 | filename: config.file 53 | }, 54 | optimization: { 55 | ...WebpackConfig.optimization, 56 | minimize: mode 57 | } 58 | }, (err, stats) => { 59 | //TODO: get assets 60 | const assets = stats.toJson().assets; 61 | const chank = assets[assets.length - 1]; 62 | write(path.join(__dirname, '..', 'dist', chank.name), chank.size, true); 63 | }); 64 | }); 65 | } 66 | 67 | build(configs); 68 | -------------------------------------------------------------------------------- /build/configs.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | // browser dev 3 | { 4 | file: 'vue-injector.js', 5 | format: 'umd', 6 | env: 'development' 7 | }, 8 | { 9 | file: 'vue-injector.min.js', 10 | format: 'umd', 11 | env: 'production' 12 | }, 13 | { 14 | file: 'vue-injector.common.js', 15 | format: 'commonjs' 16 | }, 17 | { 18 | file: 'vue-injector.esm.js', 19 | format: 'commonjs-module' 20 | } 21 | ]; 22 | -------------------------------------------------------------------------------- /build/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | echo "Enter release version: " 5 | read VERSION 6 | 7 | commit() { 8 | git add dist/* 9 | git commit -m "[build] $VERSION" 10 | yarn version --new-version $VERSION 11 | } 12 | 13 | publish() { 14 | git push $1 --tags 15 | git push 16 | } 17 | 18 | confirmRelease() { 19 | read -p "Releasing $VERSION - are you sure? (y/n)" yn 20 | case $yn in 21 | [Yy]* ) release; break;; 22 | [Nn]* ) exit;; 23 | * ) echo "Please answer yes or no.";; 24 | esac 25 | } 26 | 27 | confirmRemote() { 28 | linenum=1 29 | remotes=(origin) 30 | r=$(git remote) 31 | 32 | echo "Remotes:" 33 | while read remote ; do 34 | remotes+=($remote) 35 | echo $linenum: $remote 36 | linenum=$((linenum+1)) 37 | done <<< "$r" 38 | 39 | read -p "What remote do you want to use? " remotenum 40 | publish ${remotes[$remotenum]} 41 | } 42 | 43 | confirmPush() { 44 | read -p "Do you want to push release to remote? (y/n)" yn 45 | case $yn in 46 | [Yy]* ) confirmRemote; break;; 47 | [Nn]* ) exit;; 48 | * ) echo "Please answer yes or no.";; 49 | esac 50 | } 51 | 52 | release() { 53 | echo "Releasing $VERSION ..." 54 | yarn test 55 | VERSION=$VERSION yarn run build 56 | 57 | commit 58 | confirmPush 59 | } 60 | 61 | while true; do 62 | confirmRelease 63 | done 64 | -------------------------------------------------------------------------------- /build/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { BannerPlugin } = require('webpack'); 3 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 4 | const { version: packageVersion } = require('../package.json'); 5 | 6 | const version = process.env.VERSION || packageVersion; 7 | const banner = ` 8 | @scandltd/vue-injector v${version} 9 | (c) ${new Date().getFullYear()} Scandltd 10 | @license GPL-2.0 11 | `; 12 | 13 | module.exports = { 14 | mode: 'production', 15 | 16 | entry: path.join(__dirname, '..', 'src', 'index.ts'), 17 | 18 | output: { 19 | path: path.join(__dirname, '..', 'dist') 20 | }, 21 | 22 | externals: { 23 | vue: { 24 | commonjs: 'vue', 25 | commonjs2: 'vue', 26 | amd: 'vue', 27 | root: 'Vue' 28 | } 29 | }, 30 | 31 | module: { 32 | rules: [ 33 | { 34 | test: /\.js$/, 35 | exclude: /node_modules/, 36 | use: 'babel-loader' 37 | }, 38 | { 39 | test: /\.vue$/, 40 | use: 'vue-loader' 41 | }, 42 | { 43 | test: /\.ts$/, 44 | exclude: /node_modules/, 45 | use: [{ 46 | loader: 'ts-loader', 47 | options: { 48 | appendTsSuffixTo: [/\.vue$/] 49 | } 50 | }, 'eslint-loader'] 51 | } 52 | ] 53 | }, 54 | 55 | resolve: { 56 | extensions: ['.ts', '.js', '.vue', '.json'] 57 | }, 58 | 59 | optimization: { 60 | minimize: false, 61 | minimizer: [ 62 | new UglifyJsPlugin({ 63 | cache: true, 64 | parallel: true, 65 | sourceMap: false 66 | }) 67 | ] 68 | }, 69 | 70 | plugins: [ 71 | new BannerPlugin({ 72 | banner 73 | }) 74 | ] 75 | }; 76 | -------------------------------------------------------------------------------- /dist/vue-injector.common.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * @scandltd/vue-injector v3.4.1 4 | * (c) 2021 Scandltd 5 | * @license GPL-2.0 6 | * 7 | */ 8 | (function(e, a) { for(var i in a) e[i] = a[i]; }(exports, /******/ (function(modules) { // webpackBootstrap 9 | /******/ // The module cache 10 | /******/ var installedModules = {}; 11 | /******/ 12 | /******/ // The require function 13 | /******/ function __webpack_require__(moduleId) { 14 | /******/ 15 | /******/ // Check if module is in cache 16 | /******/ if(installedModules[moduleId]) { 17 | /******/ return installedModules[moduleId].exports; 18 | /******/ } 19 | /******/ // Create a new module (and put it into the cache) 20 | /******/ var module = installedModules[moduleId] = { 21 | /******/ i: moduleId, 22 | /******/ l: false, 23 | /******/ exports: {} 24 | /******/ }; 25 | /******/ 26 | /******/ // Execute the module function 27 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 28 | /******/ 29 | /******/ // Flag the module as loaded 30 | /******/ module.l = true; 31 | /******/ 32 | /******/ // Return the exports of the module 33 | /******/ return module.exports; 34 | /******/ } 35 | /******/ 36 | /******/ 37 | /******/ // expose the modules object (__webpack_modules__) 38 | /******/ __webpack_require__.m = modules; 39 | /******/ 40 | /******/ // expose the module cache 41 | /******/ __webpack_require__.c = installedModules; 42 | /******/ 43 | /******/ // define getter function for harmony exports 44 | /******/ __webpack_require__.d = function(exports, name, getter) { 45 | /******/ if(!__webpack_require__.o(exports, name)) { 46 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 47 | /******/ } 48 | /******/ }; 49 | /******/ 50 | /******/ // define __esModule on exports 51 | /******/ __webpack_require__.r = function(exports) { 52 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 53 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 54 | /******/ } 55 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 56 | /******/ }; 57 | /******/ 58 | /******/ // create a fake namespace object 59 | /******/ // mode & 1: value is a module id, require it 60 | /******/ // mode & 2: merge all properties of value into the ns 61 | /******/ // mode & 4: return value when already ns object 62 | /******/ // mode & 8|1: behave like require 63 | /******/ __webpack_require__.t = function(value, mode) { 64 | /******/ if(mode & 1) value = __webpack_require__(value); 65 | /******/ if(mode & 8) return value; 66 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 67 | /******/ var ns = Object.create(null); 68 | /******/ __webpack_require__.r(ns); 69 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 70 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 71 | /******/ return ns; 72 | /******/ }; 73 | /******/ 74 | /******/ // getDefaultExport function for compatibility with non-harmony modules 75 | /******/ __webpack_require__.n = function(module) { 76 | /******/ var getter = module && module.__esModule ? 77 | /******/ function getDefault() { return module['default']; } : 78 | /******/ function getModuleExports() { return module; }; 79 | /******/ __webpack_require__.d(getter, 'a', getter); 80 | /******/ return getter; 81 | /******/ }; 82 | /******/ 83 | /******/ // Object.prototype.hasOwnProperty.call 84 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 85 | /******/ 86 | /******/ // __webpack_public_path__ 87 | /******/ __webpack_require__.p = ""; 88 | /******/ 89 | /******/ 90 | /******/ // Load entry module and return exports 91 | /******/ return __webpack_require__(__webpack_require__.s = 1); 92 | /******/ }) 93 | /************************************************************************/ 94 | /******/ ([ 95 | /* 0 */ 96 | /***/ (function(module, exports) { 97 | 98 | module.exports = require("vue"); 99 | 100 | /***/ }), 101 | /* 1 */ 102 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 103 | 104 | "use strict"; 105 | __webpack_require__.r(__webpack_exports__); 106 | 107 | // EXTERNAL MODULE: external {"commonjs":"vue","commonjs2":"vue","amd":"vue","root":"Vue"} 108 | var external_commonjs_vue_commonjs2_vue_amd_vue_root_Vue_ = __webpack_require__(0); 109 | var external_commonjs_vue_commonjs2_vue_amd_vue_root_Vue_default = /*#__PURE__*/__webpack_require__.n(external_commonjs_vue_commonjs2_vue_amd_vue_root_Vue_); 110 | 111 | // CONCATENATED MODULE: ./src/enums/messages.ts 112 | var ERROR_MESSAGE; 113 | (function (ERROR_MESSAGE) { 114 | ERROR_MESSAGE["ERROR_TYPE"] = "[@scandltd/vue-injector]:"; 115 | ERROR_MESSAGE["ERROR_INJECTABLE_OPTIONS_CONFLICT"] = "@injectable can take only one parameter either {names}"; 116 | ERROR_MESSAGE["ERROR_BUILD_MESSAGE"] = "function \"message\". Parameters in a string do not match those in array: "; 117 | ERROR_MESSAGE["ERROR_INIT_PLUGIN"] = "not installed. Make sure to call `Vue.use(VueInjector)` before creating root instance."; 118 | ERROR_MESSAGE["ERROR_PROVIDERS_TYPE"] = "providers are not objects"; 119 | ERROR_MESSAGE["ERROR_USE_DECORATOR"] = "no decorator Injectable"; 120 | ERROR_MESSAGE["ERROR_USE_FACTORY_RETURN"] = "useFactory invalid return"; 121 | ERROR_MESSAGE["ERROR_USE_VALUE_RETURN"] = "invalid useValue"; 122 | ERROR_MESSAGE["ERROR_USE_FACTORY_TYPE"] = "{name} invalid type useFactory: must be 'function'"; 123 | ERROR_MESSAGE["ERROR_EMTY_INJECT_PARAMS"] = "@inject must get a service as parameter"; 124 | })(ERROR_MESSAGE || (ERROR_MESSAGE = {})); 125 | var WARNING_MESSAGE; 126 | (function (WARNING_MESSAGE) { 127 | WARNING_MESSAGE["WARNING_000"] = "Wrong service registration. Service name: {name}.\n@injectable can take only one parameter either useFactory or useValue, but got {options}"; 128 | })(WARNING_MESSAGE || (WARNING_MESSAGE = {})); 129 | function messages_message(str, arg) { 130 | if (arg === void 0) { arg = {}; } 131 | var newStr = str; 132 | var spareParameters = Reflect.ownKeys(arg).filter(function (val) { return str.match(new RegExp("{" + String(val) + "}")) === null; }); 133 | if (spareParameters.length) { 134 | // eslint-disable-next-line no-console 135 | console.warn(ERROR_MESSAGE.ERROR_BUILD_MESSAGE + spareParameters); 136 | } 137 | Object.keys(arg).forEach(function (key) { 138 | var regex = new RegExp("{" + key + "}"); 139 | newStr = str.replace(regex, arg[key]); 140 | }); 141 | return newStr; 142 | } 143 | 144 | // CONCATENATED MODULE: ./src/util/warn.ts 145 | 146 | function assert(condition, message) { 147 | if (!condition) { 148 | throw new Error(ERROR_MESSAGE.ERROR_TYPE + " " + message); 149 | } 150 | } 151 | function warn(condition, message) { 152 | if (false) {} 153 | } 154 | function isError(err) { 155 | return Object.prototype.toString.call(err).indexOf('Error') > -1; 156 | } 157 | 158 | // CONCATENATED MODULE: ./src/install.ts 159 | 160 | 161 | function install(Vue) { 162 | if (install.installed) 163 | return; 164 | install.installed = true; 165 | if (external_commonjs_vue_commonjs2_vue_amd_vue_root_Vue_default.a !== Vue) { 166 | throw assert(false, 'Multiple instances of Vue detected'); 167 | } 168 | var isDef = function (v) { return v !== undefined; }; 169 | if (Vue.$injectorInstalled) 170 | return; 171 | Vue.$injectorInstalled = true; 172 | Vue.mixin({ 173 | beforeCreate: function () { 174 | if (isDef(this.$options.providers)) { 175 | this.providers = this.$options.providers; 176 | } 177 | if (isDef(this.$options.injector)) { 178 | Object.defineProperty(this, '$injector', { 179 | configurable: false, 180 | writable: false, 181 | value: Vue.observable(this.$options.injector) 182 | }); 183 | this.$injector.init(this); 184 | } 185 | else { 186 | Object.defineProperty(this, '$injector', { 187 | configurable: false, 188 | writable: false, 189 | value: (this.$parent && this.$parent.$injector) || this 190 | }); 191 | if (this.$injector && this.$injector.initComponent && typeof this.$injector.initComponent === 'function') { 192 | this.$injector.initComponent(this); 193 | } 194 | } 195 | } 196 | }); 197 | } 198 | 199 | // CONCATENATED MODULE: ./src/util/dom.ts 200 | var inBrowser = typeof window !== 'undefined'; 201 | 202 | // CONCATENATED MODULE: ./src/util/object.ts 203 | function checkObject(obj) { 204 | return !Array.isArray(obj) && typeof obj === 'object' && obj !== null; 205 | } 206 | 207 | // CONCATENATED MODULE: ./src/di/bindings/binding.ts 208 | var ServiceBinding = /** @class */ (function () { 209 | function ServiceBinding() { 210 | } 211 | ServiceBinding.bind = function (target, service, name) { 212 | return Reflect.defineProperty(target, name, { 213 | enumerable: true, 214 | configurable: false, 215 | get: function () { return service; } 216 | }); 217 | }; 218 | return ServiceBinding; 219 | }()); 220 | 221 | 222 | // CONCATENATED MODULE: ./src/enums/metadata.ts 223 | var METADATA = { 224 | TYPE: Symbol('inject:type'), 225 | VALUE: Symbol('inject:value'), 226 | NAME: Symbol('inject:name'), 227 | SERVICE: Symbol('inject:service'), 228 | TS_TYPE: 'design:type' 229 | }; 230 | var FACTORY_TYPES; 231 | (function (FACTORY_TYPES) { 232 | FACTORY_TYPES["useFactory"] = "useFactory"; 233 | FACTORY_TYPES["useValue"] = "useValue"; 234 | })(FACTORY_TYPES || (FACTORY_TYPES = {})); 235 | 236 | // CONCATENATED MODULE: ./src/di/factory/UseFactory.ts 237 | 238 | 239 | 240 | var UseFactory_UseFactory = /** @class */ (function () { 241 | function UseFactory() { 242 | } 243 | UseFactory.prototype.getFactory = function (Service) { 244 | var name = Reflect.getMetadata(METADATA.NAME, Service); 245 | var factory = Reflect.getMetadata(METADATA.VALUE, Service); 246 | if (factory && typeof factory !== 'function') { 247 | throw assert(false, messages_message(ERROR_MESSAGE.ERROR_USE_FACTORY_TYPE, { name: name })); 248 | } 249 | var result = factory(); 250 | if (!result) { 251 | throw assert(false, ERROR_MESSAGE.ERROR_USE_FACTORY_RETURN); 252 | } 253 | return function () { return result; }; 254 | }; 255 | return UseFactory; 256 | }()); 257 | 258 | 259 | // CONCATENATED MODULE: ./src/di/factory/UseValue.ts 260 | 261 | 262 | 263 | var UseValue_UseValue = /** @class */ (function () { 264 | function UseValue() { 265 | } 266 | UseValue.prototype.getFactory = function (Service) { 267 | var value = Reflect.getMetadata(METADATA.VALUE, Service); 268 | if (value) { 269 | return function () { return value; }; 270 | } 271 | throw assert(false, ERROR_MESSAGE.ERROR_USE_VALUE_RETURN); 272 | }; 273 | return UseValue; 274 | }()); 275 | 276 | 277 | // CONCATENATED MODULE: ./src/di/factory/Instance.ts 278 | var Instance = /** @class */ (function () { 279 | function Instance() { 280 | } 281 | Instance.prototype.getFactory = function (Service) { 282 | var service = new Service(); 283 | return function () { return service; }; 284 | }; 285 | return Instance; 286 | }()); 287 | 288 | 289 | // CONCATENATED MODULE: ./src/di/factory/Factory.ts 290 | 291 | 292 | 293 | 294 | var Factory_ServiceFactory = /** @class */ (function () { 295 | function ServiceFactory() { 296 | } 297 | ServiceFactory.make = function (Service) { 298 | var factoryName = Reflect.getMetadata(METADATA.TYPE, Service); 299 | var factory = ServiceFactory.getFactoryByName(factoryName); 300 | return factory.getFactory(Service); 301 | }; 302 | ServiceFactory.getFactoryByName = function (name) { 303 | switch (name) { 304 | case FACTORY_TYPES.useFactory: 305 | return new UseFactory_UseFactory(); 306 | case FACTORY_TYPES.useValue: 307 | return new UseValue_UseValue(); 308 | default: 309 | return new Instance(); 310 | } 311 | }; 312 | return ServiceFactory; 313 | }()); 314 | 315 | 316 | // CONCATENATED MODULE: ./src/di/provider.ts 317 | 318 | 319 | 320 | 321 | 322 | 323 | var provider_Provider = /** @class */ (function () { 324 | function Provider(service) { 325 | this.service = service; 326 | this.factory = null; 327 | this.register(); 328 | } 329 | Object.defineProperty(Provider.prototype, "instance", { 330 | get: function () { 331 | return this.factory(); 332 | }, 333 | enumerable: true, 334 | configurable: true 335 | }); 336 | Provider.prototype.bindTo = function (target, name) { 337 | return ServiceBinding.bind(target, this.instance, name || this.name); 338 | }; 339 | Object.defineProperty(Provider.prototype, "name", { 340 | get: function () { 341 | return Reflect.getMetadata(METADATA.NAME, this.service); 342 | }, 343 | enumerable: true, 344 | configurable: true 345 | }); 346 | Object.defineProperty(Provider.prototype, "isService", { 347 | get: function () { 348 | return !!Reflect.getMetadata(METADATA.SERVICE, this.service); 349 | }, 350 | enumerable: true, 351 | configurable: true 352 | }); 353 | Provider.prototype.register = function () { 354 | if (this.service === external_commonjs_vue_commonjs2_vue_amd_vue_root_Vue_default.a) { 355 | this.factory = function () { return Provider.app; }; 356 | } 357 | if (!this.factory && this.isService) { 358 | this.factory = Factory_ServiceFactory.make(this.service); 359 | } 360 | if (this.factory) { 361 | return this.factory; 362 | } 363 | throw assert(false, ERROR_MESSAGE.ERROR_USE_DECORATOR); 364 | }; 365 | return Provider; 366 | }()); 367 | 368 | 369 | // CONCATENATED MODULE: ./src/di/injector.ts 370 | 371 | 372 | 373 | 374 | var injector_Injector = /** @class */ (function () { 375 | function Injector(app, rootServices) { 376 | this.rootServices = []; 377 | provider_Provider.app = app; 378 | this.app = app; 379 | this.rootServices = rootServices; 380 | this.services = new Map(); 381 | } 382 | Injector.prototype.registerComponent = function (component) { 383 | var _this = this; 384 | this.provideAllServices(component); 385 | if (this.rootServices.length) { 386 | this.rootServices.forEach(function (provider) { 387 | _this.provide(provider, component); 388 | }); 389 | } 390 | }; 391 | Injector.prototype.get = function (service) { 392 | return this.provide(service); 393 | }; 394 | Injector.prototype.provide = function (service, target, customName) { 395 | if (target === void 0) { target = null; } 396 | if (!this.services.has(service)) { 397 | if (service.prototype.providers) { 398 | this.registerDependencies(service.prototype); 399 | } 400 | this.services.set(service, new provider_Provider(service)); 401 | } 402 | var provider = this.services.get(service); 403 | if (target) { 404 | provider.bindTo(target, customName); 405 | } 406 | return provider.instance; 407 | }; 408 | Injector.prototype.provideAllServices = function (target) { 409 | var _this = this; 410 | if (Object.hasOwnProperty.call(target, 'providers')) { 411 | var providers_1 = target.providers; 412 | if (providers_1 && checkObject(providers_1)) { 413 | Object.keys(providers_1).forEach(function (name) { 414 | if (providers_1 && Object.hasOwnProperty.call(providers_1, name)) { 415 | _this.provide(providers_1[name], target, name); 416 | } 417 | }); 418 | } 419 | else { 420 | throw assert(false, ERROR_MESSAGE.ERROR_PROVIDERS_TYPE); 421 | } 422 | } 423 | }; 424 | Injector.prototype.registerDependencies = function (service) { 425 | this.provideAllServices(service); 426 | delete service.providers; 427 | }; 428 | return Injector; 429 | }()); 430 | 431 | 432 | // CONCATENATED MODULE: ./src/di/decorators/injectable.ts 433 | 434 | 435 | 436 | var injectable_InjectableFactory = /** @class */ (function () { 437 | function InjectableFactory() { 438 | } 439 | Object.defineProperty(InjectableFactory, "whitelist", { 440 | get: function () { 441 | return Reflect.ownKeys(FACTORY_TYPES); 442 | }, 443 | enumerable: true, 444 | configurable: true 445 | }); 446 | InjectableFactory.getOptionKeys = function (options) { 447 | return Reflect.ownKeys(options); 448 | }; 449 | /* checks whether all options given are allowed. Allowed options (useValue, useFactory) */ 450 | InjectableFactory.isOtherProperty = function (options) { 451 | return !InjectableFactory.getOptionKeys(options).every(function (prop) { return InjectableFactory.whitelist.indexOf(prop) !== -1; }); 452 | }; 453 | InjectableFactory.isCollisionProps = function (options) { 454 | var props = InjectableFactory.whitelist 455 | .filter(function (p) { return Reflect.has(options, p); }); 456 | return props.length > 1; 457 | }; 458 | InjectableFactory.getType = function (options) { 459 | return InjectableFactory.whitelist.find(function (props) { return Reflect.has(options, props); }); 460 | }; 461 | InjectableFactory.getServiceType = function (target, options) { 462 | if (InjectableFactory.isOtherProperty(options)) { 463 | InjectableFactory.warnMassage(target, options); 464 | } 465 | if (InjectableFactory.isCollisionProps(options)) { 466 | throw InjectableFactory.errorMassage(); 467 | } 468 | if (InjectableFactory.getType(options)) { 469 | return FACTORY_TYPES[InjectableFactory.getType(options)]; 470 | } 471 | return null; 472 | }; 473 | InjectableFactory.errorMassage = function () { 474 | throw assert(false, messages_message(ERROR_MESSAGE.ERROR_INJECTABLE_OPTIONS_CONFLICT, { names: JSON.stringify(InjectableFactory.whitelist) })); 475 | }; 476 | InjectableFactory.warnMassage = function (target, options) { 477 | warn(false, messages_message(WARNING_MESSAGE.WARNING_000, { 478 | name: target.name, options: JSON.stringify(options) 479 | })); 480 | }; 481 | InjectableFactory.make = function (target, options) { 482 | if (options === void 0) { options = {}; } 483 | this.defineMetadata(target, options); 484 | this.createDecorators(target); 485 | return target; 486 | }; 487 | InjectableFactory.defineMetadata = function (target, options) { 488 | var serviceType = InjectableFactory.getServiceType(target, options); 489 | if (serviceType) { 490 | Reflect.defineMetadata(METADATA.TYPE, serviceType, target); 491 | Reflect.defineMetadata(METADATA.VALUE, options[serviceType], target); 492 | } 493 | Reflect.defineMetadata(METADATA.NAME, target.name, target); 494 | Reflect.defineMetadata(METADATA.SERVICE, true, target); 495 | }; 496 | InjectableFactory.createDecorators = function (target) { 497 | if (target.__decorators__) { 498 | target.__decorators__.forEach(function (fn) { return fn(target.prototype); }); 499 | delete target.__decorators__; 500 | } 501 | }; 502 | return InjectableFactory; 503 | }()); 504 | function Injectable(options) { 505 | if (typeof options === 'function') { 506 | return injectable_InjectableFactory.make(options); 507 | } 508 | return function (target) { return injectable_InjectableFactory.make(target, options); }; 509 | } 510 | 511 | // CONCATENATED MODULE: ./src/util/decorator.ts 512 | /* eslint-disable no-proto */ 513 | function createDecorator(factory) { 514 | return function (target, key) { 515 | var Ctor = typeof target === 'function' 516 | ? target 517 | : target.constructor; 518 | var descriptor = { 519 | enumerable: true, 520 | configurable: true, 521 | initializer: function () { 522 | return this.__proto__[key]; 523 | } 524 | }; 525 | Reflect.defineProperty(target, key, descriptor); 526 | (Ctor.__decorators__ || (Ctor.__decorators__ = [])).push(function (options) { return factory(options, key); }); 527 | return descriptor; 528 | }; 529 | } 530 | 531 | // CONCATENATED MODULE: ./src/di/decorators/inject.ts 532 | 533 | 534 | 535 | 536 | function decoratorFactory(service) { 537 | return createDecorator(function (target, keyProp) { 538 | (target.providers || (target.providers = {}))[keyProp] = service; 539 | }); 540 | } 541 | function Inject(target, key) { 542 | if (typeof target === 'function') { 543 | return decoratorFactory(target); 544 | } 545 | var service = Reflect.getMetadata(METADATA.TS_TYPE, target, key); 546 | if (service === undefined) { 547 | throw assert(false, ERROR_MESSAGE.ERROR_EMTY_INJECT_PARAMS); 548 | } 549 | decoratorFactory(service)(target, key); 550 | } 551 | 552 | // CONCATENATED MODULE: ./src/index.ts 553 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VueInjector", function() { return src_VueInjector; }); 554 | /* concated harmony reexport Injectable */__webpack_require__.d(__webpack_exports__, "Injectable", function() { return Injectable; }); 555 | /* concated harmony reexport Inject */__webpack_require__.d(__webpack_exports__, "Inject", function() { return Inject; }); 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | var src_VueInjector = /** @class */ (function () { 566 | function VueInjector(options) { 567 | if (options === void 0) { options = {}; } 568 | this.rootServices = []; 569 | this.app = null; 570 | this.injector = null; 571 | this.apps = []; 572 | this.rootServices = options.root || []; 573 | if (options.store) { 574 | options.store.$injector = this; 575 | } 576 | } 577 | Object.defineProperty(VueInjector, "app", { 578 | get: function () { 579 | return this; 580 | }, 581 | enumerable: true, 582 | configurable: true 583 | }); 584 | Object.defineProperty(VueInjector.prototype, "install", { 585 | get: function () { 586 | return VueInjector.install; 587 | }, 588 | enumerable: true, 589 | configurable: true 590 | }); 591 | VueInjector.prototype.init = function (app) { 592 | if (false) {} 593 | this.apps.push(app); 594 | // main app already initialized. 595 | if (this.app) { 596 | return; 597 | } 598 | this.app = app; 599 | this.injector = new injector_Injector(this.app, this.rootServices); 600 | }; 601 | VueInjector.prototype.initComponent = function (component) { 602 | return this.injector && this.injector.registerComponent(component); 603 | }; 604 | VueInjector.prototype.get = function (Provider) { 605 | return this.injector && this.injector.get(Provider); 606 | }; 607 | return VueInjector; 608 | }()); 609 | 610 | src_VueInjector.install = install; 611 | src_VueInjector.version = '__VERSION__'; 612 | if (inBrowser && window.Vue) { 613 | window.Vue.use(src_VueInjector); 614 | } 615 | 616 | 617 | /***/ }) 618 | /******/ ]))); -------------------------------------------------------------------------------- /dist/vue-injector.esm.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * @scandltd/vue-injector v3.4.1 4 | * (c) 2021 Scandltd 5 | * @license GPL-2.0 6 | * 7 | */ 8 | module.exports = 9 | /******/ (function(modules) { // webpackBootstrap 10 | /******/ // The module cache 11 | /******/ var installedModules = {}; 12 | /******/ 13 | /******/ // The require function 14 | /******/ function __webpack_require__(moduleId) { 15 | /******/ 16 | /******/ // Check if module is in cache 17 | /******/ if(installedModules[moduleId]) { 18 | /******/ return installedModules[moduleId].exports; 19 | /******/ } 20 | /******/ // Create a new module (and put it into the cache) 21 | /******/ var module = installedModules[moduleId] = { 22 | /******/ i: moduleId, 23 | /******/ l: false, 24 | /******/ exports: {} 25 | /******/ }; 26 | /******/ 27 | /******/ // Execute the module function 28 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 29 | /******/ 30 | /******/ // Flag the module as loaded 31 | /******/ module.l = true; 32 | /******/ 33 | /******/ // Return the exports of the module 34 | /******/ return module.exports; 35 | /******/ } 36 | /******/ 37 | /******/ 38 | /******/ // expose the modules object (__webpack_modules__) 39 | /******/ __webpack_require__.m = modules; 40 | /******/ 41 | /******/ // expose the module cache 42 | /******/ __webpack_require__.c = installedModules; 43 | /******/ 44 | /******/ // define getter function for harmony exports 45 | /******/ __webpack_require__.d = function(exports, name, getter) { 46 | /******/ if(!__webpack_require__.o(exports, name)) { 47 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 48 | /******/ } 49 | /******/ }; 50 | /******/ 51 | /******/ // define __esModule on exports 52 | /******/ __webpack_require__.r = function(exports) { 53 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 54 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 55 | /******/ } 56 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 57 | /******/ }; 58 | /******/ 59 | /******/ // create a fake namespace object 60 | /******/ // mode & 1: value is a module id, require it 61 | /******/ // mode & 2: merge all properties of value into the ns 62 | /******/ // mode & 4: return value when already ns object 63 | /******/ // mode & 8|1: behave like require 64 | /******/ __webpack_require__.t = function(value, mode) { 65 | /******/ if(mode & 1) value = __webpack_require__(value); 66 | /******/ if(mode & 8) return value; 67 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 68 | /******/ var ns = Object.create(null); 69 | /******/ __webpack_require__.r(ns); 70 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 71 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 72 | /******/ return ns; 73 | /******/ }; 74 | /******/ 75 | /******/ // getDefaultExport function for compatibility with non-harmony modules 76 | /******/ __webpack_require__.n = function(module) { 77 | /******/ var getter = module && module.__esModule ? 78 | /******/ function getDefault() { return module['default']; } : 79 | /******/ function getModuleExports() { return module; }; 80 | /******/ __webpack_require__.d(getter, 'a', getter); 81 | /******/ return getter; 82 | /******/ }; 83 | /******/ 84 | /******/ // Object.prototype.hasOwnProperty.call 85 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 86 | /******/ 87 | /******/ // __webpack_public_path__ 88 | /******/ __webpack_require__.p = ""; 89 | /******/ 90 | /******/ 91 | /******/ // Load entry module and return exports 92 | /******/ return __webpack_require__(__webpack_require__.s = 1); 93 | /******/ }) 94 | /************************************************************************/ 95 | /******/ ([ 96 | /* 0 */ 97 | /***/ (function(module, exports) { 98 | 99 | module.exports = undefined; 100 | 101 | /***/ }), 102 | /* 1 */ 103 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 104 | 105 | "use strict"; 106 | __webpack_require__.r(__webpack_exports__); 107 | 108 | // EXTERNAL MODULE: external {"commonjs":"vue","commonjs2":"vue","amd":"vue","root":"Vue"} 109 | var external_commonjs_vue_commonjs2_vue_amd_vue_root_Vue_ = __webpack_require__(0); 110 | var external_commonjs_vue_commonjs2_vue_amd_vue_root_Vue_default = /*#__PURE__*/__webpack_require__.n(external_commonjs_vue_commonjs2_vue_amd_vue_root_Vue_); 111 | 112 | // CONCATENATED MODULE: ./src/enums/messages.ts 113 | var ERROR_MESSAGE; 114 | (function (ERROR_MESSAGE) { 115 | ERROR_MESSAGE["ERROR_TYPE"] = "[@scandltd/vue-injector]:"; 116 | ERROR_MESSAGE["ERROR_INJECTABLE_OPTIONS_CONFLICT"] = "@injectable can take only one parameter either {names}"; 117 | ERROR_MESSAGE["ERROR_BUILD_MESSAGE"] = "function \"message\". Parameters in a string do not match those in array: "; 118 | ERROR_MESSAGE["ERROR_INIT_PLUGIN"] = "not installed. Make sure to call `Vue.use(VueInjector)` before creating root instance."; 119 | ERROR_MESSAGE["ERROR_PROVIDERS_TYPE"] = "providers are not objects"; 120 | ERROR_MESSAGE["ERROR_USE_DECORATOR"] = "no decorator Injectable"; 121 | ERROR_MESSAGE["ERROR_USE_FACTORY_RETURN"] = "useFactory invalid return"; 122 | ERROR_MESSAGE["ERROR_USE_VALUE_RETURN"] = "invalid useValue"; 123 | ERROR_MESSAGE["ERROR_USE_FACTORY_TYPE"] = "{name} invalid type useFactory: must be 'function'"; 124 | ERROR_MESSAGE["ERROR_EMTY_INJECT_PARAMS"] = "@inject must get a service as parameter"; 125 | })(ERROR_MESSAGE || (ERROR_MESSAGE = {})); 126 | var WARNING_MESSAGE; 127 | (function (WARNING_MESSAGE) { 128 | WARNING_MESSAGE["WARNING_000"] = "Wrong service registration. Service name: {name}.\n@injectable can take only one parameter either useFactory or useValue, but got {options}"; 129 | })(WARNING_MESSAGE || (WARNING_MESSAGE = {})); 130 | function messages_message(str, arg) { 131 | if (arg === void 0) { arg = {}; } 132 | var newStr = str; 133 | var spareParameters = Reflect.ownKeys(arg).filter(function (val) { return str.match(new RegExp("{" + String(val) + "}")) === null; }); 134 | if (spareParameters.length) { 135 | // eslint-disable-next-line no-console 136 | console.warn(ERROR_MESSAGE.ERROR_BUILD_MESSAGE + spareParameters); 137 | } 138 | Object.keys(arg).forEach(function (key) { 139 | var regex = new RegExp("{" + key + "}"); 140 | newStr = str.replace(regex, arg[key]); 141 | }); 142 | return newStr; 143 | } 144 | 145 | // CONCATENATED MODULE: ./src/util/warn.ts 146 | 147 | function assert(condition, message) { 148 | if (!condition) { 149 | throw new Error(ERROR_MESSAGE.ERROR_TYPE + " " + message); 150 | } 151 | } 152 | function warn(condition, message) { 153 | if (false) {} 154 | } 155 | function isError(err) { 156 | return Object.prototype.toString.call(err).indexOf('Error') > -1; 157 | } 158 | 159 | // CONCATENATED MODULE: ./src/install.ts 160 | 161 | 162 | function install(Vue) { 163 | if (install.installed) 164 | return; 165 | install.installed = true; 166 | if (external_commonjs_vue_commonjs2_vue_amd_vue_root_Vue_default.a !== Vue) { 167 | throw assert(false, 'Multiple instances of Vue detected'); 168 | } 169 | var isDef = function (v) { return v !== undefined; }; 170 | if (Vue.$injectorInstalled) 171 | return; 172 | Vue.$injectorInstalled = true; 173 | Vue.mixin({ 174 | beforeCreate: function () { 175 | if (isDef(this.$options.providers)) { 176 | this.providers = this.$options.providers; 177 | } 178 | if (isDef(this.$options.injector)) { 179 | Object.defineProperty(this, '$injector', { 180 | configurable: false, 181 | writable: false, 182 | value: Vue.observable(this.$options.injector) 183 | }); 184 | this.$injector.init(this); 185 | } 186 | else { 187 | Object.defineProperty(this, '$injector', { 188 | configurable: false, 189 | writable: false, 190 | value: (this.$parent && this.$parent.$injector) || this 191 | }); 192 | if (this.$injector && this.$injector.initComponent && typeof this.$injector.initComponent === 'function') { 193 | this.$injector.initComponent(this); 194 | } 195 | } 196 | } 197 | }); 198 | } 199 | 200 | // CONCATENATED MODULE: ./src/util/dom.ts 201 | var inBrowser = typeof window !== 'undefined'; 202 | 203 | // CONCATENATED MODULE: ./src/util/object.ts 204 | function checkObject(obj) { 205 | return !Array.isArray(obj) && typeof obj === 'object' && obj !== null; 206 | } 207 | 208 | // CONCATENATED MODULE: ./src/di/bindings/binding.ts 209 | var ServiceBinding = /** @class */ (function () { 210 | function ServiceBinding() { 211 | } 212 | ServiceBinding.bind = function (target, service, name) { 213 | return Reflect.defineProperty(target, name, { 214 | enumerable: true, 215 | configurable: false, 216 | get: function () { return service; } 217 | }); 218 | }; 219 | return ServiceBinding; 220 | }()); 221 | 222 | 223 | // CONCATENATED MODULE: ./src/enums/metadata.ts 224 | var METADATA = { 225 | TYPE: Symbol('inject:type'), 226 | VALUE: Symbol('inject:value'), 227 | NAME: Symbol('inject:name'), 228 | SERVICE: Symbol('inject:service'), 229 | TS_TYPE: 'design:type' 230 | }; 231 | var FACTORY_TYPES; 232 | (function (FACTORY_TYPES) { 233 | FACTORY_TYPES["useFactory"] = "useFactory"; 234 | FACTORY_TYPES["useValue"] = "useValue"; 235 | })(FACTORY_TYPES || (FACTORY_TYPES = {})); 236 | 237 | // CONCATENATED MODULE: ./src/di/factory/UseFactory.ts 238 | 239 | 240 | 241 | var UseFactory_UseFactory = /** @class */ (function () { 242 | function UseFactory() { 243 | } 244 | UseFactory.prototype.getFactory = function (Service) { 245 | var name = Reflect.getMetadata(METADATA.NAME, Service); 246 | var factory = Reflect.getMetadata(METADATA.VALUE, Service); 247 | if (factory && typeof factory !== 'function') { 248 | throw assert(false, messages_message(ERROR_MESSAGE.ERROR_USE_FACTORY_TYPE, { name: name })); 249 | } 250 | var result = factory(); 251 | if (!result) { 252 | throw assert(false, ERROR_MESSAGE.ERROR_USE_FACTORY_RETURN); 253 | } 254 | return function () { return result; }; 255 | }; 256 | return UseFactory; 257 | }()); 258 | 259 | 260 | // CONCATENATED MODULE: ./src/di/factory/UseValue.ts 261 | 262 | 263 | 264 | var UseValue_UseValue = /** @class */ (function () { 265 | function UseValue() { 266 | } 267 | UseValue.prototype.getFactory = function (Service) { 268 | var value = Reflect.getMetadata(METADATA.VALUE, Service); 269 | if (value) { 270 | return function () { return value; }; 271 | } 272 | throw assert(false, ERROR_MESSAGE.ERROR_USE_VALUE_RETURN); 273 | }; 274 | return UseValue; 275 | }()); 276 | 277 | 278 | // CONCATENATED MODULE: ./src/di/factory/Instance.ts 279 | var Instance = /** @class */ (function () { 280 | function Instance() { 281 | } 282 | Instance.prototype.getFactory = function (Service) { 283 | var service = new Service(); 284 | return function () { return service; }; 285 | }; 286 | return Instance; 287 | }()); 288 | 289 | 290 | // CONCATENATED MODULE: ./src/di/factory/Factory.ts 291 | 292 | 293 | 294 | 295 | var Factory_ServiceFactory = /** @class */ (function () { 296 | function ServiceFactory() { 297 | } 298 | ServiceFactory.make = function (Service) { 299 | var factoryName = Reflect.getMetadata(METADATA.TYPE, Service); 300 | var factory = ServiceFactory.getFactoryByName(factoryName); 301 | return factory.getFactory(Service); 302 | }; 303 | ServiceFactory.getFactoryByName = function (name) { 304 | switch (name) { 305 | case FACTORY_TYPES.useFactory: 306 | return new UseFactory_UseFactory(); 307 | case FACTORY_TYPES.useValue: 308 | return new UseValue_UseValue(); 309 | default: 310 | return new Instance(); 311 | } 312 | }; 313 | return ServiceFactory; 314 | }()); 315 | 316 | 317 | // CONCATENATED MODULE: ./src/di/provider.ts 318 | 319 | 320 | 321 | 322 | 323 | 324 | var provider_Provider = /** @class */ (function () { 325 | function Provider(service) { 326 | this.service = service; 327 | this.factory = null; 328 | this.register(); 329 | } 330 | Object.defineProperty(Provider.prototype, "instance", { 331 | get: function () { 332 | return this.factory(); 333 | }, 334 | enumerable: true, 335 | configurable: true 336 | }); 337 | Provider.prototype.bindTo = function (target, name) { 338 | return ServiceBinding.bind(target, this.instance, name || this.name); 339 | }; 340 | Object.defineProperty(Provider.prototype, "name", { 341 | get: function () { 342 | return Reflect.getMetadata(METADATA.NAME, this.service); 343 | }, 344 | enumerable: true, 345 | configurable: true 346 | }); 347 | Object.defineProperty(Provider.prototype, "isService", { 348 | get: function () { 349 | return !!Reflect.getMetadata(METADATA.SERVICE, this.service); 350 | }, 351 | enumerable: true, 352 | configurable: true 353 | }); 354 | Provider.prototype.register = function () { 355 | if (this.service === external_commonjs_vue_commonjs2_vue_amd_vue_root_Vue_default.a) { 356 | this.factory = function () { return Provider.app; }; 357 | } 358 | if (!this.factory && this.isService) { 359 | this.factory = Factory_ServiceFactory.make(this.service); 360 | } 361 | if (this.factory) { 362 | return this.factory; 363 | } 364 | throw assert(false, ERROR_MESSAGE.ERROR_USE_DECORATOR); 365 | }; 366 | return Provider; 367 | }()); 368 | 369 | 370 | // CONCATENATED MODULE: ./src/di/injector.ts 371 | 372 | 373 | 374 | 375 | var injector_Injector = /** @class */ (function () { 376 | function Injector(app, rootServices) { 377 | this.rootServices = []; 378 | provider_Provider.app = app; 379 | this.app = app; 380 | this.rootServices = rootServices; 381 | this.services = new Map(); 382 | } 383 | Injector.prototype.registerComponent = function (component) { 384 | var _this = this; 385 | this.provideAllServices(component); 386 | if (this.rootServices.length) { 387 | this.rootServices.forEach(function (provider) { 388 | _this.provide(provider, component); 389 | }); 390 | } 391 | }; 392 | Injector.prototype.get = function (service) { 393 | return this.provide(service); 394 | }; 395 | Injector.prototype.provide = function (service, target, customName) { 396 | if (target === void 0) { target = null; } 397 | if (!this.services.has(service)) { 398 | if (service.prototype.providers) { 399 | this.registerDependencies(service.prototype); 400 | } 401 | this.services.set(service, new provider_Provider(service)); 402 | } 403 | var provider = this.services.get(service); 404 | if (target) { 405 | provider.bindTo(target, customName); 406 | } 407 | return provider.instance; 408 | }; 409 | Injector.prototype.provideAllServices = function (target) { 410 | var _this = this; 411 | if (Object.hasOwnProperty.call(target, 'providers')) { 412 | var providers_1 = target.providers; 413 | if (providers_1 && checkObject(providers_1)) { 414 | Object.keys(providers_1).forEach(function (name) { 415 | if (providers_1 && Object.hasOwnProperty.call(providers_1, name)) { 416 | _this.provide(providers_1[name], target, name); 417 | } 418 | }); 419 | } 420 | else { 421 | throw assert(false, ERROR_MESSAGE.ERROR_PROVIDERS_TYPE); 422 | } 423 | } 424 | }; 425 | Injector.prototype.registerDependencies = function (service) { 426 | this.provideAllServices(service); 427 | delete service.providers; 428 | }; 429 | return Injector; 430 | }()); 431 | 432 | 433 | // CONCATENATED MODULE: ./src/di/decorators/injectable.ts 434 | 435 | 436 | 437 | var injectable_InjectableFactory = /** @class */ (function () { 438 | function InjectableFactory() { 439 | } 440 | Object.defineProperty(InjectableFactory, "whitelist", { 441 | get: function () { 442 | return Reflect.ownKeys(FACTORY_TYPES); 443 | }, 444 | enumerable: true, 445 | configurable: true 446 | }); 447 | InjectableFactory.getOptionKeys = function (options) { 448 | return Reflect.ownKeys(options); 449 | }; 450 | /* checks whether all options given are allowed. Allowed options (useValue, useFactory) */ 451 | InjectableFactory.isOtherProperty = function (options) { 452 | return !InjectableFactory.getOptionKeys(options).every(function (prop) { return InjectableFactory.whitelist.indexOf(prop) !== -1; }); 453 | }; 454 | InjectableFactory.isCollisionProps = function (options) { 455 | var props = InjectableFactory.whitelist 456 | .filter(function (p) { return Reflect.has(options, p); }); 457 | return props.length > 1; 458 | }; 459 | InjectableFactory.getType = function (options) { 460 | return InjectableFactory.whitelist.find(function (props) { return Reflect.has(options, props); }); 461 | }; 462 | InjectableFactory.getServiceType = function (target, options) { 463 | if (InjectableFactory.isOtherProperty(options)) { 464 | InjectableFactory.warnMassage(target, options); 465 | } 466 | if (InjectableFactory.isCollisionProps(options)) { 467 | throw InjectableFactory.errorMassage(); 468 | } 469 | if (InjectableFactory.getType(options)) { 470 | return FACTORY_TYPES[InjectableFactory.getType(options)]; 471 | } 472 | return null; 473 | }; 474 | InjectableFactory.errorMassage = function () { 475 | throw assert(false, messages_message(ERROR_MESSAGE.ERROR_INJECTABLE_OPTIONS_CONFLICT, { names: JSON.stringify(InjectableFactory.whitelist) })); 476 | }; 477 | InjectableFactory.warnMassage = function (target, options) { 478 | warn(false, messages_message(WARNING_MESSAGE.WARNING_000, { 479 | name: target.name, options: JSON.stringify(options) 480 | })); 481 | }; 482 | InjectableFactory.make = function (target, options) { 483 | if (options === void 0) { options = {}; } 484 | this.defineMetadata(target, options); 485 | this.createDecorators(target); 486 | return target; 487 | }; 488 | InjectableFactory.defineMetadata = function (target, options) { 489 | var serviceType = InjectableFactory.getServiceType(target, options); 490 | if (serviceType) { 491 | Reflect.defineMetadata(METADATA.TYPE, serviceType, target); 492 | Reflect.defineMetadata(METADATA.VALUE, options[serviceType], target); 493 | } 494 | Reflect.defineMetadata(METADATA.NAME, target.name, target); 495 | Reflect.defineMetadata(METADATA.SERVICE, true, target); 496 | }; 497 | InjectableFactory.createDecorators = function (target) { 498 | if (target.__decorators__) { 499 | target.__decorators__.forEach(function (fn) { return fn(target.prototype); }); 500 | delete target.__decorators__; 501 | } 502 | }; 503 | return InjectableFactory; 504 | }()); 505 | function Injectable(options) { 506 | if (typeof options === 'function') { 507 | return injectable_InjectableFactory.make(options); 508 | } 509 | return function (target) { return injectable_InjectableFactory.make(target, options); }; 510 | } 511 | 512 | // CONCATENATED MODULE: ./src/util/decorator.ts 513 | /* eslint-disable no-proto */ 514 | function createDecorator(factory) { 515 | return function (target, key) { 516 | var Ctor = typeof target === 'function' 517 | ? target 518 | : target.constructor; 519 | var descriptor = { 520 | enumerable: true, 521 | configurable: true, 522 | initializer: function () { 523 | return this.__proto__[key]; 524 | } 525 | }; 526 | Reflect.defineProperty(target, key, descriptor); 527 | (Ctor.__decorators__ || (Ctor.__decorators__ = [])).push(function (options) { return factory(options, key); }); 528 | return descriptor; 529 | }; 530 | } 531 | 532 | // CONCATENATED MODULE: ./src/di/decorators/inject.ts 533 | 534 | 535 | 536 | 537 | function decoratorFactory(service) { 538 | return createDecorator(function (target, keyProp) { 539 | (target.providers || (target.providers = {}))[keyProp] = service; 540 | }); 541 | } 542 | function Inject(target, key) { 543 | if (typeof target === 'function') { 544 | return decoratorFactory(target); 545 | } 546 | var service = Reflect.getMetadata(METADATA.TS_TYPE, target, key); 547 | if (service === undefined) { 548 | throw assert(false, ERROR_MESSAGE.ERROR_EMTY_INJECT_PARAMS); 549 | } 550 | decoratorFactory(service)(target, key); 551 | } 552 | 553 | // CONCATENATED MODULE: ./src/index.ts 554 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VueInjector", function() { return src_VueInjector; }); 555 | /* concated harmony reexport Injectable */__webpack_require__.d(__webpack_exports__, "Injectable", function() { return Injectable; }); 556 | /* concated harmony reexport Inject */__webpack_require__.d(__webpack_exports__, "Inject", function() { return Inject; }); 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | var src_VueInjector = /** @class */ (function () { 567 | function VueInjector(options) { 568 | if (options === void 0) { options = {}; } 569 | this.rootServices = []; 570 | this.app = null; 571 | this.injector = null; 572 | this.apps = []; 573 | this.rootServices = options.root || []; 574 | if (options.store) { 575 | options.store.$injector = this; 576 | } 577 | } 578 | Object.defineProperty(VueInjector, "app", { 579 | get: function () { 580 | return this; 581 | }, 582 | enumerable: true, 583 | configurable: true 584 | }); 585 | Object.defineProperty(VueInjector.prototype, "install", { 586 | get: function () { 587 | return VueInjector.install; 588 | }, 589 | enumerable: true, 590 | configurable: true 591 | }); 592 | VueInjector.prototype.init = function (app) { 593 | if (false) {} 594 | this.apps.push(app); 595 | // main app already initialized. 596 | if (this.app) { 597 | return; 598 | } 599 | this.app = app; 600 | this.injector = new injector_Injector(this.app, this.rootServices); 601 | }; 602 | VueInjector.prototype.initComponent = function (component) { 603 | return this.injector && this.injector.registerComponent(component); 604 | }; 605 | VueInjector.prototype.get = function (Provider) { 606 | return this.injector && this.injector.get(Provider); 607 | }; 608 | return VueInjector; 609 | }()); 610 | 611 | src_VueInjector.install = install; 612 | src_VueInjector.version = '__VERSION__'; 613 | if (inBrowser && window.Vue) { 614 | window.Vue.use(src_VueInjector); 615 | } 616 | 617 | 618 | /***/ }) 619 | /******/ ]); -------------------------------------------------------------------------------- /dist/vue-injector.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * @scandltd/vue-injector v3.4.1 4 | * (c) 2021 Scandltd 5 | * @license GPL-2.0 6 | * 7 | */ 8 | !function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(require("vue"));else if("function"==typeof define&&define.amd)define(["vue"],t);else{var r="object"==typeof exports?t(require("vue")):t(e.Vue);for(var n in r)("object"==typeof exports?exports:e)[n]=r[n]}}(window,function(r){return o={},i.m=n=[function(e,t){e.exports=r},function(e,t,r){"use strict";r.r(t);var o,n,i,c=r(0),a=r.n(c);function u(r,n){void 0===n&&(n={});var i=r,e=Reflect.ownKeys(n).filter(function(e){return null===r.match(new RegExp("{"+String(e)+"}"))});return e.length&&console.warn(o.ERROR_BUILD_MESSAGE+e),Object.keys(n).forEach(function(e){var t=new RegExp("{"+e+"}");i=r.replace(t,n[e])}),i}function s(e,t){if(!e)throw new Error(o.ERROR_TYPE+" "+t)}(n=o=o||{}).ERROR_TYPE="[@scandltd/vue-injector]:",n.ERROR_INJECTABLE_OPTIONS_CONFLICT="@injectable can take only one parameter either {names}",n.ERROR_BUILD_MESSAGE='function "message". Parameters in a string do not match those in array: ',n.ERROR_INIT_PLUGIN="not installed. Make sure to call `Vue.use(VueInjector)` before creating root instance.",n.ERROR_PROVIDERS_TYPE="providers are not objects",n.ERROR_USE_DECORATOR="no decorator Injectable",n.ERROR_USE_FACTORY_RETURN="useFactory invalid return",n.ERROR_USE_VALUE_RETURN="invalid useValue",n.ERROR_USE_FACTORY_TYPE="{name} invalid type useFactory: must be 'function'",n.ERROR_EMTY_INJECT_PARAMS="@inject must get a service as parameter",(i||(i={})).WARNING_000="Wrong service registration. Service name: {name}.\n@injectable can take only one parameter either useFactory or useValue, but got {options}";var f="undefined"!=typeof window;var p=(l.bind=function(e,t,r){return Reflect.defineProperty(e,r,{enumerable:!0,configurable:!1,get:function(){return t}})},l);function l(){}var d,h,y={TYPE:Symbol("inject:type"),VALUE:Symbol("inject:value"),NAME:Symbol("inject:name"),SERVICE:Symbol("inject:service"),TS_TYPE:"design:type"};(h=d=d||{}).useFactory="useFactory",h.useValue="useValue";var R=(v.prototype.getFactory=function(e){var t=Reflect.getMetadata(y.NAME,e),r=Reflect.getMetadata(y.VALUE,e);if(r&&"function"!=typeof r)throw s(!1,u(o.ERROR_USE_FACTORY_TYPE,{name:t}));var n=r();if(!n)throw s(!1,o.ERROR_USE_FACTORY_RETURN);return function(){return n}},v);function v(){}var _=(E.prototype.getFactory=function(e){var t=Reflect.getMetadata(y.VALUE,e);if(t)return function(){return t};throw s(!1,o.ERROR_USE_VALUE_RETURN)},E);function E(){}var g=(b.prototype.getFactory=function(e){var t=new e;return function(){return t}},b);function b(){}var O=(j.make=function(e){var t=Reflect.getMetadata(y.TYPE,e);return j.getFactoryByName(t).getFactory(e)},j.getFactoryByName=function(e){switch(e){case d.useFactory:return new R;case d.useValue:return new _;default:return new g}},j);function j(){}var m=(Object.defineProperty(S.prototype,"instance",{get:function(){return this.factory()},enumerable:!0,configurable:!0}),S.prototype.bindTo=function(e,t){return p.bind(e,this.instance,t||this.name)},Object.defineProperty(S.prototype,"name",{get:function(){return Reflect.getMetadata(y.NAME,this.service)},enumerable:!0,configurable:!0}),Object.defineProperty(S.prototype,"isService",{get:function(){return!!Reflect.getMetadata(y.SERVICE,this.service)},enumerable:!0,configurable:!0}),S.prototype.register=function(){if(this.service===a.a&&(this.factory=function(){return S.app}),!this.factory&&this.isService&&(this.factory=O.make(this.service)),this.factory)return this.factory;throw s(!1,o.ERROR_USE_DECORATOR)},S);function S(e){this.service=e,this.factory=null,this.register()}var w=(T.prototype.registerComponent=function(t){var r=this;this.provideAllServices(t),this.rootServices.length&&this.rootServices.forEach(function(e){r.provide(e,t)})},T.prototype.get=function(e){return this.provide(e)},T.prototype.provide=function(e,t,r){void 0===t&&(t=null),this.services.has(e)||(e.prototype.providers&&this.registerDependencies(e.prototype),this.services.set(e,new m(e)));var n=this.services.get(e);return t&&n.bindTo(t,r),n.instance},T.prototype.provideAllServices=function(t){var r=this;if(Object.hasOwnProperty.call(t,"providers")){var n=t.providers;if(!n||!function(e){return!Array.isArray(e)&&"object"==typeof e&&null!==e}(n))throw s(!1,o.ERROR_PROVIDERS_TYPE);Object.keys(n).forEach(function(e){n&&Object.hasOwnProperty.call(n,e)&&r.provide(n[e],t,e)})}},T.prototype.registerDependencies=function(e){this.provideAllServices(e),delete e.providers},T);function T(e,t){this.rootServices=[],m.app=e,this.app=e,this.rootServices=t,this.services=new Map}var P=(Object.defineProperty(M,"whitelist",{get:function(){return Reflect.ownKeys(d)},enumerable:!0,configurable:!0}),M.getOptionKeys=function(e){return Reflect.ownKeys(e)},M.isOtherProperty=function(e){return!M.getOptionKeys(e).every(function(e){return-1!==M.whitelist.indexOf(e)})},M.isCollisionProps=function(t){return 1 { 3 | config.module 4 | .rule('js') // Find the rule. 5 | .use('babel-loader') // Find the loader 6 | .tap(options => Object.assign(options, { // Modifying options 7 | presets: [ 8 | ["@vue/babel-preset-app", { 9 | useBuiltIns: "entry", // or "entry" 10 | corejs: 3, 11 | }] 12 | ] 13 | })) 14 | }, 15 | locales: { 16 | '/': { 17 | lang: 'en', 18 | title: 'Vue Injector', 19 | description: 'Dependancy injection for Vue.js.' 20 | }, 21 | '/ru/': { 22 | lang: 'ru', 23 | title: 'Vue Injector', 24 | description: 'Dependancy injection for Vue.js.' 25 | } 26 | }, 27 | head: [ 28 | ['link', { rel: 'icon', href: `/logo.png` }], 29 | ['link', { rel: 'manifest', href: '/manifest.json' }], 30 | ['meta', { name: 'theme-color', content: '#3eaf7c' }], 31 | ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }], 32 | ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }], 33 | ['link', { rel: 'apple-touch-icon', href: `/icons/apple-icon-152x152.png` }], 34 | ['meta', { name: 'msapplication-TileImage', content: '/icons/ms-icon-144x144.png' }], 35 | ['meta', { name: 'msapplication-TileColor', content: '#000000' }] 36 | ], 37 | serviceWorker: true, 38 | themeConfig: { 39 | repo: 'Scandltd/vue-injector', 40 | editLinks: true, 41 | docsDir: 'docs', 42 | locales: { 43 | '/': { 44 | label: 'English', 45 | selectText: 'Translations', 46 | editLinkText: 'Edit this page on GitHub', 47 | nav: [ 48 | { 49 | text: 'Guide', 50 | link: '/guide/' 51 | }, 52 | { 53 | text: 'API', 54 | link: '/api/' 55 | }, 56 | { 57 | text: 'Change history', 58 | link: 'https://github.com/Scandltd/vue-injector/releases' 59 | } 60 | ], 61 | sidebar: [ 62 | '/installation.md', 63 | '/', 64 | { 65 | title: 'Guide', 66 | collapsable: false, 67 | children: [ 68 | '/guide/', 69 | '/guide/essentials/reg-service.md', 70 | '/guide/essentials/get-service.md', 71 | '/guide/essentials/vue.md', 72 | '/guide/essentials/vuex.md', 73 | '/guide/essentials/nuxt.md' 74 | ] 75 | } 76 | ] 77 | }, 78 | '/ru/': { 79 | label: 'Русский', 80 | selectText: 'Переводы', 81 | editLinkText: 'Изменить эту страницу на GitHub', 82 | nav: [ 83 | { 84 | text: 'Руководство', 85 | link: '/ru/guide/' 86 | }, 87 | { 88 | text: 'Справочник APIe', 89 | link: '/ru/api/' 90 | }, 91 | { 92 | text: 'История изменений', 93 | link: 'https://github.com/Scandltd/vue-injector/releases' 94 | } 95 | ], 96 | sidebar: [ 97 | '/ru/installation.md', 98 | '/ru/', 99 | { 100 | title: 'Основы', 101 | collapsable: false, 102 | children: [ 103 | '/ru/guide/', 104 | '/ru/guide/essentials/reg-service.md', 105 | '/ru/guide/essentials/get-service.md', 106 | '/ru/guide/essentials/vue.md', 107 | '/ru/guide/essentials/vuex.md', 108 | '/ru/guide/essentials/nuxt.md' 109 | ] 110 | } 111 | ] 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /docs/.vuepress/public/_redirects: -------------------------------------------------------------------------------- 1 | # redirect old urls to root 2 | # https://vueinjector.netlify.com/* https://injector.example.com/:splat 301! 3 | 4 | /en/essentials/getting-started.html /guide/ 5 | /en/essentials/* /guide/essentials/* 6 | /en/advanced/* /guide/advanced/* 7 | /en/api/* /api/ 8 | /en/* /:splat 9 | 10 | /ru/essentials/getting-started.html /ru/guide/ 11 | /ru/essentials/* /ru/guide/essentials/* 12 | /ru/advanced/* /ru/guide/advanced/* 13 | /ru/api/* /ru/api/ 14 | /ru/* /ru/:splat 15 | -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/android-icon-144x144.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/android-icon-192x192.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/android-icon-36x36.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/android-icon-48x48.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/android-icon-72x72.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/android-icon-96x96.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/apple-icon-114x114.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/apple-icon-120x120.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/apple-icon-144x144.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/apple-icon-152x152.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/apple-icon-180x180.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/apple-icon-57x57.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/apple-icon-60x60.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/apple-icon-72x72.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/apple-icon-76x76.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/apple-icon-precomposed.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/apple-icon.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/favicon-16x16.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/favicon-32x32.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/favicon-96x96.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/favicon.ico -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/ms-icon-144x144.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/ms-icon-150x150.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/ms-icon-310x310.png -------------------------------------------------------------------------------- /docs/.vuepress/public/icons/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/icons/ms-icon-70x70.png -------------------------------------------------------------------------------- /docs/.vuepress/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Scandltd/vue-injector/1467946588b3a472513ec59d06468f93bff3d1f2/docs/.vuepress/public/logo.png -------------------------------------------------------------------------------- /docs/.vuepress/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "VueInjector", 3 | "short_name": "VueInjector", 4 | "icons": [ 5 | { 6 | "src": "/icons/android-icon-36x36.png", 7 | "sizes": "36x36", 8 | "type": "image/png", 9 | "density": "0.75" 10 | }, 11 | { 12 | "src": "/icons/android-icon-48x48.png", 13 | "sizes": "48x48", 14 | "type": "image/png", 15 | "density": "1.0" 16 | }, 17 | { 18 | "src": "/icons/android-icon-72x72.png", 19 | "sizes": "72x72", 20 | "type": "image/png", 21 | "density": "1.5" 22 | }, 23 | { 24 | "src": "/icons/android-icon-96x96.png", 25 | "sizes": "96x96", 26 | "type": "image/png", 27 | "density": "2.0" 28 | }, 29 | { 30 | "src": "/icons/android-icon-144x144.png", 31 | "sizes": "144x144", 32 | "type": "image/png", 33 | "density": "3.0" 34 | }, 35 | { 36 | "src": "/icons/android-icon-192x192.png", 37 | "sizes": "192x192", 38 | "type": "image/png", 39 | "density": "4.0" 40 | } 41 | ], 42 | "start_url": "/index.html", 43 | "display": "standalone", 44 | "background_color": "#fff", 45 | "theme_color": "#3eaf7c" 46 | } -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Vue Injector — Dependency Injection library for [Vue.js](https://ru.vuejs.org/). Includes the following: 4 | 5 | - Dependency injection for components 6 | - Construction of the injected services 7 | - Accessibility of Vue application from a service 8 | - Utilization of decorators for convenient operation 9 | 10 | [Start acquaintance](./guide/) or try [samples](https://github.com/Scandltd/vue-injector/tree/develop/examples) (check [`README.md`](https://github.com/Scandltd/vue-injector) before launching them). 11 | -------------------------------------------------------------------------------- /docs/api/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar: auto 3 | --- 4 | 5 | # API 6 | 7 | ## `@Injectable` 8 | 9 | `@Injectable` — is a decorator used for a service declaration. By default, the created service includes `Vue` application that can be found in `vm` properties. It is also possible to inject the updated service to an on-going one and add a context needed. 10 | 11 | `@Injectable` must be specified for a service you create. 12 | 13 | ### useFactory 14 | 15 | - type: `function` 16 | - unnecessary 17 | 18 | Signature: 19 | 20 | ``` js 21 | useFactory(() => any) 22 | ``` 23 | 24 | Using a factory to build a service. 25 | 26 | ``` js 27 | class Logger { ... } 28 | 29 | @Injectable({ 30 | useFactory: () => new Logger() 31 | }) 32 | class UserService {} 33 | ``` 34 | 35 | Restricting access to the application from the service. 36 | 37 | ``` js 38 | @Injectable({ 39 | useFactory: () => new UserService() 40 | }) 41 | class UserService {} 42 | ``` 43 | ### useValue 44 | 45 | - type: `any` 46 | - unnecessary 47 | 48 | Signature: 49 | 50 | 51 | Using a value to build a service. 52 | 53 | ``` js 54 | @Injectable({ 55 | useValue: 'someValue' 56 | }) 57 | class UserService {} 58 | ``` 59 | 60 | ## `VueInjector` 61 | 62 | ### app 63 | 64 | - type: `Vue instance` 65 | 66 | Root instance of Vue, in which `injector` is incorporated. 67 | 68 | ### provider 69 | 70 | - type: `Provider` 71 | 72 | Provider that makes services in the form of [Provider](#provider-2) objects available. 73 | 74 | ### initComponent 75 | 76 | Signature: 77 | 78 | ``` js 79 | initComponent(component: Component) 80 | ``` 81 | 82 | Injection of services specified in the `providers` properties into components. 83 | 84 | ### get 85 | 86 | Signature: 87 | 88 | ``` js 89 | get(service: InjectableConstructor) 90 | ``` 91 | 92 | Rolls back an instance of the requested service. If there is no instance yet, creates it and injects into the root instance of Vue. 93 | 94 | ## `Provider` 95 | 96 | **Object (Provider)** registers services and provides access to them. 97 | 98 | Object `Provider` is immutable. Each registration of a component will include the creation of the services needed. 99 | 100 | ### app 101 | 102 | - type: `Vue instance` 103 | 104 | Root instance of Vue, in which `injector` is incorporated. 105 | 106 | ### services 107 | 108 | - type: `Map` 109 | 110 | Object that includes key/value pairs of the connected services. If there are no specifications, an empty object will be a value. 111 | 112 | 113 | ### initComponent 114 | 115 | Signature: 116 | 117 | ``` js 118 | initComponent(component: Component) 119 | ``` 120 | 121 | Injection of the specified in the `providers` properties services into the component. 122 | 123 | ### registerService 124 | 125 | Signature: 126 | 127 | ``` js 128 | registerService(target: InjectedObject, name: string, Service: InjectableConstructor) 129 | ``` 130 | 131 | Injection of the service into the specified object. Returns an instance of the service. 132 | 133 | ### get 134 | 135 | Signature: 136 | 137 | ``` js 138 | get(service: Inject) 139 | ``` 140 | 141 | Rolls back an instance of the requested service. If there is no instance yet, creates it and injects into the root instance of Vue. 142 | 143 | ## Integration for components 144 | 145 | There are two methods you can use to inject dependencies: 146 | - using `providers` component properties 147 | - using decorator `@Inject` 148 | 149 | ``` js 150 | import { Injectable, Service } from 'vue-injector' 151 | 152 | @Injectable 153 | class LogService {} 154 | 155 | // Using property `providers` 156 | default { 157 | providers: { 158 | LogService 159 | } 160 | } 161 | 162 | // Using decorator 163 | @Component 164 | class Component extends Vue { 165 | @Inject(LogService) service; 166 | } 167 | ``` 168 | 169 | ## Injection of properties for components 170 | 171 | Such properties are injecting in each child component, sending an instance of DI to a root instance as an `injector` option. 172 | 173 | - **this.$injector** 174 | 175 | Instance of injector. 176 | 177 | - **this.< ServiceName >** 178 | 179 | Provided services. These properties are read-only. 180 | -------------------------------------------------------------------------------- /docs/guide/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ::: warning Required 4 | [ECMAScript stage 1 decorators](https://github.com/wycats/javascript-decorators/blob/master/README.md). 5 | If you use Babel, [@babel/plugin-proposal-decorators](https://github.com/babel/babel/tree/master/packages/babel-plugin-proposal-decorators) is needed. 6 | If you use TypeScript, enable `--experimentalDecorators` and `--emitDecoratorMetadata` flags. 7 | ::: 8 | 9 | Vue-injector requires a modern JavaScript engine with support for: 10 | 11 | - [Reflect](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Reflect) 12 | - [Reflect Metadata](https://rbuckton.github.io/reflect-metadata/) 13 | - [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) 14 | - [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 15 | 16 | If your environment doesn't support one of these you will need to import a shim or [polyfill](https://github.com/zloirock/core-js/) . 17 | 18 | ::: warning Required 19 | **The `reflect` polyfill should be imported only once in your entire application** because the Reflect object is meant to be a global singleton. 20 | ::: 21 | 22 | Also, all examples will be using the full version of Vue to make on-the-fly template compilation possible. See more details [here](https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only). 23 | ::: 24 | 25 | Using DI with Vue Injector is dead simple. Here’s a basic example: 26 | 27 | ## HTML 28 | 29 | ``` html 30 | 31 | 32 | 33 |
34 |

First Application!

35 |

36 | 37 | 38 |

39 |
40 | ``` 41 | 42 | ## JavaScript 43 | 44 | ``` js 45 | // 0. When using modular system (for ex. through vue-cli), 46 | // import Vue and VueInjector and then call `Vue.use(VueInjector)`. 47 | 48 | // 1. Construct injector instance 49 | // new VueInjector({ store, root: [Service] }) 50 | const injector = new VueInjector() 51 | 52 | // 2. Construct and mount an application’s root instance. 53 | // Make sure you transferred the instance of the plugin using the option 54 | // `injector`, so the application accommodates its existence. 55 | const app = new Vue({ 56 | injector 57 | }).$mount('#app') 58 | 59 | // Your application works! ;) 60 | ``` 61 | 62 | Now you can construct services and inject them into components of the application. 63 | 64 | ``` js 65 | import { Injectable, Inject } from '@scandltd/vue-injector' 66 | 67 | // Register a new service 68 | @Injectable 69 | class LogService extends Inject {} 70 | ``` 71 | 72 | ``` html 73 | 77 | ``` 78 | 79 | ``` js 80 | import LogService from 'logger' 81 | 82 | // Inject dependency into the component. 83 | Vue.component('logger', { 84 | name: 'logger', 85 | providers: { 86 | LogService 87 | } 88 | }) 89 | ``` 90 | 91 | By incorporating the injector, we ensure its accessibility through `this.$injector`, as well as ensure accessibility of the injected services within any component through `this.`: 92 | 93 | ```js 94 | // Home.vue 95 | export default { 96 | computed: { 97 | logger () { 98 | // Soon we'll discuss the `get` purpose 99 | return this.$injector.get(LogService) 100 | } 101 | } 102 | } 103 | ``` 104 | 105 | In documentation, we’ll often refer to `injector` instance. Note that `this.$injector`is still the same `injector`. 106 | -------------------------------------------------------------------------------- /docs/guide/essentials/get-service.md: -------------------------------------------------------------------------------- 1 | # Service use 2 | 3 | ::: tip Note 4 | Examples below are based on using a logging service: 5 | 6 | ``` js 7 | import { Injectable } from '@scandltd/vue-injector' 8 | 9 | @Injectable 10 | class LogService {} 11 | ``` 12 | ::: 13 | 14 | ## Specifying dependencies for components 15 | 16 | There are two methods you can use to inject dependency into a component: 17 | 18 | - By specifying required services in the `providers` component property. 19 | - By using decorator `@Inject`. 20 | 21 | ### `Providers` 22 | 23 | ``` js 24 | export default { 25 | providers: { 26 | LogService 27 | } 28 | }) 29 | ``` 30 | 31 | ### `@Inject` 32 | 33 | ``` js 34 | import { Service } from '@scandltd/vue-injector' 35 | 36 | export default class { 37 | @Inject(LogService) logger 38 | }) 39 | ``` 40 | 41 | ## Usage without dependency injection 42 | 43 | While working with services, sometimes you need to get an instance of a service without injecting it into a component. 44 | 45 | In such cases, we can use `get` method for the instance of the injector: 46 | 47 | ``` js 48 | export default { 49 | computed: { 50 | logger () { 51 | return this.$injector.get(LogService); 52 | } 53 | } 54 | }) 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/guide/essentials/nuxt.md: -------------------------------------------------------------------------------- 1 | # Nuxt plugin 2 | 3 | ## Plugin creation 4 | 5 | To be connected to `vue-injector`, a file with a plugin is to be added to the `plugins` folder. 6 | 7 | ``` js 8 | ``` js 9 | // injector.js 10 | import Vue from 'vue' 11 | import { VueInjector } from '@scandltd/vue-injector' 12 | 13 | Vue.use(VueInjector) 14 | ``` 15 | 16 | ## Connection with plugin 17 | 18 | Now we have to add the created plugin to the `nuxt` configuration file. 19 | 20 | ``` js 21 | // nuxt.config.js 22 | ... 23 | plugins: [ 24 | ... 25 | '~/plugins/injector.js' 26 | ] 27 | ... 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/guide/essentials/reg-service.md: -------------------------------------------------------------------------------- 1 | # Service Registration 2 | 3 | ## Default Service Construction 4 | 5 | For the construction of the service decorator `@Injectable` 6 | 7 | ``` js 8 | import { Injectable } from '@scandltd/vue-injector' 9 | 10 | @Injectable 11 | class LogService {} 12 | ``` 13 | 14 | ## Dependency Injection 15 | 16 | Dependencies could be injected not only into components, but into a service as well. To do so, you have to import dependencies by decorator `Inject`. 17 | 18 | ``` js 19 | @Injectable 20 | class UserService { 21 | @Inject(LogService) LogService; 22 | 23 | constructor () { 24 | this.LogService.log('Create User service'); 25 | } 26 | } 27 | ``` 28 | 29 | ## Factory Provider 30 | 31 | Sometimes you need to inject dependencies from a third-party library that is not designed to work with DI. In cases like this you can use a factory provider. This can be done by passing the factory into the `useFactory` property of the` @ Injectable` decorator. 32 | 33 | ``` js 34 | class Logger { ... } 35 | 36 | @Injectable({ 37 | useFactory: () => new Logger() 38 | }) 39 | class UserService {} 40 | ``` 41 | The `useFactory` property must be a function with a return value. 42 | 43 | ``` js 44 | 45 | useFactory: () => { 46 | return new Logger() 47 | } 48 | ``` 49 | 50 | ## Value Provider 51 | 52 | Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class. To inject an object you have already created, configure the injector with the useValue option 53 | 54 | ``` js 55 | const Logger = { ... } 56 | 57 | @Injectable({ 58 | useValue: Logger 59 | }) 60 | class LoggerService {} 61 | ``` 62 | The `useValue` property can be of any type. -------------------------------------------------------------------------------- /docs/guide/essentials/vue.md: -------------------------------------------------------------------------------- 1 | # Inject Vue instance 2 | 3 | Add Vue instance to some service: 4 | 5 | ```js 6 | import Vue from 'vue' 7 | 8 | @Injectable 9 | class UserService { 10 | @Inject(Vue) vm; 11 | 12 | constructor () { 13 | console.log(this.vm) 14 | } 15 | } 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/guide/essentials/vuex.md: -------------------------------------------------------------------------------- 1 | # Vuex 2 | 3 | ## Integration 4 | 5 | To use `vue-injector` in the store, you must pass the `Store` instance to the plugin constructor: 6 | 7 | ```js 8 | import Vue from 'vue' 9 | import Vuex from 'vuex' 10 | import { VueInjector } from '@scandltd/vue-injector' 11 | 12 | Vue.use(Vuex) 13 | Vue.use(VueInjector) 14 | 15 | const store = new Vuex.Store() 16 | 17 | const injector = new VueInjector({ store }) 18 | 19 | new Vue({ 20 | store, 21 | injector, 22 | el: '#app' 23 | }) 24 | ``` 25 | 26 | After that `$injector` will be available in the context of actions: 27 | 28 | ```js 29 | ... 30 | actions: { 31 | increment () { 32 | this.$injector.get(Service); 33 | } 34 | }, 35 | ... 36 | ``` -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Direct Download / CDN 4 | 5 | [https://unpkg.com/@scandltd/vue-injector@1.0.0/dist/vue-injector.js](https://unpkg.com/@scandltd/vue-injector@1.0.0/dist/vue-injector.js) 6 | 7 | 8 | [Unpkg.com](https://unpkg.com) Provides npm-based CDN links. The above link will always point to the latest release on npm. You can also use a specific version/tag via URLs like: `https://unpkg.com/@scandltd/vue-injector@1.0.0/dist/vue-injector.js`. 9 | 10 | 11 | Include `vue-injector` after Vue and it will install itself automatically: 12 | 13 | ``` html 14 | 15 | 16 | ``` 17 | 18 | ## npm 19 | 20 | ``` bash 21 | npm install @scandltd/vue-injector core-js 22 | ``` 23 | 24 | Vue-injector requires a modern JavaScript engine with support for: 25 | 26 | - [Reflect](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Reflect) 27 | - [Reflect Metadata](https://rbuckton.github.io/reflect-metadata/) 28 | - [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) 29 | - [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 30 | 31 | If your environment doesn't support one of these you will need to import a shim or [polyfill](https://github.com/zloirock/core-js/) . 32 | 33 | ::: warning Required 34 | **The `reflect-metadata` polyfill should be imported only once in your entire application** because the Reflect object is meant to be a global singleton. 35 | ::: 36 | 37 | When used with a module system, you must explicitly install the injector via `Vue.use()`: 38 | 39 | ``` js 40 | import Vue from 'vue' 41 | import { VueInjector } from '@scandltd/vue-injector' 42 | 43 | Vue.use(VueInjector) 44 | 45 | const injector = new VueInjector() 46 | 47 | new Vue({ 48 | injector 49 | }).$mount('#app') 50 | ``` 51 | 52 | You don't need to do this when using global `script` tags. 53 | 54 | ## Dev Build 55 | 56 | You will have to clone directly from GitHub and build `vue-injector` yourself if you want to use the latest dev build: 57 | 58 | ``` bash 59 | git clone https://github.com/Scandltd/vue-injector.git node_modules/@scandltd/vue-injector 60 | cd node_modules/@scandltd/vue-injector 61 | npm install 62 | npm run build 63 | ``` 64 | -------------------------------------------------------------------------------- /docs/ru/README.md: -------------------------------------------------------------------------------- 1 | # Введение 2 | 3 | Vue Injector — библиотека Dependency Injection для [Vue.js](https://ru.vuejs.org/). Включает следующие возможности: 4 | 5 | - Внедрение зависимостей для ваших компонентов 6 | - Конструирование внедряемых сервисов 7 | - Доступ к приложению Vue из сервиса 8 | - Использование декораторов для удобной работы 9 | 10 | [Начать знакомство](./guide/) или поиграться с [примерами](https://github.com/Scandltd/vue-injector/tree/develop/examples) (см. [`README.md`](https://github.com/Scandltd/vue-injector) для их запуска). 11 | -------------------------------------------------------------------------------- /docs/ru/api/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar: auto 3 | --- 4 | 5 | # Справочник API 6 | 7 | ## `@Injectable` 8 | 9 | `@Injectable` — это декоратор, предназначенный для объявления сервиса. По умолчанию в созданном сервисе в свойстве `vm` доступно приложение `Vue`. Кроме того, существует возможность внедрить дополненные сервисы в текущий и добавить собственный контекст. 10 | 11 | `@Injectable` должен быть обязательно указан для создаваемого сервиса. 12 | 13 | ### useFactory 14 | 15 | - тип: `function` 16 | - не обязательный 17 | 18 | Сигнатура: 19 | 20 | ``` js 21 | useFactory() => any 22 | ``` 23 | 24 | Использование фабрики для построения сервиса. 25 | 26 | ``` js 27 | class Logger { ... } 28 | 29 | @Injectable({ 30 | useFactory: () => new Logger() 31 | }) 32 | class UserService extends Inject {} 33 | ``` 34 | 35 | ### useValue 36 | 37 | - type: `любой` 38 | - не обязательный 39 | 40 | Сигнатура: 41 | 42 | Использование значения для построения сервиса. 43 | 44 | ``` js 45 | @Injectable({ 46 | useValue: 'someValue' 47 | }) 48 | class UserService {} 49 | ``` 50 | 51 | ## `VueInjector` 52 | 53 | ### app 54 | 55 | - тип: `Vue instance` 56 | 57 | Корневой экземпляр Vue, в который внедряется `injector`. 58 | 59 | ### provider 60 | 61 | - тип: `Provider` 62 | 63 | Провайдер, предоставляющий доступ к сервисам в виде [объекта Provider](#provider-2). 64 | 65 | ### initComponent 66 | 67 | Сигнатура: 68 | 69 | ``` js 70 | initComponent(component: Component) 71 | ``` 72 | 73 | Внедрение в компонент указанных в свойстве `providers` сервисов. 74 | 75 | ### get 76 | 77 | Сигнатура: 78 | 79 | ``` js 80 | get(service: InjectableConstructor) 81 | ``` 82 | 83 | Возвращает экземпляр запрашиваемого сервиса. Создает экземпляр, если его еще не существует, и внедряет его в корневой экземпляр Vue. 84 | 85 | ## `Provider` 86 | 87 | **Объект (Provider)** регистрирует сервисы и предоставляет к ним доступ. 88 | 89 | Объект `Provider` иммутабелен. Каждая регистрация компонента будет создавать необходимые ему сервисы. 90 | 91 | ### app 92 | 93 | - тип: `Vue instance` 94 | 95 | Корневой экземпляр Vue, в который внедряется `injector`. 96 | 97 | ### services 98 | 99 | - тип: `Map` 100 | 101 | Объект, который содержит пары ключ/значение подключенных сервисов. Если параметров нет, то значением будет пустой объект. 102 | 103 | 104 | ### initComponent 105 | 106 | Сигнатура: 107 | 108 | ``` js 109 | initComponent(component: Component) 110 | ``` 111 | 112 | Внедрение в компонент указанных в свойстве `providers` сервисов. 113 | 114 | ### registerService 115 | 116 | Сигнатура: 117 | 118 | ``` js 119 | registerService(target: InjectedObject, name: string, Service: InjectableConstructor) 120 | ``` 121 | 122 | Внедрение сервиса в указаный объект. Возвращает экземпляр сервиса. 123 | 124 | ### get 125 | 126 | Сигнатура: 127 | 128 | ``` js 129 | get(service: InjectableConstructor) 130 | ``` 131 | 132 | Возвращает экземпляр запрашиваемого сервиса. Создает экземпляр, если его еще не существует, и внедряет его в корневой экземпляр Vue. 133 | 134 | ## Интеграция в компоненты 135 | 136 | Для внедрении зависимости можно использовать два метода: 137 | - используя свойство компонента `providers` 138 | - используя декоратор `@Service` 139 | 140 | ``` js 141 | import { Injectable, Inject } from '@scandltd/vue-injector' 142 | 143 | @Injectable 144 | class LogService {} 145 | 146 | // Используя свойство `providers` 147 | default { 148 | providers: { 149 | LogService 150 | } 151 | } 152 | 153 | // Используя декоратор 154 | @Component 155 | class Component extends Vue { 156 | @Inject(LogService) service; 157 | } 158 | ``` 159 | 160 | ## Внедряемые в компоненты свойства 161 | 162 | Эти свойства внедряются в каждый дочерний компонент, передавая экземпляр DI в корневой экземпляр в качестве опции `injector`. 163 | 164 | - **this.$injector** 165 | 166 | Экземпляр инжектора. 167 | 168 | - **this.< ServiceName >** 169 | 170 | Предоставляемые сервисы. Эти свойства только для чтения. 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /docs/ru/guide/README.md: -------------------------------------------------------------------------------- 1 | # Начало работы 2 | 3 | ::: warning Зависимости 4 | [ECMAScript stage 1 decorators](https://github.com/wycats/javascript-decorators/blob/master/README.md). 5 | Если вы используете Babel, необходим [@babel/plugin-proposal-decorators](https://github.com/babel/babel/tree/master/packages/babel-plugin-proposal-decorators). 6 | Если вы используете TypeScript, включите флаги `--experimentalDecorators` и `--emitDecoratorMetadata`. 7 | ::: 8 | 9 | Vue-injector requires a modern JavaScript engine with support for: 10 | 11 | - [Reflect](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Reflect) 12 | - [Reflect Metadata](https://rbuckton.github.io/reflect-metadata/) 13 | - [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) 14 | - [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 15 | 16 | If your environment doesn't support one of these you will need to import a shim or [polyfill](https://github.com/zloirock/core-js/) . 17 | 18 | ::: warning Required 19 | **The `reflect` polyfill should be imported only once in your entire application** because the Reflect object is meant to be a global singleton. 20 | ::: 21 | 22 | Кроме того, все примеры будут использовать полную сборку Vue, чтобы позволить компиляцию шаблонов на лету. Подробнее о различиях сборок читайте [здесь](https://ru.vuejs.org/v2/guide/installation.html#Runtime-Компилятор-vs-Runtime-only). 23 | ::: 24 | 25 | Использовать DI при помощи Vue Injector очень просто. Пример: 26 | 27 | ## HTML 28 | 29 | ``` html 30 | 31 | 32 | 33 |
34 |

Первое приложение!

35 |

36 | 37 | 38 |

39 |
40 | ``` 41 | 42 | ## JavaScript 43 | 44 | ``` js 45 | // 0. Если используем модульную систему (например через vue-cli), 46 | // импортируем Vue и VueInjector и затем вызываем `Vue.use(VueInjector)`. 47 | 48 | // 1. Создаём экземпляр инжектора 49 | // new VueInjector({ store, root: [Service] }) 50 | const injector = new VueInjector() 51 | 52 | // 2. Создаём и монтируем корневой экземпляр приложения. 53 | // Убедитесь, что передали экземпляр плагина в опции 54 | // `injector`, чтобы позволить приложению знать о его наличии. 55 | const app = new Vue({ 56 | injector 57 | }).$mount('#app') 58 | 59 | // Всё, приложение работает! ;) 60 | ``` 61 | 62 | Теперь мы можем создавать сервисы и внедрять их в компоненты нашего приложения. 63 | 64 | ``` js 65 | import { Injectable, Inject } from '@scandltd/vue-injector' 66 | 67 | // Регистрируем новый сервис 68 | @Injectable 69 | class LogService extends Inject {} 70 | ``` 71 | 72 | ``` html 73 | 77 | ``` 78 | 79 | ``` js 80 | import LogService from 'logger' 81 | 82 | // Внедряем зависимость в компонент. 83 | Vue.component('logger', { 84 | name: 'logger', 85 | providers: { 86 | LogService 87 | } 88 | }) 89 | ``` 90 | 91 | Внедряя инжектор, мы сможем получить к нему доступ через `this.$injector`, а также к внедренным сервисам `this.` внутри любого компонента: 92 | 93 | ```js 94 | // Home.vue 95 | export default { 96 | computed: { 97 | logger () { 98 | // Мы скоро разберём, что такое `get` 99 | return this.$injector.get(LogService) 100 | } 101 | } 102 | } 103 | ``` 104 | 105 | В документации мы будем часто использовать экземпляр `injector`. Имейте ввиду, что `this.$injector` в точности то же самое, что и `injector`. -------------------------------------------------------------------------------- /docs/ru/guide/essentials/get-service.md: -------------------------------------------------------------------------------- 1 | # Получение сервиса 2 | 3 | ::: tip Примечание 4 | В примерах ниже будем использовать сервис для логирования: 5 | 6 | ``` js 7 | import { Injectable } from '@scandltd/vue-injector' 8 | 9 | @Injectable 10 | class LogService {} 11 | ``` 12 | ::: 13 | 14 | ## Указание зависимостей компонента 15 | 16 | Для внедрения зависимости в компонент можно использовать два метода: 17 | 18 | - Указать необходимые сервисы в свойстве компонента `providers`. 19 | - Использовать декоратор `@Inject`. 20 | 21 | ### `Providers` 22 | 23 | ``` js 24 | export default { 25 | providers: { 26 | LogService 27 | } 28 | }) 29 | ``` 30 | 31 | ### `@Inject` 32 | 33 | ``` js 34 | import { Inject } from '@scandltd/vue-injector' 35 | 36 | export default { 37 | @Inject(LogService) logger 38 | }) 39 | ``` 40 | 41 | ## Использование без внедрения зависимости 42 | 43 | Иногда при работе с сервисами требуется получить экземпляр сервиса без внедрения его в компонент. 44 | 45 | Для этого мы можем использовать метод `get` экземпляра инжектора: 46 | 47 | ``` js 48 | export default { 49 | computed: { 50 | logger () { 51 | return this.$injector.get(LogService); 52 | } 53 | } 54 | }) 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/ru/guide/essentials/nuxt.md: -------------------------------------------------------------------------------- 1 | # Nuxt plugin 2 | 3 | ## Создание плагина 4 | 5 | Добавим в папку `plugins` файл плагина для подключения `vue-injector`. 6 | 7 | ``` js 8 | ``` js 9 | // injector.js 10 | import Vue from 'vue' 11 | import { VueInjector } from '@scandltd/vue-injector' 12 | 13 | Vue.use(VueInjector) 14 | ``` 15 | 16 | ## Подключение плагина 17 | 18 | Теперь добавим созданный плагин в конфигурационный файл `nuxt`. 19 | 20 | ``` js 21 | // nuxt.config.js 22 | ... 23 | plugins: [ 24 | ... 25 | '~/plugins/injector.js' 26 | ] 27 | ... 28 | ``` -------------------------------------------------------------------------------- /docs/ru/guide/essentials/reg-service.md: -------------------------------------------------------------------------------- 1 | # Регистрация сервиса 2 | 3 | ## Создание сервиса по умолчанию 4 | 5 | Для создания сервиса используется декоратор `@Injectable` 6 | 7 | ``` js 8 | import { Injectable } from '@scandltd/vue-injector' 9 | 10 | @Injectable 11 | class LogService {} 12 | ``` 13 | 14 | 15 | ## Внедрение зависимостей 16 | 17 | В сервис, так же как и в компонент, возможно внедрить зависимости. Сделать это можно, передав зависимости в декоратор `Inject`. 18 | 19 | ``` js 20 | @Injectable 21 | class UserService extends Inject { 22 | @Inject(LogService) LogService; 23 | 24 | constructor () { 25 | this.LogService.log('Create User service'); 26 | } 27 | } 28 | ``` 29 | 30 | ## Использование фабрики 31 | 32 | Иногда вам нужно реализовать зависимости от сторонней библиотеки, которая не предназначена для работы с DI. В таких случаях вы можете использовать поставщика фабрики. Это можно сделать, передав фабрику в свойстве `useFactory` декоратора` @ Injectable`. 33 | 34 | ``` js 35 | class Logger { ... } 36 | 37 | @Injectable({ 38 | useFactory: () => new Logger() 39 | }) 40 | class UserService extends Inject {} 41 | ``` 42 | 43 | Свойство `useFactory` должно быть функцией с возвращаемым значением. Также в эту функцию передаются экземпляр корневого приложения `Vue` и объект, содержащий встроенные зависимости. 44 | 45 | ``` js 46 | 47 | useFactory: () => { 48 | return new Logger(vm, imports) 49 | } 50 | ``` 51 | 52 | ## Использование значения 53 | 54 | Иногда легче использовать готовый объект. Для того чтобы использовать уже созданный объект, необходимо сконфигурировать декоратор используя свойство "useValue". 55 | ``` js 56 | const Logger = { ... } 57 | 58 | @Injectable({ 59 | useValue: Logger 60 | }) 61 | class LoggerService {} 62 | ``` 63 | `useValue` может быть любого типа -------------------------------------------------------------------------------- /docs/ru/guide/essentials/vue.md: -------------------------------------------------------------------------------- 1 | # Inject Vue instance 2 | 3 | Внедрение `Vue` в сервис: 4 | 5 | ```js 6 | import Vue from 'vue' 7 | 8 | @Injectable 9 | class UserService { 10 | @Inject(Vue) vm; 11 | 12 | constructor () { 13 | console.log(this.vm) 14 | } 15 | } 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/ru/guide/essentials/vuex.md: -------------------------------------------------------------------------------- 1 | # Vuex 2 | 3 | ## Интеграция 4 | 5 | Для использования `vue-injector` в хранилище необходимо передать экземпляр `store` в конструктор плагина: 6 | 7 | ```js 8 | import Vue from 'vue' 9 | import Vuex from 'vuex' 10 | import { VueInjector } from '@scandltd/vue-injector' 11 | 12 | Vue.use(Vuex) 13 | Vue.use(VueInjector) 14 | 15 | const store = new Vuex.Store() 16 | 17 | const injector = new VueInjector({ store }) 18 | 19 | new Vue({ 20 | store, 21 | injector, 22 | el: '#app' 23 | }) 24 | ``` 25 | 26 | После чего `$injector` станет доступен в контексте событий: 27 | 28 | ```js 29 | ... 30 | actions: { 31 | increment () { 32 | this.$injector.get(Service); 33 | } 34 | }, 35 | ... 36 | ``` -------------------------------------------------------------------------------- /docs/ru/installation.md: -------------------------------------------------------------------------------- 1 | # Установка 2 | 3 | ## Скачивание напрямую / CDN 4 | 5 | [https://unpkg.com/@scandltd/vue-injector@1.0.0/dist/vue-injector.js](https://unpkg.com/@scandltd/vue-injector@1.0.0/dist/vue-injector.js) 6 | 7 | 8 | [Unpkg.com](https://unpkg.com) предоставляет CDN-ссылки для NPM-пакетов. Ссылка выше всегда указывает на самую последнюю версию Vue-injector на NPM. Вы можете также использовать конкретную версию с помощью ссылок вида `https://unpkg.com/@scandltd/vue-injector@1.0.0/dist/vue-injector.js`. 9 | 10 | 11 | Подключите `vue-injector` после Vue, и установка произойдёт автоматически: 12 | 13 | ``` html 14 | 15 | 16 | ``` 17 | 18 | ## npm 19 | 20 | ``` bash 21 | npm install @scandltd/vue-injector core-js 22 | ``` 23 | 24 | Vue-injector requires a modern JavaScript engine with support for: 25 | 26 | - [Reflect](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Reflect) 27 | - [Reflect Metadata](https://rbuckton.github.io/reflect-metadata/) 28 | - [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) 29 | - [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) 30 | 31 | If your environment doesn't support one of these you will need to import a shim or [polyfill](https://github.com/zloirock/core-js/) . 32 | 33 | ::: warning Required 34 | **The `reflect-metadata` polyfill should be imported only once in your entire application** because the Reflect object is meant to be a global singleton. 35 | ::: 36 | 37 | При использовании модульной системы необходимо явно обозначить использование инжектора при помощи `Vue.use()`: 38 | 39 | ``` js 40 | import Vue from 'vue' 41 | import { VueInjector } from '@scandltd/vue-injector' 42 | 43 | Vue.use(VueInjector) 44 | ``` 45 | 46 | Это не требуется при подключении через глобальный тег `script`. 47 | 48 | ## Версия для разработки 49 | 50 | Если вы хотите использовать самую новую dev-сборку `vue-injector`, то придётся вручную склонировать репозиторий с GitHub и запустить сборку: 51 | 52 | ``` bash 53 | git clone https://github.com/Scandltd/vue-injector.git node_modules/@scandltd/vue-injector 54 | cd node_modules/@scandltd/vue-injector 55 | npm install 56 | npm run build 57 | ``` 58 | -------------------------------------------------------------------------------- /examples/basic/app.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { Injectable } from '@scandltd/vue-injector'; 3 | 4 | /** 0. Setup vue injector */ 5 | import('../demo.setup'); 6 | 7 | /** 1. Create services */ 8 | @Injectable 9 | class LogService { 10 | log() { 11 | /** ... */ 12 | } 13 | } 14 | 15 | /** 2. Define components */ 16 | Vue.component('VueInjector', { 17 | name: 'AvatarComponent', 18 | providers: { 19 | $Logger: LogService 20 | }, 21 | template: 22 | '
', 23 | mounted() { 24 | this.demo(this.$Logger); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /examples/basic/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ← Examples index 4 | 5 |
6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/demo.mixin.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import JSONFormatter from 'json-formatter-js'; 3 | 4 | export default { 5 | methods: { 6 | demo(obj) { 7 | const formatter = new JSONFormatter(obj); 8 | this.$el.appendChild(formatter.render()); 9 | 10 | // eslint-disable-next-line no-console 11 | console.log(obj); 12 | } 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /examples/demo.setup.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { VueInjector, Injectable } from '@scandltd/vue-injector'; 3 | 4 | import Demo from './demo.mixin'; 5 | 6 | /** Use mixin for showing injectable object */ 7 | Vue.mixin(Demo); 8 | 9 | /** 1. Use plugins. */ 10 | Vue.use(VueInjector); 11 | 12 | /** 2. Create root service */ 13 | @Injectable 14 | class RootService {} 15 | 16 | /** 3. Create the provider */ 17 | const injector = new VueInjector({ 18 | root: [RootService] 19 | }); 20 | 21 | /** 4. Create and mount root instance. */ 22 | export default new Vue({ 23 | el: '#app', 24 | injector 25 | }); 26 | -------------------------------------------------------------------------------- /examples/demo.setup.vuex.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import { VueInjector } from '@scandltd/vue-injector'; 4 | 5 | import Demo from './demo.mixin'; 6 | 7 | /** Use mixin for showing injectable object */ 8 | Vue.mixin(Demo); 9 | 10 | /** 1. Use plugins. */ 11 | Vue.use(VueInjector); 12 | Vue.use(Vuex); 13 | 14 | /** 2. Create vuex store. */ 15 | const store = new Vuex.Store({ 16 | state: { 17 | count: 0 18 | }, 19 | mutations: { 20 | increment(state) { 21 | state.count += 1; 22 | } 23 | }, 24 | actions: { 25 | increment(context) { 26 | // this.$injector.get(Service) 27 | context.commit('increment'); 28 | } 29 | } 30 | }); 31 | 32 | /** 3. Create the provider */ 33 | const injector = new VueInjector({ 34 | store 35 | }); 36 | 37 | /** 4. Create and mount root instance. */ 38 | export default new Vue({ 39 | el: '#app', 40 | store, 41 | injector 42 | }); 43 | -------------------------------------------------------------------------------- /examples/demo.styles.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 3 | color: #2c3e50; 4 | } 5 | 6 | #app { 7 | padding: 0 20px; 8 | } 9 | 10 | ul { 11 | line-height: 1.5em; 12 | padding-left: 1.5em; 13 | } 14 | 15 | a { 16 | color: #7f8c8d; 17 | text-decoration: none; 18 | } 19 | 20 | a:hover { 21 | color: #4fc08d; 22 | } 23 | 24 | .servicet { 25 | float: left; 26 | height: 100%; 27 | width: 100%; 28 | position: relative; 29 | font-family: monospace; 30 | } 31 | 32 | #app > .block > .json-formatter-row { 33 | float: left; 34 | } -------------------------------------------------------------------------------- /examples/factory/app.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-classes-per-file */ 2 | import Vue from 'vue'; 3 | import { Injectable, Inject } from '@scandltd/vue-injector'; 4 | 5 | /** 0. Setup vue injector */ 6 | import('../demo.setup'); 7 | 8 | /** 1. Some kind of 3rd HTTP client */ 9 | class Client { 10 | get() { 11 | /** ... */ 12 | } 13 | } 14 | 15 | /** 2. Create client service wrapper */ 16 | @Injectable({ 17 | useFactory: () => new Client() 18 | }) 19 | class ClientService {} 20 | 21 | /** 3. Create internal Http service */ 22 | @Injectable 23 | class HttpService { 24 | @Inject(ClientService) client; 25 | 26 | get() { 27 | this.client.get(); 28 | } 29 | } 30 | 31 | /** 4. Define components */ 32 | Vue.component('VueInjector', { 33 | name: 'TodoComponent', 34 | providers: { 35 | $Http: HttpService 36 | }, 37 | template: 38 | '
', 39 | mounted() { 40 | this.demo(this.$Http); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /examples/factory/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ← Examples index 4 | 5 |
6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/get_service/app.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { Injectable } from '@scandltd/vue-injector'; 3 | 4 | /** 0. Setup vue injector */ 5 | import('../demo.setup'); 6 | 7 | /** 1. Create user services */ 8 | @Injectable 9 | class UserService { 10 | users() { 11 | /** ... */ 12 | } 13 | } 14 | 15 | /** 2. Define components */ 16 | Vue.component('VueInjector', { 17 | name: 'UserListComponent', 18 | template: 19 | '
', 20 | created() { 21 | this.$UserService = this.$injector.get(UserService); 22 | }, 23 | mounted() { 24 | this.demo(this.$UserService); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /examples/get_service/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ← Examples index 4 | 5 |
6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vue Services Examples 5 | 6 | 7 | 8 |

Vue Services Examples

9 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/provide_error/app.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | /** 0. Setup vue injector */ 4 | import('../demo.setup'); 5 | 6 | /** 1. Create other service */ 7 | class OtherService {} 8 | 9 | /** 2. Define components */ 10 | Vue.component('VueInjector', { 11 | name: 'ListComponent', 12 | providers: { 13 | $OtherService: OtherService 14 | }, 15 | template: 16 | '
', 17 | mounted() { 18 | this.demo(this.$OtherService); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /examples/provide_error/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ← Examples index 4 | 5 |
6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/root/app.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | /** 0. Setup vue injector */ 4 | import('../demo.setup'); 5 | 6 | /** 1. Define components */ 7 | Vue.component('VueInjector', { 8 | name: 'RootComponent', 9 | template: 10 | '
', 11 | mounted() { 12 | /** root service implementation in `../demo.setup` */ 13 | this.demo(this.RootService); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /examples/root/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ← Examples index 4 | 5 |
6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/server.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | /* eslint-disable max-classes-per-file */ 3 | const express = require('express'); 4 | const rewrite = require('express-urlrewrite'); 5 | const webpack = require('webpack'); 6 | const webpackDevMiddleware = require('webpack-dev-middleware'); 7 | 8 | const app = express(); 9 | const fs = require('fs'); 10 | const path = require('path'); 11 | const WebpackConfig = require('./webpack.config'); 12 | 13 | app.use(webpackDevMiddleware(webpack(WebpackConfig), { 14 | publicPath: '/__build__/', 15 | stats: { 16 | colors: true, 17 | chunks: false 18 | }, 19 | watchOptions: { 20 | aggregateTimeout: 300, 21 | poll: 1000 22 | } 23 | })); 24 | 25 | fs.readdirSync(__dirname).forEach((file) => { 26 | if (fs.statSync(path.join(__dirname, file)).isDirectory()) { 27 | app.use(rewrite(`/${file}/*`, `/${file}/index.html`)); 28 | } 29 | }); 30 | 31 | app.use(express.static(__dirname)); 32 | 33 | const port = process.env.PORT || 8080; 34 | module.exports = app.listen(port, () => { 35 | // eslint-disable-next-line no-console 36 | console.log(`Server listening on http://localhost:${port}, Ctrl+C to stop`); 37 | }); 38 | -------------------------------------------------------------------------------- /examples/service_decorator/app.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-classes-per-file */ 2 | import Vue from 'vue'; 3 | import Component from 'vue-class-component'; 4 | 5 | import { Injectable, Inject } from '@scandltd/vue-injector'; 6 | 7 | /** 0. Setup vue injector */ 8 | import('../demo.setup'); 9 | 10 | /** 1. Create services */ 11 | @Injectable 12 | class LogService {} 13 | 14 | /** 2. Define components */ 15 | @Component({ 16 | template: 17 | '
' 18 | }) 19 | class MenuComponent extends Vue { 20 | @Inject(LogService) logger; 21 | 22 | mounted() { 23 | this.demo(this.logger); 24 | } 25 | } 26 | 27 | Vue.component('VueInjector', MenuComponent); 28 | -------------------------------------------------------------------------------- /examples/service_decorator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ← Examples index 4 | 5 |
6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/typescript/UserComponent.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 23 | -------------------------------------------------------------------------------- /examples/typescript/app.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { VueInjector } from '@scandltd/vue-injector'; 3 | 4 | import UserComponent from './UserComponent.vue'; 5 | 6 | import Demo from '../demo.mixin'; 7 | 8 | /** Use mixin for showing injectable object */ 9 | Vue.mixin(Demo); 10 | 11 | /** 1. Use plugins. */ 12 | Vue.use(VueInjector); 13 | 14 | /** 2. Create the provider */ 15 | const injector = new VueInjector(); 16 | 17 | /** 4. Create and mount root instance. */ 18 | export default new Vue({ 19 | el: '#app', 20 | injector, 21 | components: { 22 | VueInjector: UserComponent 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /examples/typescript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ← Examples index 4 | 5 |
6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/value/app.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { Injectable } from '@scandltd/vue-injector'; 3 | 4 | /** 0. Setup vue injector */ 5 | import('../demo.setup'); 6 | 7 | /** 1. Create services */ 8 | @Injectable({ 9 | useValue: 'TOKEN' 10 | }) 11 | class TokenService {} 12 | 13 | /** 2. Define components */ 14 | Vue.component('VueInjector', { 15 | name: 'TokenComponent', 16 | providers: { 17 | $TOKEN: TokenService 18 | }, 19 | template: 20 | '
', 21 | mounted() { 22 | this.demo(this.$TOKEN); 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /examples/value/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ← Examples index 4 | 5 |
6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/vue/app.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { Injectable, Inject } from '@scandltd/vue-injector'; 3 | 4 | /** 0. Setup vue injector */ 5 | import('../demo.setup'); 6 | 7 | /** 1. Create services */ 8 | @Injectable 9 | class LogService { 10 | @Inject(Vue) vm; 11 | } 12 | 13 | /** 2. Define components */ 14 | Vue.component('VueInjector', { 15 | name: 'TransportComponent', 16 | providers: { 17 | $logger: LogService 18 | }, 19 | template: 20 | '
', 21 | mounted() { 22 | this.demo(this.$logger); 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /examples/vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ← Examples index 4 | 5 |
6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/vuex/app.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | import Vue from 'vue'; 3 | import { Injectable } from '@scandltd/vue-injector'; 4 | 5 | /** 0. Setup vue injector */ 6 | import('../demo.setup.vuex'); 7 | 8 | // 3. Create services 9 | @Injectable 10 | class StoreService {} 11 | 12 | // 4. Define components 13 | Vue.component('VueInjector', { 14 | name: 'anyComponent', 15 | providers: { 16 | $StoreService: StoreService 17 | }, 18 | template: 19 | '
', 20 | mounted() { 21 | this.$store.dispatch('increment'); 22 | this.demo(this.$StoreService); 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /examples/vuex/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ← Examples index 4 | 5 |
6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/webpack.config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const VuePlugin = require('vue-loader/lib/plugin'); 4 | 5 | module.exports = { 6 | // Expose __dirname to allow automatically setting basename. 7 | context: __dirname, 8 | node: { 9 | __dirname: true 10 | }, 11 | 12 | mode: process.env.NODE_ENV || 'development', 13 | 14 | entry: fs.readdirSync(__dirname).reduce((entries, dir) => { 15 | const fullDir = path.join(__dirname, dir); 16 | const newEntries = { ...entries }; 17 | let entry = path.join(fullDir, 'app.js'); 18 | 19 | if (!fs.existsSync(path.join(fullDir, 'app.js'))) { 20 | entry = path.join(fullDir, 'app.ts'); 21 | } 22 | 23 | if (fs.statSync(fullDir).isDirectory() && fs.existsSync(entry)) { 24 | newEntries[dir] = [path.join(__dirname, '..', 'src', 'polyfill.ts'), entry]; 25 | } 26 | 27 | return newEntries; 28 | }, {}), 29 | 30 | output: { 31 | path: path.join(__dirname, '__build__'), 32 | filename: '[name].js', 33 | chunkFilename: '[id].chunk.js', 34 | publicPath: '/__build__/' 35 | }, 36 | 37 | module: { 38 | rules: [ 39 | { 40 | test: /\.js$/, 41 | exclude: /node_modules/, 42 | use: 'babel-loader' 43 | }, 44 | { 45 | test: /\.vue$/, 46 | use: 'vue-loader' 47 | }, 48 | { 49 | test: /\.ts$/, 50 | exclude: /node_modules/, 51 | use: [{ 52 | loader: 'ts-loader', 53 | options: { 54 | appendTsSuffixTo: [/\.vue$/] 55 | } 56 | }, 'eslint-loader'] 57 | }, 58 | { 59 | test: /\.css$/, 60 | use: [ 61 | 'vue-style-loader', 62 | 'css-loader' 63 | ] 64 | } 65 | ] 66 | }, 67 | 68 | resolve: { 69 | extensions: ['.ts', '.js', '.vue', '.json'], 70 | alias: { 71 | vuex: 'vuex/dist/vuex.esm.js', 72 | vue: 'vue/dist/vue.esm.js', 73 | '@scandltd/vue-injector': path.join(__dirname, '..', 'src') 74 | } 75 | }, 76 | 77 | optimization: { 78 | splitChunks: { 79 | cacheGroups: { 80 | shared: { 81 | name: 'shared', 82 | chunks: 'initial', 83 | minChunks: 2 84 | } 85 | } 86 | } 87 | }, 88 | 89 | plugins: [ 90 | new VuePlugin() 91 | ], 92 | 93 | watchOptions: { 94 | aggregateTimeout: 300, 95 | poll: 1000 96 | } 97 | }; 98 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@scandltd/vue-injector", 3 | "version": "3.4.1", 4 | "description": "Dependency Injection for Vue.js 2", 5 | "type": "library", 6 | "license": "GPL-2.0", 7 | "authors": [ 8 | { 9 | "name": "Stepan Telitsyn", 10 | "email": "telitsyn@scand.com" 11 | } 12 | ], 13 | "main": "dist/vue-injector.js", 14 | "unpkg": "dist/vue-injector.js", 15 | "jsdelivr": "dist/vue-injector.js", 16 | "sideEffects": false, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/Scandltd/vue-injector.git" 20 | }, 21 | "types": "src/index.d.ts", 22 | "files": [ 23 | "dist/*.js", 24 | "types/*.d.ts", 25 | "src/**/*.d.ts" 26 | ], 27 | "keywords": [ 28 | "vue", 29 | "typescript", 30 | "typescript inject", 31 | "vue inject", 32 | "vue injector", 33 | "vue provider", 34 | "vue services", 35 | "vue di", 36 | "di", 37 | "dependency injection" 38 | ], 39 | "scripts": { 40 | "dev": "node examples/server.js", 41 | "dev:dist": "rollup -wm -c build/rollup.dev.config.js", 42 | "build": "node build/build.js", 43 | "lint": "eslint examples src/**/*.ts --ignore-pattern *.d.ts", 44 | "lint:fix": "eslint examples src/**/*.ts --ignore-pattern *.d.ts --fix", 45 | "test": "yarn run lint && yarn run test:unit && yarn run test:types", 46 | "test:unit": "node_modules/.bin/jest", 47 | "test:cover": "node_modules/.bin/jest --coverage --coverageReporters=text-lcov | coveralls", 48 | "test:types": "tsc --noEmit -p .", 49 | "types": "tsc --emitDeclarationOnly -p .", 50 | "docs": "vuepress dev docs", 51 | "docs:build": "vuepress build docs", 52 | "release": "bash build/release.sh" 53 | }, 54 | "devDependencies": { 55 | "@babel/core": "^7.0.0", 56 | "@babel/plugin-external-helpers": "^7.0.0", 57 | "@babel/plugin-proposal-class-properties": "^7.0.0", 58 | "@babel/plugin-proposal-decorators": "^7.0.0", 59 | "@babel/plugin-syntax-dynamic-import": "^7.0.0", 60 | "@babel/preset-env": "^7.0.0", 61 | "@babel/preset-typescript": "^7.7.2", 62 | "@types/core-js": "^2.5.2", 63 | "@types/jest": "^24.0.23", 64 | "@types/node": "^12.12.7", 65 | "@types/reflect-metadata": "^0.1.0", 66 | "@typescript-eslint/eslint-plugin": "^2.7.0", 67 | "@typescript-eslint/parser": "^2.7.0", 68 | "@vue/test-utils": "^1.0.0-beta.29", 69 | "babel-core": "^7.0.0-bridge.0", 70 | "babel-eslint": "^9.0.0", 71 | "babel-jest": "^23.4.2", 72 | "babel-loader": "^8.0.0", 73 | "babel-plugin-istanbul": "^4.1.6", 74 | "buble": "^0.19.3", 75 | "core-js": "^3.4.2", 76 | "coveralls": "^3.0.8", 77 | "cross-spawn": "^5.0.1", 78 | "css-loader": "3.2.0", 79 | "eslint": "^6.1.0", 80 | "eslint-config-airbnb-base": "^14.0.0", 81 | "eslint-loader": "^3.0.2", 82 | "eslint-plugin-import": "^2.18.2", 83 | "eslint-plugin-jest": "^23.0.2", 84 | "eslint-plugin-jsx-a11y": "^6.2.3", 85 | "express": "^4.16.2", 86 | "express-urlrewrite": "^1.2.0", 87 | "jest": "^24.9.0", 88 | "json-formatter-js": "^2.2.0", 89 | "ts-jest": "^24.1.0", 90 | "ts-loader": "^6.2.1", 91 | "typescript": "^3.7.2", 92 | "uglify-js": "^3.6.9", 93 | "uglifyjs-webpack-plugin": "^2.2.0", 94 | "vue": "^2.6.10", 95 | "vue-class-component": "^7.1.0", 96 | "vue-loader": "^15.7.2", 97 | "vue-property-decorator": "^8.3.0", 98 | "vue-template-compiler": "^2.6.10", 99 | "vuepress": "^1.2.0", 100 | "vuex": "^3.1.2", 101 | "webpack": "^4.41.2", 102 | "webpack-cli": "^3.3.10", 103 | "webpack-dev-middleware": "^3.7.2" 104 | }, 105 | "peerDependencies": { 106 | "vue": "^2.6.10" 107 | }, 108 | "bugs": { 109 | "url": "https://github.com/Scandltd/vue-injector/issues" 110 | }, 111 | "homepage": "https://scand.com/products/vue-injector/", 112 | "jest": { 113 | "preset": "ts-jest", 114 | "testRegex": "/test/unit/.*(\\.test.ts|\\.spec.ts)$", 115 | "setupFiles": [ 116 | "./src/polyfill.ts" 117 | ], 118 | "moduleFileExtensions": [ 119 | "js", 120 | "ts", 121 | "json" 122 | ], 123 | "modulePaths": [ 124 | "", 125 | "/node_modules" 126 | ], 127 | "coverageDirectory": "test/unit/coverage", 128 | "collectCoverageFrom": [ 129 | "src/**/*.ts" 130 | ] 131 | }, 132 | "nyc": { 133 | "include": [ 134 | "src/**/*.js" 135 | ], 136 | "all": true, 137 | "require": [ 138 | "babel-register" 139 | ], 140 | "reporter": [ 141 | "lcov", 142 | "text" 143 | ], 144 | "sourceMap": false, 145 | "instrument": false 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/di/bindings/binding.ts: -------------------------------------------------------------------------------- 1 | import { InjectedObject } from '../decorators/injectable'; 2 | 3 | export class ServiceBinding { 4 | static bind( 5 | target: InjectedObject, 6 | service: T, 7 | name: string 8 | ): boolean { 9 | return Reflect.defineProperty(target, name, { 10 | enumerable: true, 11 | configurable: false, 12 | get: () => service 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/di/decorators/inject.ts: -------------------------------------------------------------------------------- 1 | import { createDecorator } from '../../util/decorator'; 2 | import { METADATA } from '../../enums/metadata'; 3 | import { ERROR_MESSAGE } from '../../enums/messages'; 4 | import { assert } from '../../util/warn'; 5 | import { InjectableConstructor, InjectedObject } from './injectable'; 6 | 7 | function decoratorFactory(service: any): any { 8 | return createDecorator((target: any, keyProp: string) => { 9 | (target.providers || (target.providers = {}))[keyProp] = service; 10 | }); 11 | } 12 | 13 | export interface Inject { 14 | service?: InjectableConstructor; 15 | } 16 | 17 | export function Inject(servise: InjectableConstructor): any 18 | export function Inject(target: InjectedObject, key: string): any 19 | export function Inject(target: InjectableConstructor | InjectedObject, key?: string): any { 20 | if (typeof target === 'function') { 21 | return decoratorFactory(target); 22 | } 23 | 24 | const service = Reflect.getMetadata(METADATA.TS_TYPE, target, key); 25 | 26 | if (service === undefined) { 27 | throw assert(false, ERROR_MESSAGE.ERROR_EMTY_INJECT_PARAMS); 28 | } 29 | 30 | decoratorFactory(service)(target, key); 31 | } 32 | -------------------------------------------------------------------------------- /src/di/decorators/injectable.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import { assert, warn } from '../../util/warn'; 4 | import { ERROR_MESSAGE, message, WARNING_MESSAGE } from '../../enums/messages'; 5 | import { FACTORY_TYPES, METADATA } from '../../enums/metadata'; 6 | 7 | export type InjectableConstructor = { 8 | __decorators__?: Array; 9 | providers?: { [key: string]: InjectableConstructor }; 10 | new (): T; 11 | } 12 | 13 | export interface InjectableOptions { 14 | useFactory?: () => any; 15 | useValue?: any; 16 | } 17 | 18 | export type InjectedObject = Vue | InjectableConstructor | any; 19 | 20 | class InjectableFactory { 21 | private static get whitelist() { 22 | return Reflect.ownKeys(FACTORY_TYPES); 23 | } 24 | 25 | private static getOptionKeys(options: InjectableOptions): PropertyKey[] { 26 | return Reflect.ownKeys(options); 27 | } 28 | 29 | /* checks whether all options given are allowed. Allowed options (useValue, useFactory) */ 30 | private static isOtherProperty(options: InjectableOptions): boolean { 31 | return !InjectableFactory.getOptionKeys(options).every( 32 | (prop: PropertyKey) => InjectableFactory.whitelist.indexOf(prop) !== -1 33 | ); 34 | } 35 | 36 | private static isCollisionProps(options: InjectableOptions) { 37 | const props = InjectableFactory.whitelist 38 | .filter((p) => Reflect.has(options, p)); 39 | 40 | return props.length > 1; 41 | } 42 | 43 | private static getType(options: InjectableOptions) { 44 | return InjectableFactory.whitelist.find((props) => Reflect.has(options, props)); 45 | } 46 | 47 | private static getServiceType(target: InjectableConstructor, options: InjectableOptions) { 48 | if (InjectableFactory.isOtherProperty(options)) { 49 | InjectableFactory.warnMassage(target, options); 50 | } 51 | 52 | if (InjectableFactory.isCollisionProps(options)) { 53 | throw InjectableFactory.errorMassage(); 54 | } 55 | 56 | if (InjectableFactory.getType(options)) { 57 | return FACTORY_TYPES[InjectableFactory.getType(options)]; 58 | } 59 | 60 | return null; 61 | } 62 | 63 | private static errorMassage() { 64 | throw assert(false, message( 65 | ERROR_MESSAGE.ERROR_INJECTABLE_OPTIONS_CONFLICT, 66 | { names: JSON.stringify(InjectableFactory.whitelist) } 67 | )); 68 | } 69 | 70 | private static warnMassage(target: InjectableConstructor, options: InjectableOptions) { 71 | warn( 72 | false, 73 | message(WARNING_MESSAGE.WARNING_000, { 74 | name: target.name, options: JSON.stringify(options) 75 | }) 76 | ); 77 | } 78 | 79 | static make( 80 | target: InjectableConstructor, 81 | options: InjectableOptions = {} 82 | ): InjectableConstructor { 83 | this.defineMetadata(target, options); 84 | this.createDecorators(target); 85 | 86 | return target; 87 | } 88 | 89 | private static defineMetadata(target: InjectableConstructor, options: InjectableOptions) { 90 | const serviceType = InjectableFactory.getServiceType(target, options); 91 | 92 | if (serviceType) { 93 | Reflect.defineMetadata(METADATA.TYPE, serviceType, target); 94 | Reflect.defineMetadata(METADATA.VALUE, options[serviceType], target); 95 | } 96 | 97 | Reflect.defineMetadata(METADATA.NAME, target.name, target); 98 | Reflect.defineMetadata(METADATA.SERVICE, true, target); 99 | } 100 | 101 | private static createDecorators(target: InjectableConstructor) { 102 | if (target.__decorators__) { 103 | target.__decorators__.forEach((fn) => fn(target.prototype)); 104 | delete target.__decorators__; 105 | } 106 | } 107 | } 108 | 109 | export interface Injectable extends InjectableOptions {} 110 | 111 | export function Injectable(target: InjectableConstructor): any 112 | export function Injectable(options: InjectableOptions): any 113 | export function Injectable(options: InjectableOptions | InjectableConstructor): any { 114 | if (typeof options === 'function') { 115 | return InjectableFactory.make(options); 116 | } 117 | 118 | return (target) => InjectableFactory.make(target, options); 119 | } 120 | -------------------------------------------------------------------------------- /src/di/factory/Factory.ts: -------------------------------------------------------------------------------- 1 | import { InjectableConstructor } from '../decorators/injectable'; 2 | import { FACTORY_TYPES, METADATA } from '../../enums/metadata'; 3 | import { UseFactory } from './UseFactory'; 4 | import { UseValue } from './UseValue'; 5 | import { Instance } from './Instance'; 6 | 7 | 8 | export interface FactoryInterface { 9 | getFactory(Service: InjectableConstructor): () => R; 10 | } 11 | 12 | export class ServiceFactory { 13 | static make(Service: InjectableConstructor): () => R { 14 | const factoryName = Reflect.getMetadata(METADATA.TYPE, Service); 15 | const factory = ServiceFactory.getFactoryByName(factoryName); 16 | 17 | return factory.getFactory(Service); 18 | } 19 | 20 | private static getFactoryByName(name: FACTORY_TYPES) { 21 | switch (name) { 22 | case FACTORY_TYPES.useFactory: 23 | return new UseFactory(); 24 | case FACTORY_TYPES.useValue: 25 | return new UseValue(); 26 | default: 27 | return new Instance(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/di/factory/Instance.ts: -------------------------------------------------------------------------------- 1 | import { FactoryInterface } from './Factory'; 2 | import { InjectableConstructor } from '../decorators/injectable'; 3 | 4 | export class Instance implements FactoryInterface { 5 | getFactory(Service: InjectableConstructor): () => R { 6 | const service = new Service(); 7 | 8 | return () => service as any as R; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/di/factory/UseFactory.ts: -------------------------------------------------------------------------------- 1 | import { FactoryInterface } from './Factory'; 2 | import { InjectableConstructor } from '../decorators/injectable'; 3 | import { assert } from '../../util/warn'; 4 | import { METADATA } from '../../enums/metadata'; 5 | import { ERROR_MESSAGE, message } from '../../enums/messages'; 6 | 7 | export class UseFactory implements FactoryInterface { 8 | getFactory(Service: InjectableConstructor): () => R { 9 | const name = Reflect.getMetadata(METADATA.NAME, Service); 10 | const factory = Reflect.getMetadata(METADATA.VALUE, Service); 11 | 12 | if (factory && typeof factory !== 'function') { 13 | throw assert(false, message(ERROR_MESSAGE.ERROR_USE_FACTORY_TYPE, { name })); 14 | } 15 | 16 | const result = factory(); 17 | 18 | if (!result) { 19 | throw assert(false, ERROR_MESSAGE.ERROR_USE_FACTORY_RETURN); 20 | } 21 | 22 | return () => result; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/di/factory/UseValue.ts: -------------------------------------------------------------------------------- 1 | import { FactoryInterface } from './Factory'; 2 | import { InjectableConstructor } from '../decorators/injectable'; 3 | import { assert } from '../../util/warn'; 4 | import { METADATA } from '../../enums/metadata'; 5 | import { ERROR_MESSAGE } from '../../enums/messages'; 6 | 7 | export class UseValue implements FactoryInterface { 8 | getFactory(Service: InjectableConstructor): () => R { 9 | const value = Reflect.getMetadata(METADATA.VALUE, Service); 10 | 11 | if (value) { 12 | return () => value; 13 | } 14 | 15 | throw assert(false, ERROR_MESSAGE.ERROR_USE_VALUE_RETURN); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/di/injector.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { assert } from '../util/warn'; 3 | import { InjectableConstructor, InjectedObject } from './decorators/injectable'; 4 | import { checkObject } from '../util/object'; 5 | import { ERROR_MESSAGE } from '../enums/messages'; 6 | import { Provider } from './provider'; 7 | 8 | export class Injector { 9 | app: Vue; 10 | services: Map; 11 | 12 | rootServices: Array = []; 13 | 14 | constructor(app: Vue, rootServices) { 15 | Provider.app = app; 16 | 17 | this.app = app; 18 | this.rootServices = rootServices; 19 | 20 | this.services = new Map(); 21 | } 22 | 23 | registerComponent(component: Vue) { 24 | this.provideAllServices(component); 25 | 26 | if (this.rootServices.length) { 27 | this.rootServices.forEach((provider) => { 28 | this.provide(provider, component); 29 | }); 30 | } 31 | } 32 | 33 | get(service: InjectableConstructor): T { 34 | return this.provide(service); 35 | } 36 | 37 | provide( 38 | service: InjectableConstructor, 39 | target: InjectedObject = null, 40 | customName?: string 41 | ) { 42 | if (!this.services.has(service)) { 43 | if (service.prototype.providers) { 44 | this.registerDependencies(service.prototype); 45 | } 46 | 47 | this.services.set(service, new Provider(service)); 48 | } 49 | 50 | const provider = this.services.get(service); 51 | 52 | if (target) { 53 | provider.bindTo(target, customName); 54 | } 55 | 56 | return provider.instance as T; 57 | } 58 | 59 | private provideAllServices(target: InjectedObject) { 60 | if (Object.hasOwnProperty.call(target, 'providers')) { 61 | const { providers } = target; 62 | 63 | if (providers && checkObject(providers)) { 64 | Object.keys(providers).forEach((name) => { 65 | if (providers && Object.hasOwnProperty.call(providers, name)) { 66 | this.provide( 67 | providers[name], 68 | target, 69 | name 70 | ); 71 | } 72 | }); 73 | } else { 74 | throw assert(false, ERROR_MESSAGE.ERROR_PROVIDERS_TYPE); 75 | } 76 | } 77 | } 78 | 79 | private registerDependencies(service: InjectableConstructor) { 80 | this.provideAllServices(service); 81 | 82 | delete service.providers; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/di/provider.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { InjectableConstructor, InjectedObject } from './decorators/injectable'; 3 | import { ServiceBinding } from './bindings/binding'; 4 | import { ServiceFactory } from './factory/Factory'; 5 | import { assert } from '../util/warn'; 6 | 7 | import { METADATA } from '../enums/metadata'; 8 | import { ERROR_MESSAGE } from '../enums/messages'; 9 | 10 | export class Provider { 11 | static app: Vue; 12 | 13 | private factory: () => any = null; 14 | 15 | constructor( 16 | public service: InjectableConstructor 17 | ) { 18 | this.register(); 19 | } 20 | 21 | get instance(): T { 22 | return this.factory(); 23 | } 24 | 25 | bindTo(target: InjectedObject, name?: string): boolean { 26 | return ServiceBinding.bind(target, this.instance, name || this.name); 27 | } 28 | 29 | private get name(): string { 30 | return Reflect.getMetadata(METADATA.NAME, this.service); 31 | } 32 | 33 | private get isService(): boolean { 34 | return !!Reflect.getMetadata(METADATA.SERVICE, this.service); 35 | } 36 | 37 | private register(): any { 38 | if (this.service === Vue) { 39 | this.factory = () => Provider.app; 40 | } 41 | 42 | if (!this.factory && this.isService) { 43 | this.factory = ServiceFactory.make(this.service); 44 | } 45 | 46 | if (this.factory) { 47 | return this.factory; 48 | } 49 | 50 | throw assert(false, ERROR_MESSAGE.ERROR_USE_DECORATOR); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/enums/messages.ts: -------------------------------------------------------------------------------- 1 | export enum ERROR_MESSAGE { 2 | ERROR_TYPE = '[@scandltd/vue-injector]:', 3 | ERROR_INJECTABLE_OPTIONS_CONFLICT = '@injectable can take only one parameter either {names}', 4 | ERROR_BUILD_MESSAGE = 'function "message". Parameters in a string do not match those in array: ', 5 | ERROR_INIT_PLUGIN = 'not installed. Make sure to call `Vue.use(VueInjector)` before creating root instance.', 6 | ERROR_PROVIDERS_TYPE = 'providers are not objects', 7 | ERROR_USE_DECORATOR = 'no decorator Injectable', 8 | ERROR_USE_FACTORY_RETURN = 'useFactory invalid return', 9 | ERROR_USE_VALUE_RETURN = 'invalid useValue', 10 | ERROR_USE_FACTORY_TYPE = '{name} invalid type useFactory: must be \'function\'', 11 | ERROR_EMTY_INJECT_PARAMS = '@inject must get a service as parameter' 12 | } 13 | 14 | export enum WARNING_MESSAGE { 15 | WARNING_000 = 'Wrong service registration. Service name: {name}.\n' 16 | + '@injectable can take only one parameter either useFactory or useValue, but got {options}' 17 | } 18 | 19 | export function message(str: string, arg: Object = {}): string { 20 | let newStr = str; 21 | const spareParameters = Reflect.ownKeys(arg).filter((val) => str.match(new RegExp(`{${String(val)}}`)) === null); 22 | 23 | if (spareParameters.length) { 24 | // eslint-disable-next-line no-console 25 | console.warn(ERROR_MESSAGE.ERROR_BUILD_MESSAGE + spareParameters); 26 | } 27 | 28 | Object.keys(arg).forEach((key) => { 29 | const regex = new RegExp(`{${key}}`); 30 | newStr = str.replace(regex, arg[key]); 31 | }); 32 | 33 | return newStr; 34 | } 35 | -------------------------------------------------------------------------------- /src/enums/metadata.ts: -------------------------------------------------------------------------------- 1 | export const METADATA = { 2 | TYPE: Symbol('inject:type'), 3 | VALUE: Symbol('inject:value'), 4 | NAME: Symbol('inject:name'), 5 | SERVICE: Symbol('inject:service'), 6 | TS_TYPE: 'design:type' 7 | }; 8 | 9 | export enum FACTORY_TYPES { 10 | useFactory = 'useFactory', 11 | useValue = 'useValue' 12 | } 13 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import '../types/vue.options'; 2 | 3 | import Vue, { PluginFunction, PluginObject } from 'vue'; 4 | 5 | import { install } from './install'; 6 | import { assert } from './util/warn'; 7 | import { inBrowser } from './util/dom'; 8 | 9 | import { Injector } from './di/injector'; 10 | 11 | import { Injectable, InjectableConstructor } from './di/decorators/injectable'; 12 | import { Inject } from './di/decorators/inject'; 13 | 14 | import { ERROR_MESSAGE } from './enums/messages'; 15 | 16 | export { 17 | Injectable, 18 | Inject 19 | }; 20 | 21 | export type VueInjectorOptions = { 22 | root?: Array, 23 | store?: any 24 | }; 25 | 26 | export class VueInjector implements PluginObject { 27 | static install: PluginFunction; 28 | static version: string; 29 | 30 | injector: Injector | null; 31 | 32 | private app: Vue | null; 33 | private apps: Array; 34 | 35 | private rootServices: Array = []; 36 | 37 | constructor(options: VueInjectorOptions = {}) { 38 | this.app = null; 39 | this.injector = null; 40 | this.apps = []; 41 | 42 | this.rootServices = options.root || []; 43 | 44 | if (options.store) { 45 | options.store.$injector = this; 46 | } 47 | } 48 | 49 | static get app() { 50 | return this; 51 | } 52 | 53 | get install(): PluginFunction { 54 | return VueInjector.install; 55 | } 56 | 57 | init(app: Vue) { 58 | if (process.env.NODE_ENV !== 'production') { 59 | assert( 60 | (install as any).installed, 61 | ERROR_MESSAGE.ERROR_INIT_PLUGIN 62 | ); 63 | } 64 | 65 | this.apps.push(app); 66 | 67 | // main app already initialized. 68 | if (this.app) { 69 | return; 70 | } 71 | 72 | this.app = app; 73 | this.injector = new Injector(this.app, this.rootServices); 74 | } 75 | 76 | initComponent(component: Vue) { 77 | return this.injector && this.injector.registerComponent(component); 78 | } 79 | 80 | get(Provider: InjectableConstructor): T { 81 | return this.injector && this.injector.get(Provider); 82 | } 83 | } 84 | 85 | VueInjector.install = install; 86 | VueInjector.version = '__VERSION__'; 87 | 88 | if (inBrowser && window.Vue) { 89 | window.Vue.use(VueInjector); 90 | } 91 | -------------------------------------------------------------------------------- /src/install.ts: -------------------------------------------------------------------------------- 1 | import OurVue, { VueConstructor } from 'vue'; 2 | import { assert } from './util/warn'; 3 | 4 | export function install(Vue: VueConstructor) { 5 | if ((install as any).installed) return; 6 | (install as any).installed = true; 7 | 8 | if (OurVue !== Vue) { 9 | throw assert(false, 'Multiple instances of Vue detected'); 10 | } 11 | 12 | const isDef = (v) => v !== undefined; 13 | 14 | if (Vue.$injectorInstalled) return; 15 | Vue.$injectorInstalled = true; 16 | 17 | Vue.mixin({ 18 | beforeCreate() { 19 | if (isDef(this.$options.providers)) { 20 | this.providers = this.$options.providers; 21 | } 22 | 23 | if (isDef(this.$options.injector)) { 24 | Object.defineProperty(this, '$injector', { 25 | configurable: false, 26 | writable: false, 27 | value: Vue.observable(this.$options.injector) 28 | }); 29 | 30 | this.$injector.init(this); 31 | } else { 32 | Object.defineProperty(this, '$injector', { 33 | configurable: false, 34 | writable: false, 35 | value: (this.$parent && this.$parent.$injector) || this 36 | }); 37 | 38 | if (this.$injector && this.$injector.initComponent && typeof this.$injector.initComponent === 'function') { 39 | this.$injector.initComponent(this); 40 | } 41 | } 42 | } 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /src/polyfill.ts: -------------------------------------------------------------------------------- 1 | import 'core-js/features/reflect'; 2 | import 'core-js/features/promise'; 3 | -------------------------------------------------------------------------------- /src/util/decorator.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-proto */ 2 | export function createDecorator( 3 | factory: (target: any, key: string) => void 4 | ): PropertyDecorator { 5 | return (target: any, key: string) => { 6 | const Ctor = typeof target === 'function' 7 | ? target 8 | : target.constructor; 9 | 10 | const descriptor = { 11 | enumerable: true, 12 | configurable: true, 13 | initializer() { 14 | return this.__proto__[key]; 15 | } 16 | }; 17 | 18 | Reflect.defineProperty(target, key, descriptor); 19 | 20 | (Ctor.__decorators__ || (Ctor.__decorators__ = [])).push((options) => factory(options, key)); 21 | 22 | return descriptor; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /src/util/dom.ts: -------------------------------------------------------------------------------- 1 | export const inBrowser = typeof window !== 'undefined'; 2 | -------------------------------------------------------------------------------- /src/util/object.ts: -------------------------------------------------------------------------------- 1 | export function checkObject(obj: any): boolean { 2 | return !Array.isArray(obj) && typeof obj === 'object' && obj !== null; 3 | } 4 | -------------------------------------------------------------------------------- /src/util/warn.ts: -------------------------------------------------------------------------------- 1 | import { ERROR_MESSAGE } from '../enums/messages'; 2 | 3 | export function assert(condition: any, message: string) { 4 | if (!condition) { 5 | throw new Error(`${ERROR_MESSAGE.ERROR_TYPE} ${message}`); 6 | } 7 | } 8 | 9 | export function warn(condition: any, message: string) { 10 | if (process.env.NODE_ENV !== 'production' && !condition) { 11 | // eslint-disable-next-line no-console 12 | if (typeof window.console !== 'undefined') console.warn(`${ERROR_MESSAGE.ERROR_TYPE} ${message}`); 13 | } 14 | } 15 | 16 | export function isError(err: any): boolean { 17 | return Object.prototype.toString.call(err).indexOf('Error') > -1; 18 | } 19 | -------------------------------------------------------------------------------- /test/unit/specs/register-component.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-empty-function */ 2 | /* eslint-disable no-unused-vars */ 3 | /* eslint-disable @typescript-eslint/no-unused-vars */ 4 | /* eslint-disable max-classes-per-file */ 5 | import Vue from 'vue'; 6 | import { VueInjector, Injectable, Inject } from '../../../src/index'; 7 | import { ERROR_MESSAGE } from '../../../src/enums/messages'; 8 | import { METADATA } from '../../../src/enums/metadata'; 9 | 10 | Vue.use(VueInjector); 11 | 12 | describe('root service', () => { 13 | it('register root service', () => { 14 | @Injectable 15 | class Service {} 16 | 17 | const injector = new VueInjector({ root: [Service] }); 18 | 19 | const app = new Vue({ 20 | injector 21 | }); 22 | 23 | app.$forceUpdate(); 24 | 25 | const mockComponent: any = {}; 26 | 27 | injector.injector.registerComponent(mockComponent); 28 | 29 | expect(mockComponent.Service).toEqual(injector.injector.get(Service)); 30 | }); 31 | }); 32 | 33 | describe('register component', () => { 34 | let injector; 35 | let app; 36 | 37 | beforeEach(() => { 38 | injector = new VueInjector(); 39 | 40 | app = new Vue({ 41 | injector 42 | }); 43 | 44 | app.$forceUpdate(); 45 | }); 46 | 47 | it('register one', () => { 48 | @Injectable 49 | class Service {} 50 | 51 | const mockComponent: any = { 52 | providers: { 53 | Service 54 | } 55 | }; 56 | 57 | injector.injector.registerComponent(mockComponent); 58 | 59 | expect(mockComponent.Service).toEqual(injector.injector.get(Service)); 60 | expect(VueInjector).toEqual(VueInjector.app); 61 | }); 62 | 63 | it('register two', () => { 64 | @Injectable 65 | class Service {} 66 | 67 | @Injectable 68 | class ServiceTwo {} 69 | 70 | const mockComponent: any = { 71 | providers: { 72 | Service, 73 | ServiceTwo 74 | } 75 | }; 76 | 77 | injector.injector.registerComponent(mockComponent); 78 | 79 | expect(Reflect.getMetadata(METADATA.NAME, Service)).toEqual('Service'); 80 | expect(Reflect.getMetadata(METADATA.NAME, ServiceTwo)).toEqual('ServiceTwo'); 81 | 82 | expect(injector.injector.services.size).toBe(2); 83 | 84 | expect(mockComponent.Service).toEqual(injector.injector.get(Service)); 85 | expect(mockComponent.ServiceTwo).toEqual(injector.injector.get(ServiceTwo)); 86 | }); 87 | 88 | it('register with VALUE', () => { 89 | @Injectable({ 90 | useValue: 'anyValue' 91 | }) 92 | class Service {} 93 | 94 | const mockComponent: any = { 95 | providers: { 96 | Service 97 | } 98 | }; 99 | 100 | injector.injector.registerComponent(mockComponent); 101 | 102 | expect(Reflect.getMetadata(METADATA.NAME, Service)).toEqual('Service'); 103 | 104 | expect(injector.injector.services.size).toBe(1); 105 | expect(mockComponent.Service).toEqual(injector.injector.get(Service)); 106 | }); 107 | 108 | it('register with import', () => { 109 | @Injectable 110 | class Service {} 111 | 112 | @Injectable 113 | class ServiceTwo { 114 | @Inject(Service) Service; 115 | } 116 | 117 | const mockComponent: any = { 118 | providers: { 119 | Service, 120 | ServiceTwo 121 | } 122 | }; 123 | 124 | injector.injector.registerComponent(mockComponent); 125 | 126 | expect(injector.injector.services.size).toBe(2); 127 | 128 | expect(injector.injector.get(Service) instanceof Service).toBe(true); 129 | expect(Reflect.getMetadata(METADATA.NAME, Service)).toEqual('Service'); 130 | expect(injector.injector.get(ServiceTwo) instanceof ServiceTwo).toBe(true); 131 | expect(Reflect.getMetadata(METADATA.NAME, ServiceTwo)).toEqual('ServiceTwo'); 132 | expect(injector.injector.get(ServiceTwo).Service).toEqual(jasmine.any(Object)); 133 | expect(injector.injector.get(ServiceTwo).Service).toEqual(injector.injector.get(Service)); 134 | 135 | expect(mockComponent.Service instanceof Service).toBe(true); 136 | expect(mockComponent.Service).toEqual(injector.injector.get(Service)); 137 | expect(mockComponent.ServiceTwo instanceof ServiceTwo).toBe(true); 138 | expect(mockComponent.ServiceTwo.Service).toEqual(jasmine.any(Object)); 139 | 140 | expect(mockComponent.Service).toEqual(injector.injector.get(Service)); 141 | expect(mockComponent.ServiceTwo).toEqual(injector.injector.get(ServiceTwo)); 142 | expect(mockComponent.ServiceTwo.Service).toEqual(injector.injector.get(ServiceTwo).Service); 143 | }); 144 | 145 | it('error register', () => { 146 | const mockComponent = { 147 | providers: null 148 | }; 149 | 150 | expect( 151 | () => injector.injector.registerComponent(mockComponent) 152 | ).toThrowError(`${ERROR_MESSAGE.ERROR_TYPE} ${ERROR_MESSAGE.ERROR_PROVIDERS_TYPE}`); 153 | }); 154 | 155 | it('empty register', () => { 156 | const mockComponent: any = {}; 157 | 158 | injector.injector.registerComponent(mockComponent); 159 | 160 | expect(injector.injector.services.size).toBe(0); 161 | expect(mockComponent.length).toBe(undefined); 162 | }); 163 | }); 164 | -------------------------------------------------------------------------------- /test/unit/specs/register-service.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-empty-function */ 2 | /* eslint-disable no-unused-vars */ 3 | /* eslint-disable @typescript-eslint/no-unused-vars */ 4 | /* eslint-disable max-classes-per-file */ 5 | import Vue from 'vue'; 6 | import { VueInjector, Injectable, Inject } from '../../../src/index'; 7 | import { message, ERROR_MESSAGE, WARNING_MESSAGE } from '../../../src/enums/messages'; 8 | import { FACTORY_TYPES, METADATA } from '../../../src/enums/metadata'; 9 | 10 | Vue.use(VueInjector); 11 | 12 | describe('registerComponent service', () => { 13 | let injector: VueInjector; 14 | let app; 15 | 16 | beforeEach(() => { 17 | injector = new VueInjector(); 18 | 19 | app = new Vue({ 20 | injector 21 | }); 22 | }); 23 | 24 | it('register one', () => { 25 | @Injectable 26 | class Service {} 27 | 28 | const service = injector.injector.provide(Service); 29 | 30 | expect(injector.injector.services.size).toBe(1); 31 | 32 | expect(service).toEqual(injector.injector.get(Service)); 33 | expect(Reflect.getMetadata(METADATA.NAME, Service)).toEqual('Service'); 34 | }); 35 | 36 | it('register two', () => { 37 | @Injectable 38 | class Service {} 39 | 40 | @Injectable 41 | class ServiceTwo {} 42 | 43 | const service = injector.injector.provide(Service); 44 | const serviceTwo = injector.injector.provide(ServiceTwo); 45 | 46 | expect(injector.injector.services.size).toBe(2); 47 | 48 | expect(Reflect.getMetadata(METADATA.NAME, Service)).toEqual('Service'); 49 | expect(Reflect.getMetadata(METADATA.NAME, ServiceTwo)).toEqual('ServiceTwo'); 50 | 51 | expect(service).toEqual(injector.injector.get(Service)); 52 | expect(serviceTwo).toEqual(injector.injector.get(ServiceTwo)); 53 | }); 54 | 55 | it('register with import', () => { 56 | @Injectable 57 | class Service { 58 | count = 0; 59 | } 60 | 61 | @Injectable 62 | class ServiceTwo { 63 | @Inject(Service) Service; 64 | } 65 | 66 | @Injectable 67 | class ServiceThree { 68 | @Inject(Service) Service; 69 | } 70 | 71 | const serviceTwo = injector.injector.provide(ServiceTwo); 72 | const serviceThree = injector.injector.provide(ServiceThree); 73 | 74 | serviceTwo.Service.count += 1; 75 | serviceThree.Service.count += 1; 76 | 77 | expect(injector.injector.services.size).toBe(3); 78 | expect(Reflect.getMetadata(METADATA.NAME, Service)).toEqual('Service'); 79 | expect(Reflect.getMetadata(METADATA.NAME, ServiceTwo)).toEqual('ServiceTwo'); 80 | expect(injector.injector.get(ServiceTwo).Service).toEqual(jasmine.any(Object)); 81 | expect(injector.injector.get(ServiceTwo).Service).toEqual(injector.injector.get(Service)); 82 | 83 | 84 | expect(serviceTwo).toEqual(injector.injector.get(ServiceTwo)); 85 | expect(serviceTwo).toEqual(injector.injector.get(ServiceTwo)); 86 | expect(serviceTwo.Service).toEqual(injector.injector.get(Service)); 87 | 88 | expect(injector.injector.get(Service).count).toEqual(2); 89 | }); 90 | 91 | 92 | it('register with type', () => { 93 | @Injectable 94 | class Service { 95 | count = 0; 96 | } 97 | 98 | @Injectable 99 | class ServiceTwo { 100 | @Inject Service: Service; 101 | } 102 | 103 | @Injectable 104 | class ServiceThree { 105 | @Inject Service: Service; 106 | } 107 | 108 | const serviceTwo = injector.injector.provide(ServiceTwo); 109 | const serviceThree = injector.injector.provide(ServiceThree); 110 | 111 | serviceTwo.Service.count += 1; 112 | serviceThree.Service.count += 1; 113 | 114 | expect(injector.injector.services.size).toBe(3); 115 | expect(Reflect.getMetadata(METADATA.NAME, Service)).toEqual('Service'); 116 | expect(Reflect.getMetadata(METADATA.NAME, ServiceTwo)).toEqual('ServiceTwo'); 117 | expect(injector.injector.get(ServiceTwo).Service).toEqual(jasmine.any(Object)); 118 | expect(injector.injector.get(ServiceTwo).Service).toEqual(injector.injector.get(Service)); 119 | 120 | 121 | expect(serviceTwo).toEqual(injector.injector.get(ServiceTwo)); 122 | expect(serviceTwo).toEqual(injector.injector.get(ServiceTwo)); 123 | expect(serviceTwo.Service).toEqual(injector.injector.get(Service)); 124 | 125 | expect(injector.injector.get(Service).count).toEqual(2); 126 | }); 127 | 128 | it('register with FACTORY', () => { 129 | class Factory { 130 | a = 0; 131 | 132 | constructor() { 133 | this.a += 1; 134 | } 135 | 136 | add() { 137 | this.a += 1; 138 | } 139 | 140 | get type() { 141 | return 'FACTORY'; 142 | } 143 | } 144 | 145 | const factory = () => new Factory(); 146 | 147 | @Injectable({ 148 | useFactory: factory 149 | }) 150 | class Service extends Factory {} 151 | 152 | 153 | const service = injector.injector.provide(Service); 154 | 155 | expect(injector.injector.services.size).toBe(1); 156 | 157 | const a = injector.injector.get(Service); 158 | 159 | a.add(); 160 | a.add(); 161 | 162 | expect(injector.injector.get(Service).type).toEqual('FACTORY'); 163 | expect(service.type).toEqual('FACTORY'); 164 | 165 | expect(factory().a).toEqual(1); 166 | expect(injector.injector.get(Service).a).toEqual(3); 167 | 168 | injector.injector.get(Service).add(); 169 | injector.injector.get(Service).add(); 170 | 171 | expect(injector.injector.get(Service).a).toEqual(5); 172 | }); 173 | 174 | it('register error FACTORY', () => { 175 | // @ts-ignore: Unreachable code error 176 | @Injectable({ 177 | useFactory: {} 178 | }) 179 | class Service {} 180 | 181 | expect( 182 | () => injector.injector.provide(Service) 183 | ).toThrowError(message(`${ERROR_MESSAGE.ERROR_TYPE} ${ERROR_MESSAGE.ERROR_USE_FACTORY_TYPE}`, { name: 'Service' })); 184 | }); 185 | 186 | it('register with VALUE', () => { 187 | @Injectable({ 188 | useValue: 'anyValue' 189 | }) 190 | class Service {} 191 | 192 | injector.injector.provide(Service); 193 | 194 | expect(injector.injector.services.size).toBe(1); 195 | expect(Reflect.getMetadata(METADATA.NAME, Service)).toEqual('Service'); 196 | expect(injector.injector.get(Service)).toEqual('anyValue'); 197 | }); 198 | 199 | it('register error VALUE', () => { 200 | @Injectable({ 201 | useValue: null 202 | }) 203 | class Service {} 204 | 205 | expect( 206 | () => injector.injector.provide(Service) 207 | ).toThrowError(`${ERROR_MESSAGE.ERROR_TYPE} ${ERROR_MESSAGE.ERROR_USE_VALUE_RETURN}`); 208 | }); 209 | 210 | it('register with VALUE and FACTORY', () => { 211 | const options = { 212 | useValue: 'anyValue', 213 | useFactory() {} 214 | }; 215 | 216 | const whitelist = Reflect.ownKeys(FACTORY_TYPES); 217 | 218 | const error = message( 219 | ERROR_MESSAGE.ERROR_INJECTABLE_OPTIONS_CONFLICT, 220 | { names: JSON.stringify(whitelist) } 221 | ); 222 | 223 | expect( 224 | () => { 225 | @Injectable(options) 226 | class Service {} 227 | } 228 | ).toThrowError(`${ERROR_MESSAGE.ERROR_TYPE} ${error}`); 229 | }); 230 | 231 | it('register with random keys', () => { 232 | jest.spyOn(console, 'warn'); 233 | 234 | const options = { 235 | anyKey: 'anyValue' 236 | }; 237 | // @ts-ignore: Unreachable code error 238 | @Injectable(options) 239 | class Service {} 240 | 241 | const msg = message(WARNING_MESSAGE.WARNING_000, { name: 'Service', options: JSON.stringify(options) }); 242 | 243 | expect(console.warn) 244 | .toHaveBeenCalledWith(`${ERROR_MESSAGE.ERROR_TYPE} ${msg}`); 245 | }); 246 | 247 | 248 | it('register with Vue', () => { 249 | @Injectable 250 | class Service { 251 | @Inject(Vue) vm; 252 | } 253 | 254 | const service = injector.injector.provide(Service); 255 | 256 | expect(injector.injector.services.size).toBe(2); 257 | 258 | expect(service).toEqual(injector.injector.get(Service)); 259 | expect(Reflect.getMetadata(METADATA.NAME, Service)).toEqual('Service'); 260 | 261 | expect(service.vm).toEqual(app); 262 | }); 263 | 264 | it('FACTORY invalid return', () => { 265 | class Factory { 266 | constructor(public vm) {} 267 | } 268 | // @ts-ignore: Unreachable code error 269 | @Injectable({ 270 | useFactory: (vm) => { 271 | new Factory(vm); 272 | } 273 | }) 274 | class Service {} 275 | 276 | expect( 277 | () => injector.injector.provide(Service) 278 | ).toThrowError(`${ERROR_MESSAGE.ERROR_TYPE} ${ERROR_MESSAGE.ERROR_USE_FACTORY_RETURN}`); 279 | }); 280 | 281 | 282 | it('get service before register', () => { 283 | @Injectable 284 | class Service {} 285 | 286 | const service = injector.get(Service); 287 | 288 | expect(injector.injector.services.size).toBe(1); 289 | expect(Reflect.getMetadata(METADATA.NAME, Service)).toEqual('Service'); 290 | expect(service).toEqual(injector.injector.get(Service)); 291 | }); 292 | 293 | it('get service after register', () => { 294 | @Injectable 295 | class Service {} 296 | 297 | const service = injector.injector.provide(Service); 298 | const injectorService = injector.get(Service); 299 | 300 | expect(Reflect.getMetadata(METADATA.NAME, Service)).toEqual('Service'); 301 | expect(service).toEqual(injector.injector.get(Service)); 302 | 303 | expect(injector.injector.services.size).toBe(1); 304 | expect(injectorService).toEqual(injector.injector.get(Service)); 305 | }); 306 | 307 | it('register not Injectable', () => { 308 | class Service {} 309 | 310 | @Injectable 311 | class ServiceTwo { 312 | @Inject(Service) Service; 313 | } 314 | 315 | expect( 316 | () => injector.injector.provide(Service) 317 | ).toThrowError(`${ERROR_MESSAGE.ERROR_TYPE} ${ERROR_MESSAGE.ERROR_USE_DECORATOR}`); 318 | expect( 319 | () => injector.injector.provide(ServiceTwo) 320 | ).toThrowError(`${ERROR_MESSAGE.ERROR_TYPE} ${ERROR_MESSAGE.ERROR_USE_DECORATOR}`); 321 | }); 322 | }); 323 | -------------------------------------------------------------------------------- /test/unit/specs/warm.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert, isError, warn } from '../../../src/util/warn'; 2 | import { ERROR_MESSAGE, message } from '../../../src/enums/messages'; 3 | 4 | describe('warn', () => { 5 | it('assert false', () => { 6 | expect(() => assert(false, 'error text')).toThrowError(`${ERROR_MESSAGE.ERROR_TYPE} error text`); 7 | }); 8 | 9 | it('assert true', () => { 10 | expect(() => assert(true, 'error text')).not.toThrow(); 11 | }); 12 | 13 | it('isError true', () => { 14 | const error = new Error(); 15 | 16 | expect(isError(error)).toBe(true); 17 | }); 18 | 19 | it('isError false', () => { 20 | const error = {}; 21 | 22 | expect(isError(error)).toBe(false); 23 | }); 24 | 25 | describe('console', () => { 26 | beforeEach(() => { 27 | spyOn(console, 'warn'); 28 | }); 29 | 30 | it('error message', () => { 31 | message('', { name: 'name' }); 32 | 33 | expect(console.warn).toHaveBeenCalledTimes(1); 34 | expect(console.warn).toHaveBeenCalledWith(`${ERROR_MESSAGE.ERROR_BUILD_MESSAGE}name`); 35 | }); 36 | 37 | it('empty message', () => { 38 | expect(message('test')).toEqual('test'); 39 | }); 40 | 41 | it('warn true', () => { 42 | warn(true, 'error text'); 43 | expect(console.warn).toHaveBeenCalledTimes(0); 44 | }); 45 | 46 | it('warn false', () => { 47 | warn(false, 'error text'); 48 | expect(console.warn).toHaveBeenCalledWith(`${ERROR_MESSAGE.ERROR_TYPE} error text`); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "es2017.object", 5 | "es6", 6 | "es5", 7 | "dom", 8 | "es2015.promise", 9 | "es2015.core" 10 | ], 11 | "target": "es5", 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "esModuleInterop": true, 15 | "sourceMap": true, 16 | "allowSyntheticDefaultImports": true, 17 | "experimentalDecorators": true, 18 | "emitDecoratorMetadata": true, 19 | "declaration": true, 20 | "baseUrl": ".", 21 | "rootDirs": ["./"], 22 | "paths": { 23 | "@scandltd/vue-injector": ["./src"] 24 | }, 25 | "typeRoots": [ 26 | "node_modules/@types", 27 | "types" 28 | ], 29 | "types": [ 30 | "node", 31 | "jest", 32 | "core-js", 33 | "reflect-metadata" 34 | ] 35 | }, 36 | "exclude": [ 37 | "node_modules" 38 | ], 39 | "include": [ 40 | "**/*.ts", 41 | "**/*.vue" 42 | ] 43 | } -------------------------------------------------------------------------------- /types/global.d.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | declare global { 4 | interface Window { 5 | Vue: typeof Vue; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /types/vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue'; 3 | 4 | export default Vue; 5 | } 6 | -------------------------------------------------------------------------------- /types/vue.options.d.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { VueInjector } from '../src/index'; 3 | import { InjectableConstructor } from '../src/di/decorators/injectable'; 4 | declare module 'vue/types/vue' { 5 | interface Vue { 6 | readonly $injector: VueInjector; 7 | $injectorInstalled: boolean; 8 | providers: { 9 | [key: string]: InjectableConstructor; 10 | }; 11 | } 12 | interface VueConstructor { 13 | $injectorInstalled: boolean; 14 | } 15 | } 16 | declare module 'vue/types/options' { 17 | interface ComponentOptions { 18 | readonly injector?: VueInjector; 19 | providers?: { 20 | [key: string]: InjectableConstructor; 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /types/vue.options.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { VueInjector } from '../src/index'; 3 | import { InjectableConstructor } from '../src/di/decorators/injectable'; 4 | 5 | declare module 'vue/types/vue' { 6 | interface Vue { 7 | readonly $injector: VueInjector; 8 | $injectorInstalled: boolean; 9 | providers: { [key: string]: InjectableConstructor }; 10 | } 11 | interface VueConstructor { 12 | $injectorInstalled: boolean; 13 | } 14 | } 15 | 16 | declare module 'vue/types/options' { 17 | interface ComponentOptions { 18 | readonly injector?: VueInjector; 19 | providers?: { [key: string]: InjectableConstructor }; 20 | } 21 | } 22 | --------------------------------------------------------------------------------