├── .npmignore ├── .gitignore ├── .vscode └── settings.json ├── logo.png ├── generator ├── template-ts │ ├── src │ │ ├── shims-vue.d.ts │ │ ├── options │ │ │ ├── index.ts │ │ │ ├── App │ │ │ │ └── App.vue │ │ │ └── index.html │ │ ├── popup │ │ │ ├── index.ts │ │ │ ├── App │ │ │ │ └── App.vue │ │ │ └── index.html │ │ └── shims-tsx.d.ts │ └── vue.config.js ├── template-js │ ├── src │ │ ├── options │ │ │ ├── index.js │ │ │ ├── App │ │ │ │ └── App.vue │ │ │ └── index.html │ │ └── popup │ │ │ ├── index.js │ │ │ ├── App │ │ │ └── App.vue │ │ │ └── index.html │ └── vue.config.js ├── index.js └── generate │ └── manifest.js ├── prod.sh ├── index.js ├── auto.sh ├── prompts.js ├── package.json ├── LICENSE └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | test-dir 2 | test-prod -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test-dir 2 | test-prod 3 | node_modules 4 | *.log -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/superoo7/vue-cli-plugin-chrome-ext/HEAD/logo.png -------------------------------------------------------------------------------- /generator/template-ts/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | -------------------------------------------------------------------------------- /prod.sh: -------------------------------------------------------------------------------- 1 | # /bin/sh 2 | 3 | [ -e test-prod ] && rm -rf test-prod 4 | vue create test-prod -d 5 | ( 6 | cd test-prod && 7 | vue add chrome-ext 8 | ) 9 | -------------------------------------------------------------------------------- /generator/template-js/src/options/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import AppComponent from "./App/App.vue"; 3 | 4 | Vue.component("app-component", AppComponent); 5 | 6 | new Vue({ 7 | el: "#app", 8 | render: createElement => { 9 | return createElement(AppComponent); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /generator/template-js/src/popup/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import AppComponent from "./App/App.vue"; 3 | 4 | Vue.component("app-component", AppComponent); 5 | 6 | new Vue({ 7 | el: "#app", 8 | render: createElement => { 9 | return createElement(AppComponent); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /generator/template-ts/src/options/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import AppComponent from "./App/App.vue"; 3 | 4 | Vue.component("app-component", AppComponent); 5 | 6 | new Vue({ 7 | el: "#app", 8 | render: createElement => { 9 | return createElement(AppComponent); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /generator/template-ts/src/popup/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import AppComponent from "./App/App.vue"; 3 | 4 | Vue.component("app-component", AppComponent); 5 | 6 | new Vue({ 7 | el: "#app", 8 | render: createElement => { 9 | return createElement(AppComponent); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /generator/template-ts/src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /generator/template-js/src/options/App/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /generator/template-js/src/popup/App/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /generator/template-ts/src/options/App/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /generator/template-ts/src/popup/App/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = (api, opts) => { 2 | api.chainWebpack(webpackConfig => { 3 | // remove split chunks for chrome extension, make sure everything in a file 4 | webpackConfig.optimization.delete("splitChunks"); 5 | }); 6 | 7 | api.configureWebpack(webpackConfig => {}); 8 | 9 | api.registerCommand("build-watch", (...args) => { 10 | api.configureWebpack(webpackConfig => { 11 | webpackConfig.watch = true; 12 | }); 13 | api.service.run("build", ...args); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /auto.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | # https://stackoverflow.com/questions/42308064/check-for-flag-passed-to-a-bash-script-and-process-value 4 | while getopts ":r" opt; do 5 | case $opt in 6 | r) 7 | echo "resetting test-dir" && 8 | [ -e test-dir ] && rm -rf test-dir && 9 | vue create test-dir -d 10 | ;; 11 | esac 12 | done 13 | 14 | [ ! -e test-dir ] && 15 | vue create test-dir -d 16 | 17 | ( 18 | cd test-dir && 19 | # npm uninstall ../ 20 | npm install --save-dev ../ && 21 | vue invoke vue-cli-plugin-chrome-ext --name "test 1" --description "test 2" --version_no "1.0.0" --popup "yes" --script "js" 22 | ) -------------------------------------------------------------------------------- /generator/template-js/src/options/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Options 8 | 9 | 10 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /generator/template-js/src/popup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Options 8 | 9 | 10 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /generator/template-ts/src/options/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Options 8 | 9 | 10 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /generator/template-ts/src/popup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Options 8 | 9 | 10 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /prompts.js: -------------------------------------------------------------------------------- 1 | module.exports = [{ 2 | name: "name", 3 | type: "input", 4 | message: "Name of the Chrome Extension?", 5 | default: "chrome-ext" 6 | }, 7 | { 8 | name: "description", 9 | type: "input", 10 | message: "Description for the Chrome Extension?", 11 | default: "chrome extension" 12 | }, 13 | { 14 | name: "version_no", 15 | type: "input", 16 | message: "Version for the Chrome Extension?", 17 | default: "0.0.1" 18 | }, 19 | { 20 | name: "script", 21 | type: "list", 22 | message: "javascript or typescript?", 23 | choices: ["js", "ts"], 24 | default: "js" 25 | } 26 | ]; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-cli-plugin-chrome-ext", 3 | "version": "0.0.5", 4 | "description": "generate chrome extension with vue.js", 5 | "author": "superoo7 ", 6 | "scripts": {}, 7 | "dependencies": {}, 8 | "devDependencies": {}, 9 | "bugs": { 10 | "url": "https://github.com/superoo7/vue-cli-plugin-chrome-ext/issues" 11 | }, 12 | "homepage": "https://github.com/superoo7/vue-cli-plugin-chrome-ext#readme", 13 | "keywords": [ 14 | "vue", 15 | "vue-cli", 16 | "chrome", 17 | "chrome-extension" 18 | ], 19 | "license": "MIT", 20 | "main": "index.js", 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/superoo7/vue-cli-plugin-chrome-ext.git" 24 | } 25 | } -------------------------------------------------------------------------------- /generator/index.js: -------------------------------------------------------------------------------- 1 | const generateManifest = require("./generate/manifest"); 2 | const path = require("path"); 3 | 4 | module.exports = (api, options, rootOptions) => { 5 | const ext = options.script; 6 | 7 | // create file 8 | api.render(`./template-${ext}`); 9 | 10 | const extPkg = { 11 | scripts: { 12 | "build-watch": "vue-cli-service build-watch" 13 | }, 14 | devDependencies: { 15 | "copy-webpack-plugin": "^4.6.0" 16 | } 17 | }; 18 | if (ext === "ts") { 19 | extPkg.devDependencies = { 20 | ...extPkg.devDependencies, 21 | "@types/chrome": "^0.0.75" 22 | }; 23 | } 24 | api.extendPackage(extPkg); 25 | 26 | api.onCreateComplete(() => { 27 | // add manifest.json to src file 28 | const manifestPath = api.resolve("./src"); 29 | generateManifest(options, manifestPath); 30 | }); 31 | }; -------------------------------------------------------------------------------- /generator/template-js/vue.config.js: -------------------------------------------------------------------------------- 1 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 2 | const path = require("path"); 3 | 4 | // Generate pages object 5 | const pagesObj = {}; 6 | 7 | const chromeName = ["popup", "options"]; 8 | 9 | chromeName.forEach(name => { 10 | pagesObj[name] = { 11 | entry: `src/${name}/index.js`, 12 | template: "public/index.html", 13 | filename: `${name}.html` 14 | }; 15 | }); 16 | 17 | const plugins = 18 | process.env.NODE_ENV === "production" 19 | ? [ 20 | { 21 | from: path.resolve("src/manifest.production.json"), 22 | to: `${path.resolve("dist")}/manifest.json` 23 | } 24 | ] 25 | : [ 26 | { 27 | from: path.resolve("src/manifest.development.json"), 28 | to: `${path.resolve("dist")}/manifest.json` 29 | } 30 | ]; 31 | 32 | module.exports = { 33 | pages: pagesObj, 34 | configureWebpack: { 35 | plugins: [CopyWebpackPlugin(plugins)] 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /generator/template-ts/vue.config.js: -------------------------------------------------------------------------------- 1 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 2 | const path = require("path"); 3 | 4 | // Generate pages object 5 | const pagesObj = {}; 6 | 7 | const chromeName = ["popup", "options"]; 8 | 9 | chromeName.forEach(name => { 10 | pagesObj[name] = { 11 | entry: `src/${name}/index.ts`, 12 | template: "public/index.html", 13 | filename: `${name}.html` 14 | }; 15 | }); 16 | 17 | const plugins = 18 | process.env.NODE_ENV === "production" 19 | ? [ 20 | { 21 | from: path.resolve("src/manifest.production.json"), 22 | to: `${path.resolve("dist")}/manifest.json` 23 | } 24 | ] 25 | : [ 26 | { 27 | from: path.resolve("src/manifest.development.json"), 28 | to: `${path.resolve("dist")}/manifest.json` 29 | } 30 | ]; 31 | 32 | module.exports = { 33 | pages: pagesObj, 34 | configureWebpack: { 35 | plugins: [CopyWebpackPlugin(plugins)] 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /generator/generate/manifest.js: -------------------------------------------------------------------------------- 1 | const generateManifest = (options, manifestPath) => { 2 | const fs = require("fs"); 3 | const { version_no: version, description, name } = options; 4 | const manifestJson = { 5 | manifest_version: 2, 6 | name, 7 | description, 8 | version, 9 | options_page: "options.html", 10 | browser_action: { 11 | default_popup: "popup.html" 12 | } 13 | }; 14 | 15 | // Production build of manifest.json 16 | fs.writeFileSync( 17 | `${manifestPath}/manifest.production.json`, 18 | JSON.stringify(manifestJson, null, 4), 19 | { 20 | encoding: "utf-8" 21 | } 22 | ); 23 | // Development build of manifest.json 24 | manifestJson["content_security_policy"] = 25 | "script-src 'self' 'unsafe-eval'; object-src 'self'"; 26 | fs.writeFileSync( 27 | `${manifestPath}/manifest.development.json`, 28 | JSON.stringify(manifestJson, null, 4), 29 | { 30 | encoding: "utf-8" 31 | } 32 | ); 33 | }; 34 | 35 | module.exports = generateManifest; 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Lai Weng Han 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-cli-plugin-chrome-ext 2 | 3 | [![npm version](https://badge.fury.io/js/vue-cli-plugin-chrome-ext.svg)](https://www.npmjs.com/package/vue-cli-plugin-chrome-ext) 4 | 5 | Start a chrome extension project with Vue-CLI with ease! 6 | 7 | 8 | 9 | _Logo from FontAwesome_ 10 | 11 | ## Installation 12 | 13 | This plugin is meant for using new project for chrome extensions. Tested on default project of Vue, Vue with TypeScript 14 | 15 | ### For TypeScript 16 | 17 | - Create a new project with `vue create test-project`, and select typescript without class-style component syntax 18 | - Then, add this plugin with `vue add chrome-ext`. 19 | - Clean up the repo by removing `src/main.ts`, `src/components`, `public/favicon.ico` and `public/index.html` 20 | 21 | ### For JavaScript 22 | 23 | - Create a new project with `vue create test-project`. 24 | - Then, add this plugin with `vue add chrome-ext`. 25 | - Clean up the repo by removing `src/main.js`, `src/components`, `public/favicon.ico` and `public/index.html` 26 | 27 | ### Run Development mode and Production 28 | 29 | - Run development mode with `npm run build-watch` and a `dist` file will be generated. Install [Extension Reloader](https://chrome.google.com/webstore/detail/extensions-reloader/fimgfedafeadlieiabdeeaodndnlbhid) to reload chrome extensions easily everytime you reload. (take note that when u change manifest.json file, it will not automatically load, you need to remove and add the chrome extensions) 30 | - Build for production `npm run build` and zip it and deploy onto chrome store. 31 | 32 | ## Current feature 33 | 34 | - Generate manifest.json 35 | - Generate popup.html 36 | - Generate options.html 37 | - Emit file out 38 | - Support TypeScript (only generated with `vue add typescript`) 39 | 40 | ## TODO 41 | 42 | ### High Priority 43 | 44 | - Add background script 45 | - Make options to generate certain files 46 | 47 | ### Medium Priority 48 | 49 | - Move over with this [template](https://github.com/posva/vue-plugin-template) 50 | - Clean up src and public file. 51 | 52 | ## Development 53 | 54 | ### Testing 55 | 56 | #### Development 57 | 58 | Currently, testing is done manually with the file `./auto.sh`, by passing `-r` flag, it will delete the initial file generated. 59 | 60 | #### Production 61 | 62 | Test production code in npm as well with `./prod.sh`. 63 | 64 | ### prompts.js 65 | 66 | Vue CLI prompt is based on [inquirer.js](https://github.com/SBoudrias/Inquirer.js) api. 67 | 68 | ## Resources 69 | 70 | - https://itnext.io/how-to-build-a-simple-vue-cli-plugin-a2e1323de1a0 71 | 72 | ## Credit 73 | 74 | - https://github.com/zwenza/vue-cli-plugin-build-watch 75 | - https://github.com/RequireSun/vue-cli-plugin-chrome-extension 76 | 77 | ## License 78 | 79 | MIT 80 | --------------------------------------------------------------------------------