├── .history ├── src │ ├── backgroundts_20200505175101.ts │ ├── store │ │ ├── index_20200427165410.js │ │ ├── index_20200506103507.js │ │ └── index_20200506103510.js │ ├── background_20200507105557.js │ ├── background_20200504171805.js │ ├── background_20200505175106.js │ ├── background_20200505175256.js │ ├── backgroundts_20200505175746.ts │ └── popup │ │ ├── App_20200429172340.vue │ │ ├── App_20200507104748.vue │ │ ├── App_20200507105625.vue │ │ ├── App_20200507095248.vue │ │ ├── App_20200507101946.vue │ │ ├── App_20200507103021.vue │ │ ├── App_20200507103254.vue │ │ ├── App_20200507101751.vue │ │ ├── App_20200507101901.vue │ │ ├── App_20200507102024.vue │ │ ├── App_20200507102118.vue │ │ ├── App_20200507102257.vue │ │ └── App_20200507102320.vue ├── .eslintrc_20200507105541.js ├── .eslintrc_20200507105514.js ├── .eslintrc_20200507105454.js ├── .eslintrc_20200428103401.js ├── .eslintrc_20200507105239.js ├── .eslintrc_20200507105304.js ├── tailwind.config_20200428171145.js ├── tailwind.config_20200507102900.js └── tailwind.config_20200507103019.js ├── src ├── icons │ ├── .DS_Store │ ├── iles.png │ ├── gridsome.png │ ├── icon-128.png │ ├── icon-16.png │ ├── icon-48.png │ ├── nuxtjs.png │ ├── quasar.png │ ├── vuepress.png │ ├── vitepress.png │ ├── icon-grey-128.png │ ├── icon-grey-16.png │ ├── icon-grey-48.png │ └── vuestorefront.png ├── fonts │ └── PTRootUI │ │ ├── PTRootUI-Bold.woff │ │ ├── PTRootUI-Bold.woff2 │ │ ├── PTRootUI-Regular.woff │ │ └── PTRootUI-Regular.woff2 ├── images │ ├── refresh.svg │ ├── external-link.svg │ ├── twitter.svg │ ├── github.svg │ ├── info.svg │ ├── link.svg │ ├── modules.svg │ ├── plugins.svg │ └── logo.svg ├── popup │ ├── popup.js │ ├── popup.css │ └── popup.html ├── background │ ├── TabsStateService.js │ └── index.js ├── utils.js ├── shared │ └── CacheService.js ├── tailwind.css ├── components │ ├── ExploreDataItem.vue │ └── AppButton.vue ├── content.js └── injected.js ├── .gitignore ├── postcss.config.js ├── .editorconfig ├── .babelrc ├── .eslintrc.js ├── firefox └── manifest.json ├── chrome └── manifest.json ├── scripts └── build-zip.js ├── tailwind.config.js ├── package.json ├── README.md ├── webpack.config.js └── CHANGELOG.md /.history/src/backgroundts_20200505175101.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/.DS_Store -------------------------------------------------------------------------------- /src/icons/iles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/iles.png -------------------------------------------------------------------------------- /src/icons/gridsome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/gridsome.png -------------------------------------------------------------------------------- /src/icons/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/icon-128.png -------------------------------------------------------------------------------- /src/icons/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/icon-16.png -------------------------------------------------------------------------------- /src/icons/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/icon-48.png -------------------------------------------------------------------------------- /src/icons/nuxtjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/nuxtjs.png -------------------------------------------------------------------------------- /src/icons/quasar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/quasar.png -------------------------------------------------------------------------------- /src/icons/vuepress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/vuepress.png -------------------------------------------------------------------------------- /src/icons/vitepress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/vitepress.png -------------------------------------------------------------------------------- /src/icons/icon-grey-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/icon-grey-128.png -------------------------------------------------------------------------------- /src/icons/icon-grey-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/icon-grey-16.png -------------------------------------------------------------------------------- /src/icons/icon-grey-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/icon-grey-48.png -------------------------------------------------------------------------------- /src/icons/vuestorefront.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/icons/vuestorefront.png -------------------------------------------------------------------------------- /src/fonts/PTRootUI/PTRootUI-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/fonts/PTRootUI/PTRootUI-Bold.woff -------------------------------------------------------------------------------- /src/fonts/PTRootUI/PTRootUI-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/fonts/PTRootUI/PTRootUI-Bold.woff2 -------------------------------------------------------------------------------- /src/fonts/PTRootUI/PTRootUI-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/fonts/PTRootUI/PTRootUI-Regular.woff -------------------------------------------------------------------------------- /src/fonts/PTRootUI/PTRootUI-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtlabs/vue-telescope-extensions/HEAD/src/fonts/PTRootUI/PTRootUI-Regular.woff2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /*.log 3 | /dist 4 | /dist-zip 5 | /dist-firefox 6 | /dist-zip-firefox 7 | coverage 8 | .env 9 | .DS_Store 10 | .nuxt 11 | .vscode -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const tailwindcss = require('tailwindcss') 2 | 3 | module.exports = { 4 | plugins: [ 5 | tailwindcss('./tailwind.config.js'), 6 | require('autoprefixer') 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.history/.eslintrc_20200507105541.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true 6 | }, 7 | parserOptions: { 8 | parser: 'babel-eslint' 9 | }, 10 | // add your custom rules here 11 | rules: { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/images/refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = space 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /src/popup/popup.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | global.browser = require('webextension-polyfill') 5 | Vue.prototype.$browser = global.browser 6 | 7 | /* eslint-disable no-new */ 8 | new Vue({ 9 | el: '#app', 10 | render: h => h(App) 11 | }) 12 | -------------------------------------------------------------------------------- /src/popup/popup.css: -------------------------------------------------------------------------------- 1 | @import "../tailwind.css"; 2 | 3 | @keyframes spin{ 4 | 0% { 5 | transform:rotate(0deg); 6 | } to { 7 | transform:rotate(1turn); 8 | } 9 | } 10 | 11 | .animate-spin { 12 | -webkit-animation: spin 1s linear infinite; 13 | animation: spin 1s linear infinite; 14 | } -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-optional-chaining" 4 | ], 5 | "presets": [ 6 | ["@babel/preset-env", { 7 | "useBuiltIns": "usage", 8 | "corejs": 3, 9 | "targets": { 10 | // https://jamie.build/last-2-versions 11 | "browsers": ["> 0.25%", "not ie 11", "not op_mini all"] 12 | } 13 | }] 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/images/external-link.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/images/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/popup/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vue Telescope 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.history/.eslintrc_20200507105514.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true 6 | }, 7 | parserOptions: { 8 | parser: 'babel-eslint' 9 | }, 10 | extends: [ 11 | 'prettier', 12 | 'prettier/vue', 13 | 'plugin:prettier/recommended', 14 | ], 15 | plugins: [ 16 | 'prettier' 17 | ], 18 | // add your custom rules here 19 | rules: { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true 5 | }, 6 | extends: [ 7 | 'plugin:vue/essential', 8 | 'standard' 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly' 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 2018, 16 | sourceType: 'module' 17 | }, 18 | plugins: [ 19 | 'vue' 20 | ], 21 | rules: { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.history/.eslintrc_20200507105454.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true 6 | }, 7 | parserOptions: { 8 | parser: 'babel-eslint' 9 | }, 10 | extends: [ 11 | '@nuxtjs', 12 | 'prettier', 13 | 'prettier/vue', 14 | 'plugin:prettier/recommended', 15 | 'plugin:nuxt/recommended' 16 | ], 17 | plugins: [ 18 | 'prettier' 19 | ], 20 | // add your custom rules here 21 | rules: { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/background/TabsStateService.js: -------------------------------------------------------------------------------- 1 | import CacheService from '../shared/CacheService' 2 | 3 | // persistent state storage 4 | export default class TabsStorage extends CacheService { 5 | get key () { 6 | return 'tabs' 7 | } 8 | 9 | async get () { 10 | const cache = await super.get() 11 | return cache || {} 12 | } 13 | 14 | async updateData (tabId, state) { 15 | const cache = await this.get() 16 | return this.set({ ...cache, [tabId]: state }) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | export const IS_FIREFOX = navigator.userAgent.indexOf('Firefox') >= 0 2 | export const IS_CHROME = /Chrome/i.test(navigator.userAgent) 3 | export function getChromeVersion () { 4 | const ua = navigator.userAgent 5 | const match = ua.match(/Chrome\/(\d+)/) 6 | const version = match ? parseInt(match[1], 10) : null 7 | return version 8 | } 9 | 10 | const chromeVersion = getChromeVersion() 11 | export const isSupportExecutionVersion = chromeVersion && chromeVersion >= 102 12 | -------------------------------------------------------------------------------- /src/images/github.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/shared/CacheService.js: -------------------------------------------------------------------------------- 1 | import browser from 'webextension-polyfill' 2 | 3 | export default class AbstractCacheService { 4 | get key () { 5 | throw new Error('key must be set') 6 | } 7 | 8 | async get () { 9 | const cache = await browser.storage.local.get([this.key]) 10 | return cache[this.key] 11 | } 12 | 13 | async set (updatedState) { 14 | return browser.storage.local.set({ [this.key]: updatedState }) 15 | } 16 | 17 | async clear () { 18 | return browser.storage.local.remove([this.key]) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.history/src/store/index_20200427165410.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import VuexWebExtensions from 'vuex-webextensions'; 4 | import * as getters from './getters'; 5 | import mutations from './mutations'; 6 | import * as actions from './actions'; 7 | 8 | Vue.use(Vuex); 9 | 10 | export default new Vuex.Store({ 11 | state: { 12 | dataInfo: {}, 13 | isLoading: false, 14 | currentDomain: '', 15 | }, 16 | getters, 17 | mutations, 18 | actions, 19 | plugins: [ 20 | VuexWebExtensions({ 21 | persistentStates: ['dataInfo', 'isLoading', 'curr entDomain'], 22 | loggerLevel: 'verbose', 23 | }), 24 | ], 25 | }); 26 | -------------------------------------------------------------------------------- /.history/src/store/index_20200506103507.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import VuexWebExtensions from 'vuex-webextensions'; 4 | import * as getters from './getters'; 5 | import mutations from './mutations'; 6 | import * as actions from './actions'; 7 | 8 | Vue.use(Vuex); 9 | 10 | export default new Vuex.Store({ 11 | state: { 12 | dataInfo: {}, 13 | isLoading: false, 14 | currentDomain: '', 15 | },s 16 | getters, 17 | mutations, 18 | actions, 19 | plugins: [ 20 | VuexWebExtensions({ 21 | persistentStates: ['dataInfo', 'isLoading', 'currentDomain'], 22 | loggerLevel: 'verbose', 23 | }), 24 | ], 25 | }); 26 | -------------------------------------------------------------------------------- /.history/src/store/index_20200506103510.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import VuexWebExtensions from 'vuex-webextensions'; 4 | import * as getters from './getters'; 5 | import mutations from './mutations'; 6 | import * as actions from './actions'; 7 | 8 | Vue.use(Vuex); 9 | 10 | export default new Vuex.Store({ 11 | state: { 12 | dataInfo: {}, 13 | isLoading: false, 14 | currentDomain: '', 15 | }, 16 | getters, 17 | mutations, 18 | actions, 19 | plugins: [ 20 | VuexWebExtensions({ 21 | persistentStates: ['dataInfo', 'isLoading', 'currentDomain'], 22 | loggerLevel: 'verbose', 23 | }), 24 | ], 25 | }); 26 | -------------------------------------------------------------------------------- /.history/.eslintrc_20200428103401.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'plugin:vue/essential', 9 | 'airbnb-base', 10 | ], 11 | globals: { 12 | Atomics: 'readonly', 13 | SharedArrayBuffer: 'readonly', 14 | }, 15 | parserOptions: { 16 | ecmaVersion: 2018, 17 | }, 18 | plugins: [ 19 | 'vue', 20 | ], 21 | rules: { 22 | "at-rule-no-unknown": [true, { 23 | ignoreAtRules: [ 24 | /apply/, 25 | /tailwind/, 26 | /screen/, 27 | /if/, 28 | /else/, 29 | /return/, 30 | /function/, 31 | /debug/ 32 | ] 33 | }], 34 | "declaration-block-trailing-semicolon": null, 35 | "no-descending-specificity": null 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /.history/.eslintrc_20200507105239.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'plugin:vue/essential', 9 | 'airbnb-base', 10 | ], 11 | globals: { 12 | Atomics: 'readonly', 13 | SharedArrayBuffer: 'readonly', 14 | }, 15 | parserOptions: { 16 | ecmaVersion: 2018, 17 | }, 18 | plugins: [ 19 | 'vue', 20 | ], 21 | rules: { 22 | "at-rule-no-unknown": [null, { 23 | ignoreAtRules: [ 24 | /apply/, 25 | /tailwind/, 26 | /screen/, 27 | /if/, 28 | /else/, 29 | /return/, 30 | /function/, 31 | /debug/ 32 | ] 33 | }], 34 | "declaration-block-trailing-semicolon": null, 35 | "no-descending-specificity": null 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /.history/.eslintrc_20200507105304.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'plugin:vue/essential', 9 | 'airbnb-base', 10 | ], 11 | globals: { 12 | Atomics: 'readonly', 13 | SharedArrayBuffer: 'readonly', 14 | }, 15 | parserOptions: { 16 | ecmaVersion: 2018, 17 | }, 18 | plugins: [ 19 | 'vue', 20 | ], 21 | rules: { 22 | "scss/at-rule-no-unknown": [true, { 23 | ignoreAtRules: [ 24 | /apply/, 25 | /tailwind/, 26 | /screen/, 27 | /if/, 28 | /else/, 29 | /return/, 30 | /function/, 31 | /debug/ 32 | ] 33 | }], 34 | "declaration-block-trailing-semicolon": null, 35 | "no-descending-specificity": null 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /src/tailwind.css: -------------------------------------------------------------------------------- 1 | /* purgecss start ignore */ 2 | @import 'tailwindcss/base'; 3 | @import 'tailwindcss/components'; 4 | /* purgecss end ignore */ 5 | @import 'tailwindcss/utilities'; 6 | 7 | @font-face { 8 | font-family: 'PTRootUI'; 9 | src: url('./fonts/PTRootUI/PTRootUI-Regular.woff2') format('woff2'), 10 | url('./fonts/PTRootUI/PTRootUI-Regular.woff') format('woff'); 11 | font-weight: 400; 12 | font-style: normal; 13 | font-display: swap; 14 | } 15 | 16 | @font-face { 17 | font-family: 'PTRootUI'; 18 | src: url('./fonts/PTRootUI/PTRootUI-Bold.woff2') format('woff2'), 19 | url('./fonts/PTRootUI/PTRootUI-Bold.woff') format('woff'); 20 | font-weight: 700; 21 | font-style: normal; 22 | font-display: swap; 23 | } 24 | 25 | body { 26 | @apply font-body text-base antialiased text-grey-900; 27 | } 28 | 29 | .extension { 30 | width: 600px; 31 | min-height: 300px; 32 | } 33 | -------------------------------------------------------------------------------- /.history/tailwind.config_20200428171145.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | fill: theme => ({ 4 | 'white': theme('colors.white'), 5 | 'blue-dark': '#243746', 6 | }), 7 | extend: { 8 | screens: { 9 | light: { raw: "(prefers-color-scheme: light)" }, 10 | dark: { raw: "(prefers-color-scheme: dark)" }, 11 | }, 12 | colors: { 13 | container: 'rgba(255, 255, 255, 0.21)', 14 | 'green-light': '#41B38A', 15 | 'green-dark': '#158876', 16 | 'black-light': '#414042', 17 | 'black-dark': '#292728', 18 | 'gray': '#6D6E71', 19 | 'blue-dark': '#243746', 20 | }, 21 | textColor: { 22 | 'no-vue-title-light': '#414042', 23 | 'no-vue-title-dark': '#292728', 24 | 'no-vue-light': '#6D6E71', 25 | 'no-vue-dark': '#4D4D4F', 26 | } 27 | }, 28 | }, 29 | variants: {}, 30 | plugins: [], 31 | } 32 | -------------------------------------------------------------------------------- /.history/tailwind.config_20200507102900.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | fill: theme => ({ 4 | 'white': theme('colors.white'), 5 | 'blue-dark': '#243746', 6 | }), 7 | extend: { 8 | screens: { 9 | light: { raw: "(prefers-color-scheme: light)" }, 10 | dark: { raw: "(prefers-color-scheme: dark)" }, 11 | }, 12 | colors: { 13 | container: 'rgba(255, 255, 255, 0.21)', 14 | 'green-light': '#41B38A', 15 | 'green-dark': '#158876', 16 | 'black-light': '#414042', 17 | 'black-dark': '#292728', 18 | 'gray': '#6D6E71', 19 | 'blue-dark': '#243746', 20 | 'gray-cool': '#C8D3D9' 21 | }, 22 | textColor: { 23 | 'no-vue-title-light': '#414042', 24 | 'no-vue-title-dark': '#292728', 25 | 'no-vue-light': '#6D6E71', 26 | 'no-vue-dark': '#4D4D4F', 27 | } 28 | }, 29 | }, 30 | variants: {}, 31 | plugins: [], 32 | } 33 | -------------------------------------------------------------------------------- /.history/tailwind.config_20200507103019.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | theme: { 3 | fill: theme => ({ 4 | 'white': theme('colors.white'), 5 | 'blue-dark': '#243746', 6 | }), 7 | extend: { 8 | screens: { 9 | light: { raw: "(prefers-color-scheme: light)" }, 10 | dark: { raw: "(prefers-color-scheme: dark)" }, 11 | }, 12 | colors: { 13 | container: 'rgba(255, 255, 255, 0.21)', 14 | 'green-light': '#41B38A', 15 | 'green-dark': '#158876', 16 | 'black-light': '#414042', 17 | 'black-dark': '#292728', 18 | 'gray': '#6D6E71', 19 | 'blue-dark': '#243746', 20 | 'gray-cool': '#8F8F9A' 21 | }, 22 | textColor: { 23 | 'no-vue-title-light': '#414042', 24 | 'no-vue-title-dark': '#292728', 25 | 'no-vue-light': '#6D6E71', 26 | 'no-vue-dark': '#4D4D4F', 27 | } 28 | }, 29 | }, 30 | variants: {}, 31 | plugins: [], 32 | } 33 | -------------------------------------------------------------------------------- /src/images/info.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /firefox/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Vue Telescope", 3 | "description": "Discover Vue.js Websites", 4 | "version": "1.0.0", 5 | "manifest_version": 2, 6 | "icons": { 7 | "16": "icons/icon-16.png", 8 | "48": "icons/icon-48.png", 9 | "128": "icons/icon-128.png" 10 | }, 11 | "content_scripts": [ 12 | { 13 | "run_at": "document_start", 14 | "matches": [""], 15 | "js": ["content.js"] 16 | } 17 | ], 18 | "browser_action": { 19 | "default_icon": { 20 | "16": "icons/icon-grey-16.png", 21 | "48": "icons/icon-grey-48.png", 22 | "128": "icons/icon-grey-128.png" 23 | }, 24 | "default_title": "Vue Telescope", 25 | "default_popup": "popup/popup.html" 26 | }, 27 | "background": { 28 | "scripts": ["background.js"] 29 | }, 30 | "permissions": ["https://*/*", "storage"], 31 | "content_security_policy": "script-src 'self'; object-src 'self'", 32 | "web_accessible_resources": ["injected.js"] 33 | } 34 | -------------------------------------------------------------------------------- /src/images/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ExploreDataItem.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /chrome/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Vue Telescope", 3 | "description": "Discover Vue.js Websites", 4 | "version": "1.0.0", 5 | "manifest_version": 3, 6 | "minimum_chrome_version": "102", 7 | "icons": { 8 | "16": "icons/icon-16.png", 9 | "48": "icons/icon-48.png", 10 | "128": "icons/icon-128.png" 11 | }, 12 | "content_scripts": [ 13 | { 14 | "run_at": "document_start", 15 | "matches": [""], 16 | "js": ["content.js"] 17 | } 18 | ], 19 | "action": { 20 | "default_icon": { 21 | "16": "icons/icon-grey-16.png", 22 | "48": "icons/icon-grey-48.png", 23 | "128": "icons/icon-grey-128.png" 24 | }, 25 | "default_title": "Vue Telescope", 26 | "default_popup": "popup/popup.html" 27 | }, 28 | "background": { 29 | "service_worker": "background.js" 30 | }, 31 | "permissions": ["scripting", "storage"], 32 | "host_permissions": [ 33 | "" 34 | ], 35 | "content_security_policy": { 36 | "extension_pages": "script-src 'self'; object-src 'self'" 37 | }, 38 | "web_accessible_resources": [ 39 | { 40 | "resources": [ 41 | "injected.js" 42 | ], 43 | "matches": [ 44 | "" 45 | ], 46 | "extension_ids": [] 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /scripts/build-zip.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require('fs') 4 | const path = require('path') 5 | const archiver = require('archiver') 6 | 7 | const argv = require('minimist')(process.argv.slice(2)) 8 | const browser = argv['build-browser'] || 'chrome' 9 | const isFireFox = browser === 'firefox' 10 | 11 | const DEST_DIR = path.join(__dirname, `../dist${isFireFox ? '-firefox' : ''}`) 12 | const DEST_ZIP_DIR = path.join(__dirname, '../dist-zip') 13 | 14 | const extractExtensionData = () => { 15 | const extPackageJson = require('../package.json') 16 | 17 | return { 18 | name: extPackageJson.name, 19 | version: extPackageJson.version 20 | } 21 | } 22 | 23 | const makeDestZipDirIfNotExists = () => { 24 | if (!fs.existsSync(DEST_ZIP_DIR)) { 25 | fs.mkdirSync(DEST_ZIP_DIR) 26 | } 27 | } 28 | 29 | const buildZip = (src, dist, zipFilename) => { 30 | console.info(`Building ${zipFilename}...`) 31 | 32 | const archive = archiver('zip', { zlib: { level: 9 } }) 33 | const stream = fs.createWriteStream(path.join(dist, zipFilename)) 34 | 35 | return new Promise((resolve, reject) => { 36 | archive 37 | .directory(src, false) 38 | .on('error', err => reject(err)) 39 | .pipe(stream) 40 | 41 | stream.on('close', () => resolve()) 42 | archive.finalize() 43 | }) 44 | } 45 | 46 | const main = () => { 47 | const { name, version } = extractExtensionData() 48 | const zipFilename = `${name}${isFireFox ? '-firefox' : ''}-v${version}.zip` 49 | 50 | makeDestZipDirIfNotExists() 51 | 52 | buildZip(DEST_DIR, DEST_ZIP_DIR, zipFilename) 53 | .then(() => console.info('OK')) 54 | .catch(console.err) 55 | } 56 | 57 | main() 58 | -------------------------------------------------------------------------------- /src/content.js: -------------------------------------------------------------------------------- 1 | import { IS_CHROME, isSupportExecutionVersion } from './utils' 2 | const browser = require('webextension-polyfill') 3 | 4 | // injecting the script 5 | function injectScript (src) { 6 | const script = document.createElement('script') 7 | script.setAttribute('defer', 'defer') 8 | script.setAttribute('type', 'text/javascript') 9 | script.setAttribute('src', src) 10 | document.documentElement.appendChild(script) 11 | script.parentNode.removeChild(script) 12 | } 13 | 14 | // equivalent logic for other browser is in background.js 15 | if (!IS_CHROME || !isSupportExecutionVersion) { 16 | injectScript(browser.runtime.getURL('injected.js')) 17 | } 18 | 19 | // content script logic 20 | browser.runtime.onMessage.addListener(messageFromBackground) 21 | 22 | function messageFromBackground (message) { 23 | if (message.to === 'injected') { 24 | // proxy message to injected 25 | postMessage({ 26 | from: 'content', 27 | proxyFrom: message.from, 28 | to: message.to, 29 | action: message.action, 30 | payload: message.payload || {} 31 | }, '*') 32 | } 33 | } 34 | 35 | // listen to messages from injected script 36 | window.addEventListener('message', function (event) { 37 | if (event.data.from === 'injected') { 38 | if (event.data.action) { 39 | browser.runtime.sendMessage({ 40 | from: 'content', 41 | proxyFrom: event.data.from, 42 | to: event.data.to, 43 | action: event.data.action, 44 | payload: event.data.payload 45 | }) 46 | } 47 | } else if (event.data.from === 'popup') { 48 | // console.log('message from popup', event.data) 49 | } else if (event.data.from !== 'content') { 50 | // console.log('some other message', event.data) 51 | } 52 | }) 53 | -------------------------------------------------------------------------------- /src/images/modules.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | // Docs: https://tailwindcss.com/docs/configuration 2 | // Default: https://github.com/tailwindcss/tailwindcss/blob/master/stubs/defaultConfig.stub.js 3 | const defaultTheme = require('tailwindcss/defaultTheme') 4 | 5 | module.exports = { 6 | future: { 7 | removeDeprecatedGapUtilities: true 8 | }, 9 | theme: { 10 | fontSize: { 11 | base: '16px', 12 | one: '68px', 13 | two: '54px', 14 | three: '42px', 15 | four: '32px', 16 | five: '28px', 17 | six: '24px', 18 | seven: '20px', 19 | eight: '18px', 20 | sm: '14px', 21 | xs: '12px' 22 | }, 23 | letterSpacing: { 24 | '-20': 'calc(20 * -0.0125rem)', 25 | '-15': 'calc(15 * -0.0125rem)', 26 | '-10': 'calc(10 * -0.0125rem)', 27 | '-5': 'calc(5 * -0.0125rem)', 28 | '-4': 'calc(4 * -0.0125rem)', 29 | '-3': 'calc(3 * -0.0125rem)', 30 | '-2': 'calc(2 * -0.0125rem)', 31 | '-1': '-0.0125rem', 32 | 1: '0.0125rem', 33 | 2: 'calc(2 * 0.0125rem)', 34 | 3: 'calc(3 * 0.0125rem)', 35 | 4: 'calc(4 * 0.0125rem)', 36 | 5: 'calc(5 * 0.0125rem)', 37 | 10: 'calc(10 * 0.0125rem)', 38 | 15: 'calc(15 * 0.0125rem)', 39 | 20: 'calc(20 * 0.0125rem)' 40 | }, 41 | lineHeight: { 42 | base: '24px', 43 | one: '72px', 44 | two: '56px', 45 | three: '48px', 46 | four: '36px', 47 | five: '32px', 48 | six: '32px', 49 | seven: '28px', 50 | eight: '24px', 51 | sm: '20px', 52 | xs: '16px' 53 | }, 54 | fontWeight: { 55 | 'body-weight': 400, 56 | 'bold-body-weight': 700, 57 | 'display-weight': 700, 58 | 'mono-weight': 400 59 | }, 60 | colors: { 61 | transparent: 'transparent', 62 | current: 'currentColor', 63 | white: 'white', 64 | orange: '#ed8936', 65 | primary: { 66 | 50: '#E8FFF9', 67 | 100: '#B7F8E7', 68 | 200: '#88F1D5', 69 | 300: '#5CEAC3', 70 | 400: '#32E3B2', 71 | 500: '#0BDCA0', 72 | 600: '#09C38D', 73 | 700: '#07A97B', 74 | 800: '#058F68', 75 | 900: '#047555' 76 | }, 77 | grey: { 78 | 50: '#FAFAFA', 79 | 100: '#F5F5F5', 80 | 200: '#EEEEEE', 81 | 300: '#E0E0E0', 82 | 400: '#BDBDBD', 83 | 500: '#9E9E9E', 84 | 600: '#757575', 85 | 700: '#616161', 86 | 800: '#424242', 87 | 900: '#212121' 88 | } 89 | }, 90 | extend: { 91 | fontFamily: { 92 | display: ['PTRootUI', ...defaultTheme.fontFamily.sans], 93 | body: ['PTRootUI', ...defaultTheme.fontFamily.sans] 94 | }, 95 | borderRadius: { 96 | '2lg': '0.625rem', 97 | '3lg': '0.75rem', 98 | xl: '0.875rem', 99 | '2xl': '1rem', 100 | '3xl': '1.25rem', 101 | '4xl': '1.5rem' 102 | } 103 | } 104 | }, 105 | variants: {}, 106 | plugins: [] 107 | } 108 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-telescope-extension", 3 | "version": "1.9.0", 4 | "description": "The Vue Telescope extension to discover Vue websites.", 5 | "author": "NuxtJS Company", 6 | "license": "GPL-3.0", 7 | "engines": { 8 | "node": ">=10" 9 | }, 10 | "scripts": { 11 | "lint": "eslint --ext .js,.vue src", 12 | "build:dev": "cross-env NODE_ENV=development webpack --hide-modules", 13 | "build-zip": "node scripts/build-zip.js", 14 | "build-zip:firefox": "node scripts/build-zip.js --build-browser firefox", 15 | "watch:tailwind": "NODE_ENV=development postcss src/tailwind.css -o dist/tailwind.css -w", 16 | "dev:tailwind": "NODE_ENV=development postcss src/tailwind.css -o dist/tailwind.css", 17 | "build:tailwind": "NODE_ENV=production postcss src/tailwind.css -o dist/tailwind.css", 18 | "watch": "npm run build -- --watch", 19 | "watch:dev": "cross-env HMR=true npm run build:dev -- --watch", 20 | "dev": "concurrently \"npm run watch:tailwind\" \"npm run watch:dev\"", 21 | "build": "npm run build:tailwind && webpack --mode production", 22 | "build:firefox": "npm run build:tailwind && webpack --mode production --build-browser firefox && cp dist/tailwind.css dist-firefox/tailwind.css", 23 | "release": "standard-version && git push --follow-tags origin main" 24 | }, 25 | "dependencies": { 26 | "@babel/polyfill": "^7.8.7", 27 | "concurrently": "^5.2.0", 28 | "dotenv": "^8.2.0", 29 | "fs": "0.0.1-security", 30 | "postcss-cli": "^7.1.0", 31 | "postcss-loader": "^3.0.0", 32 | "tailwindcss": "^1.3.5", 33 | "vue": "^2.6.12", 34 | "vue-telescope-analyzer": "^0.10.1", 35 | "vuex": "^3.1.3", 36 | "webextension-polyfill": "^0.3.1" 37 | }, 38 | "devDependencies": { 39 | "@babel/core": "^7.1.2", 40 | "@babel/plugin-proposal-optional-chaining": "^7.0.0", 41 | "@babel/preset-env": "^7.1.0", 42 | "@babel/runtime-corejs3": "^7.4.0", 43 | "archiver": "^3.0.0", 44 | "babel-eslint": "^10.0.3", 45 | "babel-loader": "^8.0.2", 46 | "copy-webpack-plugin": "^5.1.1", 47 | "core-js": "^3.0.1", 48 | "cross-env": "^5.2.0", 49 | "css-loader": "^3.4.0", 50 | "ejs": "^2.6.1", 51 | "eslint": "^6.8.0", 52 | "eslint-config-airbnb-base": "^14.1.0", 53 | "eslint-config-standard": "^14.1.1", 54 | "eslint-friendly-formatter": "^4.0.1", 55 | "eslint-loader": "^3.0.2", 56 | "eslint-plugin-import": "^2.20.2", 57 | "eslint-plugin-node": "^11.1.0", 58 | "eslint-plugin-promise": "^4.2.1", 59 | "eslint-plugin-standard": "^4.0.1", 60 | "eslint-plugin-vue": "^6.2.2", 61 | "file-loader": "^5.0.2", 62 | "google-fonts-webpack-plugin": "^0.4.4", 63 | "html-loader": "^1.1.0", 64 | "mini-css-extract-plugin": "^0.9.0", 65 | "minimist": "^1.2.8", 66 | "standard-version": "^8.0.2", 67 | "vue-loader": "^15.4.2", 68 | "vue-svg-loader": "^0.16.0", 69 | "vue-template-compiler": "^2.6.10", 70 | "web-ext-types": "^2.1.0", 71 | "webpack": "^4.20.2", 72 | "webpack-cli": "^3.3.10", 73 | "webpack-extension-reloader": "^1.1.4" 74 | }, 75 | "volta": { 76 | "node": "16.20.0" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/injected.js: -------------------------------------------------------------------------------- 1 | // const browser = require('webextension-polyfill') 2 | 3 | import detectors from 'vue-telescope-analyzer/src/detectors.mjs' 4 | 5 | // backward compatibility 6 | window.$vueTelemetryExtension = true 7 | window.$vueTelescopeExtension = true 8 | 9 | async function analyze () { 10 | if (isBlacklisted(document.location.href)) return 11 | // const originalHtml = await fetch(document.location.href).then(res => res.text()) 12 | const originalHtml = document.documentElement.outerHTML 13 | const context = { 14 | originalHtml, 15 | html: document.documentElement.outerHTML, 16 | scripts: Array.from(document.getElementsByTagName('script')).map(({ src }) => src).filter(script => script), 17 | page: { 18 | evaluate: (fn) => window.eval(`(${fn});`) 19 | } 20 | } 21 | const hasVue = await detectors.hasVue(context) 22 | const vueVersion = window.$nuxt?.$root?.constructor?.version || window.Vue?.version || [...document.querySelectorAll("*")].map((el) => el.__vue__?.$root?.constructor?.version || el.__vue_app__?.version).filter(Boolean)[0] 23 | const { ssr } = await detectors.getVueMeta(context) 24 | const framework = await detectors.getFramework(context) 25 | if (framework?.slug === 'nuxtjs' && vueVersion) { 26 | try { 27 | framework.version = window.__unctx__?.get('nuxt-app')?.use()?.versions?.nuxt 28 | } catch (e) {} 29 | framework.version = framework.version || `Version ${vueVersion.split('.')[0]}` 30 | } 31 | const ui = await detectors.getUI(context) 32 | const plugins = await detectors.getPlugins(context) 33 | const nuxtMeta = await detectors.getNuxtMeta(context) 34 | const modules = await detectors.getNuxtModules(context) 35 | 36 | window.postMessage({ 37 | from: 'injected', 38 | action: 'analyze', 39 | payload: { 40 | url: document.location.href, 41 | path: document.location.pathname || '/', 42 | hasVue, 43 | vueVersion, 44 | // meta, 45 | plugins, 46 | framework, 47 | ui, 48 | hasSSR: ssr || nuxtMeta.ssr, 49 | isStatic: nuxtMeta.static, 50 | modules 51 | } 52 | }) 53 | } 54 | 55 | analyze() 56 | 57 | // listen to messages from content/popup/other script 58 | window.addEventListener('message', function (event) { 59 | if (event.data && event.data.from === 'content' && event.data.action === 'analyze') { 60 | analyze() 61 | // console.log('message from content', event.data) 62 | } else if (event.data && event.data.proxyFrom === 'background' && event.data.action === 'analyze') { 63 | // console.log('message from background', event.data) 64 | analyze() 65 | } else if (event.data && event.data.from === 'popup') { 66 | // console.log('message from popup', event.data) 67 | } else if (event.data && event.data.payload && event.data.from !== 'injected') { 68 | // console.log('other message', event.data) 69 | } 70 | }) 71 | 72 | // postMessage({ from: 'injected', payload: { message: 'hello from injected' } }, '*') 73 | 74 | function isBlacklisted (hostname) { 75 | const blacklist = ['localhost'] 76 | const likelyIP = Boolean(/\d/.test(hostname.split('.').pop())) 77 | return blacklist.includes(hostname) || likelyIP 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue Telescope Browser Extensions 2 | 3 | Browser extensions for [Vue Telescope](https://vuetelescope.com): available on Chrome, Firefox and Edge. 4 | 5 | [![Chrome Addon](https://badgen.net/chrome-web-store/v/neaebjphlfplgdhedjdhcnpjkndddbpd?icon=chrome)](https://chrome.google.com/webstore/detail/vue-telescope/neaebjphlfplgdhedjdhcnpjkndddbpd) 6 | [![Firefox Addon](https://badgen.net/amo/v/vue-telescope?icon=firefox)](https://addons.mozilla.org/en-GB/firefox/addon/vue-telescope/) 7 | [![Edge Addon](https://badgen.net/badge/icon/v1.5.8?icon=windows&label=Microsoft+Edge)](https://microsoftedge.microsoft.com/addons/detail/vue-telescope/icgcillpgelpleniodgkmohgdmeogodl) 8 | 9 | [![Extension Screenshot](https://user-images.githubusercontent.com/904724/105485378-12d55300-5cad-11eb-82f9-6cdaf214e6fa.jpg)](https://vuetelescope.com) 10 | 11 | ## Installation 12 | 13 | - [Get the Chrome Extension 🍭](https://chrome.google.com/webstore/detail/vue-telescope/neaebjphlfplgdhedjdhcnpjkndddbpd) 14 | - [Get the Firefox Addon 🦊](https://addons.mozilla.org/en-GB/firefox/addon/vue-telescope/) 15 | - [Get the Microsoft Edge Extension 🪟](https://microsoftedge.microsoft.com/addons/detail/vue-telescope/icgcillpgelpleniodgkmohgdmeogodl) 16 | 17 | ## Manual installation 18 | 19 | [download-extension-link]: https://github.com/nuxtlabs/vue-telescope-extensions/releases/download/v1.9.0/vue-telescope-extension-v1.9.0.zip 20 | [download-extension-firefox-link]: https://github.com/nuxtlabs/vue-telescope-extensions/releases/download/v1.9.0/vue-telescope-extension-firefox-v1.9.0.zip 21 | 22 | ### Chrome 23 | 24 | 1. [Download the extension (.zip)][download-extension-link] 25 | 2. Unzip it 26 | 3. Enter `chrome://extensions` in the URL bar 27 | 4. Enable the developer mode (toggle at the top right) 28 | 5. Click on "Load unpacked" button and select the unzipped directory 29 | 6. That's it ✨ ! *We recommend to pin the extension to quickly discover if a website uses Vue.js.* 30 | 31 | ![vt-chrome-extension](https://user-images.githubusercontent.com/904724/88188033-98614300-cc37-11ea-9500-f0e3ae3d97f0.gif) 32 | 33 | ### Firefox 34 | 35 | 1. [Download the extension (.zip)][download-extension-firefox-link] 36 | 2. Enter `about:debugging` in the URL bar 37 | 3. Go to "This Firefox" section 38 | 3. Click on "Load Temporary Add-on" and select the zip 39 | 4. That's it ✨! 40 | 41 | ![vt-firefox-extension](https://user-images.githubusercontent.com/904724/88186887-1d4b5d00-cc36-11ea-96c9-2b6367920863.gif) 42 | 43 | ## Development 44 | 45 | ### Setup 46 | 47 | Make sure to have [Yarn](https://classic.yarnpkg.com/en/) installed. 48 | 49 | After cloning the repository, install the dependencies: 50 | 51 | ```bash 52 | yarn install 53 | ``` 54 | 55 | Launch the project with: 56 | 57 | ```bash 58 | yarn dev 59 | ``` 60 | 61 | To have a preview in Chrome, you have to go to: `chrome://extensions/`, then enable developer mode (toggle at top right), then click on "Load unpacked" button and select the `dist` directory created. 62 | 63 | The extension will be automatically reloaded each time you make a change thanks to HMR. 64 | 65 | ### Build 66 | 67 | To build the extension you have to run: 68 | 69 | ```bash 70 | yarn build 71 | ``` 72 | 73 | and then: 74 | 75 | ```bash 76 | yarn build-zip 77 | ``` 78 | 79 | You will obtain a `zip` file inside `dist-zip` directory you can upload to the Chrome and Firefox webstore. 80 | 81 | ## License 82 | 83 | [MIT](./LICENSE) 84 | -------------------------------------------------------------------------------- /.history/src/background_20200507105557.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import store from './store'; 3 | 4 | // array to save domain for checking if allready post 5 | const domainsVisited = []; 6 | const browser = require('webextension-polyfill'); 7 | 8 | const map = store.getters.dataInfo; 9 | 10 | 11 | // send url to analyzer 12 | async function sendUrl(url, domain, tabId) { 13 | // loading 14 | store.commit('SET_ISLOADING', true); 15 | 16 | await axios({ 17 | method: 'GET', 18 | url: `https://vue-telemetry.netlify.com/api/analyze?url=${url}&src=extension`, 19 | auth: { 20 | username: 'nuxt-admin', 21 | password: 'vue-telemetry-protected-area', 22 | }, 23 | }).then(({ data }) => { 24 | // delete useless jsonKey 25 | delete data.url; 26 | delete data.hostname; 27 | delete data.domain; 28 | delete data.screenshot; 29 | delete data.meta; 30 | 31 | setMapData(domain, data); 32 | }).catch((err) => { 33 | browser.browserAction.setIcon({ 34 | tabId, 35 | path: { 36 | 16: 'icons/icon-vue-telemetry-404error-128.png', 37 | 32: 'icons/icon-vue-telemetry-404error-128.png', 38 | }, 39 | }); 40 | setMapData(domain, 'error'); 41 | }); 42 | 43 | store.commit('SET_ISLOADING', false); 44 | } 45 | 46 | // when tab created 47 | function handleCreated(tab) { 48 | setMapData(tab.url, 'noVue'); 49 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 50 | } 51 | 52 | // when tab clicked 53 | async function handleActivated() { 54 | // get active tab 55 | browser.tabs.query({ currentWindow: true, active: true }).then((tabsArray) => { 56 | if (/^chrome/.test(tabsArray[0].url) || /^about/.test(tabsArray[0].url)) { 57 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 58 | setMapData(tabsArray[0].url, 'noVue'); 59 | } else { 60 | detectVue(tabsArray[0].id, tabsArray[0].url); 61 | } 62 | }); 63 | } 64 | 65 | // when tab updated 66 | async function handleUpdated(tabId, changeInfo, tabInfo) { 67 | if (changeInfo.status == 'complete') { 68 | detectVue(tabId, tabInfo.url); 69 | } 70 | } 71 | 72 | browser.tabs.onCreated.addListener(handleCreated); 73 | browser.tabs.onActivated.addListener(handleActivated); 74 | browser.tabs.onUpdated.addListener(handleUpdated); 75 | 76 | 77 | // detect vue by calling detector and sendUrl 78 | async function detectVue(tabId, url) { 79 | await hasVue(tabId).then(({ response }) => { 80 | store.commit('SET_CURRENTDOMAIN', response.vueInfo.domain); 81 | 82 | if (response.vueInfo.hasVue) { 83 | browser.browserAction.setIcon({ 84 | tabId, 85 | path: { 86 | 16: 'icons/icon-robot-128.png', 87 | 32: 'icons/icon-robot-128.png', 88 | }, 89 | }); 90 | } 91 | 92 | if (!domainsVisited.includes(response.vueInfo.domain)) { 93 | domainsVisited.push(response.vueInfo.domain); 94 | 95 | if (response.vueInfo.hasVue) { 96 | sendUrl(url, response.vueInfo.domain, tabId); 97 | } else { 98 | setMapData(response.vueInfo.domain, 'noVue'); 99 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 100 | } 101 | } 102 | }); 103 | } 104 | 105 | // check vue in detector.js and get response 106 | function hasVue(tabId) { 107 | return new Promise((resolve) => { 108 | browser.tabs.sendMessage( 109 | tabId, 110 | { greeting: '' }, 111 | ).then((response) => { 112 | resolve(response); 113 | }); 114 | }); 115 | } 116 | 117 | function setMapData(domain, data) { 118 | map[domain] = data; 119 | store.commit('SET_DATAINFO', map); 120 | } 121 | -------------------------------------------------------------------------------- /.history/src/background_20200504171805.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import store from './store'; 3 | import { currentDomain } from './store/getters'; 4 | 5 | // array to save domain for checking if allready post 6 | const domainsVisited = []; 7 | const browser = require('webextension-polyfill'); 8 | 9 | const map = store.getters.dataInfo; 10 | 11 | 12 | // send url to analyzer 13 | async function sendUrl(url, domain, tabId) { 14 | // loading 15 | store.commit('SET_ISLOADING', true); 16 | 17 | await axios({ 18 | method: 'GET', 19 | url: `https://vue-telemetry.netlify.com/api/analyze?url=${url}&src=extension`, 20 | auth: { 21 | username: 'nuxt-admin', 22 | password: 'vue-telemetry-protected-area', 23 | }, 24 | }).then(({ data }) => { 25 | // delete useless jsonKey 26 | delete data.url; 27 | delete data.hostname; 28 | delete data.domain; 29 | delete data.screenshot; 30 | delete data.meta; 31 | 32 | setMapData(domain, data); 33 | }).catch((err) => { 34 | browser.browserAction.setIcon({ 35 | tabId, 36 | path: { 37 | 16: 'icons/icon-vue-telemetry-404error-128.png', 38 | 32: 'icons/icon-vue-telemetry-404error-128.png', 39 | }, 40 | }); 41 | setMapData(domain, 'error'); 42 | }); 43 | 44 | store.commit('SET_ISLOADING', false); 45 | } 46 | 47 | // when tab created 48 | function handleCreated(tab) { 49 | setMapData(tab.url, 'noVue'); 50 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 51 | } 52 | 53 | // when tab clicked 54 | async function handleActivated() { 55 | // get active tab 56 | browser.tabs.query({ currentWindow: true, active: true }).then((tabsArray) => { 57 | if (/^chrome/.test(tabsArray[0].url) || /^about/.test(tabsArray[0].url)) { 58 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 59 | setMapData(tabsArray[0].url, 'noVue'); 60 | } else { 61 | detectVue(tabsArray[0].id, tabsArray[0].url); 62 | } 63 | }); 64 | } 65 | 66 | // when tab updated 67 | async function handleUpdated(tabId, changeInfo, tabInfo) { 68 | if (changeInfo.status == 'complete') { 69 | detectVue(tabId, tabInfo.url); 70 | } 71 | } 72 | 73 | browser.tabs.onCreated.addListener(handleCreated); 74 | browser.tabs.onActivated.addListener(handleActivated); 75 | browser.tabs.onUpdated.addListener(handleUpdated); 76 | 77 | 78 | // detect vue by calling detector and sendUrl 79 | async function detectVue(tabId, url) { 80 | await hasVue(tabId).then(({ response }) => { 81 | store.commit('SET_CURRENTDOMAIN', response.vueInfo.domain); 82 | 83 | if (response.vueInfo.hasVue) { 84 | browser.browserAction.setIcon({ 85 | tabId, 86 | path: { 87 | 16: 'icons/icon-robot-128.png', 88 | 32: 'icons/icon-robot-128.png', 89 | }, 90 | }); 91 | } 92 | 93 | if (!domainsVisited.includes(response.vueInfo.domain)) { 94 | domainsVisited.push(response.vueInfo.domain); 95 | 96 | if (response.vueInfo.hasVue) { 97 | sendUrl(url, response.vueInfo.domain, tabId); 98 | } else { 99 | setMapData(response.vueInfo.domain, 'noVue'); 100 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 101 | } 102 | } 103 | }); 104 | } 105 | 106 | // check vue in detector.js and get response 107 | function hasVue(tabId) { 108 | return new Promise((resolve) => { 109 | browser.tabs.sendMessage( 110 | tabId, 111 | { greeting: '' }, 112 | ).then((response) => { 113 | resolve(response); 114 | }); 115 | }); 116 | } 117 | 118 | function setMapData(domain, data) { 119 | map[domain] = data; 120 | store.commit('SET_DATAINFO', map); 121 | } 122 | -------------------------------------------------------------------------------- /.history/src/background_20200505175106.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import store from './store'; 3 | import { currentDomain } from './store/getters'; 4 | 5 | // array to save domain for checking if allready post 6 | const domainsVisited = []; 7 | const browser = require('webextension-polyfill'); 8 | 9 | const map = store.getters.dataInfo; 10 | 11 | 12 | // send url to analyzer 13 | async function sendUrl(url, domain, tabId) { 14 | // loading 15 | store.commit('SET_ISLOADING', true); 16 | 17 | await axios({ 18 | method: 'GET', 19 | url: `https://vue-telemetry.netlify.com/api/analyze?url=${url}&src=extension`, 20 | auth: { 21 | username: 'nuxt-admin', 22 | password: 'vue-telemetry-protected-area', 23 | }, 24 | }).then(({ data }) => { 25 | // delete useless jsonKey 26 | delete data.url; 27 | delete data.hostname; 28 | delete data.domain; 29 | delete data.screenshot; 30 | delete data.meta; 31 | 32 | setMapData(domain, data); 33 | }).catch((err) => { 34 | browser.browserAction.setIcon({ 35 | tabId, 36 | path: { 37 | 16: 'icons/icon-vue-telemetry-404error-128.png', 38 | 32: 'icons/icon-vue-telemetry-404error-128.png', 39 | }, 40 | }); 41 | setMapData(domain, 'error'); 42 | }); 43 | 44 | store.commit('SET_ISLOADING', false); 45 | } 46 | 47 | // when tab created 48 | function handleCreated(tab) { 49 | setMapData(tab.url, 'noVue'); 50 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 51 | } 52 | 53 | // when tab clicked 54 | async function handleActivated() { 55 | // get active tab 56 | browser.tabs.query({ currentWindow: true, active: true }).then((tabsArray) => { 57 | if (/^chrome/.test(tabsArray[0].url) || /^about/.test(tabsArray[0].url)) { 58 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 59 | setMapData(tabsArray[0].url, 'noVue'); 60 | } else { 61 | detectVue(tabsArray[0].id, tabsArray[0].url); 62 | } 63 | }); 64 | } 65 | 66 | // when tab updated 67 | async function handleUpdated(tabId, changeInfo, tabInfo) { 68 | if (changeInfo.status == 'complete') { 69 | detectVue(tabId, tabInfo.url); 70 | } 71 | } 72 | 73 | browser.tabs.onCreated.addListener(handleCreated); 74 | browser.tabs.onActivated.addListener(handleActivated); 75 | browser.tabs.onUpdated.addListener(handleUpdated); 76 | 77 | 78 | // detect vue by calling detector and sendUrl 79 | async function detectVue(tabId, url) { 80 | await hasVue(tabId).then(({ response }) => { 81 | store.commit('SET_CURRENTDOMAIN', response.vueInfo.domain); 82 | 83 | if (response.vueInfo.hasVue) { 84 | browser.browserAction.setIcon({ 85 | tabId, 86 | path: { 87 | 16: 'icons/icon-robot-128.png', 88 | 32: 'icons/icon-robot-128.png', 89 | }, 90 | }); 91 | } 92 | 93 | if (!domainsVisited.includes(response.vueInfo.domain)) { 94 | domainsVisited.push(response.vueInfo.domain); 95 | 96 | if (response.vueInfo.hasVue) { 97 | sendUrl(url, response.vueInfo.domain, tabId); 98 | } else { 99 | setMapData(response.vueInfo.domain, 'noVue'); 100 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 101 | } 102 | } 103 | }); 104 | } 105 | 106 | // check vue in detector.js and get response 107 | function hasVue(tabId) { 108 | return new Promise((resolve) => { 109 | browser.tabs.sendMessage( 110 | tabId, 111 | { greeting: '' }, 112 | ).then((response) => { 113 | resolve(response); 114 | }); 115 | }); 116 | } 117 | 118 | function setMapData(domain, data) { 119 | map[domain] = data; 120 | store.commit('SET_DATAINFO', map); 121 | } 122 | -------------------------------------------------------------------------------- /.history/src/background_20200505175256.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import store from './store'; 3 | import { currentDomain } from './store/getters'; 4 | 5 | // array to save domain for checking if allready post 6 | const domainsVisited = []; 7 | const browser = require('webextension-polyfill'); 8 | 9 | const map = store.getters.dataInfo; 10 | 11 | 12 | // send url to analyzer 13 | async function sendUrl(url, domain, tabId) { 14 | // loading 15 | store.commit('SET_ISLOADING', true); 16 | 17 | await axios({ 18 | method: 'GET', 19 | url: `https://vue-telemetry.netlify.com/api/analyze?url=${url}&src=extension`, 20 | auth: { 21 | username: 'nuxt-admin', 22 | password: 'vue-telemetry-protected-area', 23 | }, 24 | }).then(({ data }) => { 25 | // delete useless jsonKey 26 | delete data.url; 27 | delete data.hostname; 28 | delete data.domain; 29 | delete data.screenshot; 30 | delete data.meta; 31 | 32 | setMapData(domain, data); 33 | }).catch((err) => { 34 | browser.browserAction.setIcon({ 35 | tabId, 36 | path: { 37 | 16: 'icons/icon-vue-telemetry-404error-128.png', 38 | 32: 'icons/icon-vue-telemetry-404error-128.png', 39 | }, 40 | }); 41 | setMapData(domain, 'error'); 42 | }); 43 | 44 | store.commit('SET_ISLOADING', false); 45 | } 46 | 47 | // when tab created 48 | function handleCreated(tab) { 49 | setMapData(tab.url, 'noVue'); 50 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 51 | } 52 | 53 | // when tab clicked 54 | async function handleActivated() { 55 | // get active tab 56 | browser.tabs.query({ currentWindow: true, active: true }).then((tabsArray) => { 57 | if (/^chrome/.test(tabsArray[0].url) || /^about/.test(tabsArray[0].url)) { 58 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 59 | setMapData(tabsArray[0].url, 'noVue'); 60 | } else { 61 | detectVue(tabsArray[0].id, tabsArray[0].url); 62 | } 63 | }); 64 | } 65 | 66 | // when tab updated 67 | async function handleUpdated(tabId, changeInfo, tabInfo) { 68 | if (changeInfo.status == 'complete') { 69 | detectVue(tabId, tabInfo.url); 70 | } 71 | } 72 | 73 | browser.tabs.onCreated.addListener(handleCreated); 74 | browser.tabs.onActivated.addListener(handleActivated); 75 | browser.tabs.onUpdated.addListener(handleUpdated); 76 | 77 | 78 | // detect vue by calling detector and sendUrl 79 | async function detectVue(tabId, url) { 80 | await hasVue(tabId).then(({ response }) => { 81 | store.commit('SET_CURRENTDOMAIN', response.vueInfo.domain); 82 | 83 | if (response.vueInfo.hasVue) { 84 | browser.browserAction.setIcon({ 85 | tabId, 86 | path: { 87 | 16: 'icons/icon-robot-128.png', 88 | 32: 'icons/icon-robot-128.png', 89 | }, 90 | }); 91 | } 92 | 93 | if (!domainsVisited.includes(response.vueInfo.domain)) { 94 | domainsVisited.push(response.vueInfo.domain); 95 | 96 | if (response.vueInfo.hasVue) { 97 | sendUrl(url, response.vueInfo.domain, tabId); 98 | } else { 99 | setMapData(response.vueInfo.domain, 'noVue'); 100 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 101 | } 102 | } 103 | }); 104 | } 105 | 106 | // check vue in detector.js and get response 107 | function hasVue(tabId) { 108 | return new Promise((resolve) => { 109 | browser.tabs.sendMessage( 110 | tabId, 111 | { greeting: '' }, 112 | ).then((response) => { 113 | resolve(response); 114 | }); 115 | }); 116 | } 117 | 118 | function setMapData(domain, data) { 119 | map[domain] = data; 120 | store.commit('SET_DATAINFO', map); 121 | } 122 | -------------------------------------------------------------------------------- /.history/src/backgroundts_20200505175746.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import store from './store'; 3 | import { currentDomain } from './store/getters'; 4 | 5 | // array to save domain for checking if allready post 6 | const domainsVisited: string[] = []; 7 | const browser = require('webextension-polyfill'); 8 | 9 | const map: Map = store.getters.dataInfo; 10 | 11 | // send url to analyzer 12 | async function sendUrl(url, domain, tabId) { 13 | // loading 14 | store.commit('SET_ISLOADING', true); 15 | 16 | await axios({ 17 | method: 'GET', 18 | url: `https://vue-telemetry.netlify.com/api/analyze?url=${url}&src=extension`, 19 | auth: { 20 | username: 'nuxt-admin', 21 | password: 'vue-telemetry-protected-area', 22 | }, 23 | }).then(({ data }) => { 24 | // delete useless jsonKey 25 | delete data.url; 26 | delete data.hostname; 27 | delete data.domain; 28 | delete data.screenshot; 29 | delete data.meta; 30 | 31 | setMapData(domain, data); 32 | }).catch((err) => { 33 | browser.browserAction.setIcon({ 34 | tabId, 35 | path: { 36 | 16: 'icons/icon-vue-telemetry-404error-128.png', 37 | 32: 'icons/icon-vue-telemetry-404error-128.png', 38 | }, 39 | }); 40 | setMapData(domain, 'error'); 41 | }); 42 | 43 | store.commit('SET_ISLOADING', false); 44 | } 45 | 46 | // when tab created 47 | function handleCreated(tab) { 48 | setMapData(tab.url, 'noVue'); 49 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 50 | } 51 | 52 | // when tab clicked 53 | async function handleActivated() { 54 | // get active tab 55 | browser.tabs.query({ currentWindow: true, active: true }).then((tabsArray) => { 56 | if (/^chrome/.test(tabsArray[0].url) || /^about/.test(tabsArray[0].url)) { 57 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 58 | setMapData(tabsArray[0].url, 'noVue'); 59 | } else { 60 | detectVue(tabsArray[0].id, tabsArray[0].url); 61 | } 62 | }); 63 | } 64 | 65 | // when tab updated 66 | async function handleUpdated(tabId, changeInfo, tabInfo) { 67 | if (changeInfo.status == 'complete') { 68 | detectVue(tabId, tabInfo.url); 69 | } 70 | } 71 | 72 | browser.tabs.onCreated.addListener(handleCreated); 73 | browser.tabs.onActivated.addListener(handleActivated); 74 | browser.tabs.onUpdated.addListener(handleUpdated); 75 | 76 | 77 | // detect vue by calling detector and sendUrl 78 | async function detectVue(tabId, url) { 79 | await hasVue(tabId).then(({ response }) => { 80 | store.commit('SET_CURRENTDOMAIN', response.vueInfo.domain); 81 | 82 | if (response.vueInfo.hasVue) { 83 | browser.browserAction.setIcon({ 84 | tabId, 85 | path: { 86 | 16: 'icons/icon-robot-128.png', 87 | 32: 'icons/icon-robot-128.png', 88 | }, 89 | }); 90 | } 91 | 92 | if (!domainsVisited.includes(response.vueInfo.domain)) { 93 | domainsVisited.push(response.vueInfo.domain); 94 | 95 | if (response.vueInfo.hasVue) { 96 | sendUrl(url, response.vueInfo.domain, tabId); 97 | } else { 98 | setMapData(response.vueInfo.domain, 'noVue'); 99 | store.commit('SET_CURRENTDOMAIN', 'noVue'); 100 | } 101 | } 102 | }); 103 | } 104 | 105 | // check vue in detector.js and get response 106 | function hasVue(tabId) { 107 | return new Promise((resolve) => { 108 | browser.tabs.sendMessage( 109 | tabId, 110 | { greeting: '' }, 111 | ).then((response) => { 112 | resolve(response); 113 | }); 114 | }); 115 | } 116 | 117 | function setMapData(domain, data) { 118 | map[domain] = data; 119 | store.commit('SET_DATAINFO', map); 120 | } 121 | -------------------------------------------------------------------------------- /src/components/AppButton.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /src/images/plugins.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const webpack = require('webpack') 3 | const ejs = require('ejs') 4 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 5 | const CopyPlugin = require('copy-webpack-plugin') 6 | // const ExtensionReloader = require('webpack-extension-reloader') 7 | const { VueLoaderPlugin } = require('vue-loader') 8 | const { version } = require('./package.json') 9 | const path = require('path') 10 | 11 | const argv = require('minimist')(process.argv.slice(2)) 12 | const browser = argv['build-browser'] || 'chrome' 13 | const distDir = `/dist${browser === 'firefox' ? '-firefox' : ''}` 14 | 15 | const config = { 16 | devtool: 'inline-source-map', 17 | mode: process.env.NODE_ENV, 18 | context: path.join(__dirname, 'src'), 19 | entry: { 20 | 'popup/popup': './popup/popup.js', 21 | injected: './injected.js', 22 | background: './background/index.js', 23 | content: './content.js' 24 | }, 25 | output: { 26 | path: path.join(__dirname, distDir), 27 | filename: '[name].js' 28 | // sourceMapFilename: '[name].js.map' 29 | }, 30 | resolve: { 31 | extensions: ['.mjs', '.js', '.vue'] 32 | }, 33 | node: { 34 | fs: 'empty' 35 | }, 36 | module: { 37 | rules: [ 38 | { 39 | test: /\.mjs$/, 40 | type: 'javascript/esm', 41 | include: [ 42 | /node_modules/ 43 | ] 44 | }, 45 | { 46 | test: /\.tsx?$/, 47 | use: 'ts-loader', 48 | exclude: /node_modules/ 49 | }, 50 | { 51 | test: /\.vue$/, 52 | loader: 'vue-loader' 53 | }, 54 | { 55 | test: /\.js$/, 56 | loader: 'babel-loader', 57 | exclude: /node_modules/ 58 | }, 59 | { 60 | test: /\.css$/, 61 | use: [MiniCssExtractPlugin.loader, 'css-loader'] 62 | }, 63 | { 64 | test: /\.(png|jpg|jpeg|gif|ico)$/, 65 | loader: 'file-loader', 66 | options: { 67 | name: '[name].[ext]', 68 | outputPath: '/images/', 69 | emitFile: false 70 | } 71 | }, 72 | { 73 | test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/, 74 | loader: 'file-loader', 75 | options: { 76 | name: '[name].[ext]', 77 | outputPath: '/fonts/', 78 | emitFile: false 79 | } 80 | }, 81 | { 82 | test: /\.svg$/, 83 | loader: 'vue-svg-loader' 84 | } 85 | ] 86 | }, 87 | plugins: [ 88 | new webpack.DefinePlugin({ 89 | global: 'window' 90 | }), 91 | new VueLoaderPlugin(), 92 | new MiniCssExtractPlugin({ 93 | filename: '[name].css' 94 | }), 95 | new CopyPlugin([ 96 | { from: 'icons', to: 'icons', ignore: ['icon.xcf'] }, 97 | { from: 'images', to: 'images', ignore: ['icon.xcf'] }, 98 | { from: 'fonts', to: 'fonts' }, 99 | { from: 'popup/popup.html', to: 'popup/popup.html', transform: transformHtml }, 100 | { 101 | from: './popup/popup.css', 102 | to: path.join(__dirname, `${distDir}/popup/popup.css`) 103 | }, 104 | { 105 | // firefox only support manifest V2 106 | from: `../${browser}/manifest.json`, 107 | to: 'manifest.json', 108 | transform: (content) => { 109 | const jsonContent = JSON.parse(content) 110 | jsonContent.version = version 111 | 112 | if (config.mode === 'development') { 113 | if (browser === 'firefox') { 114 | jsonContent.content_security_policy = "script-src 'self'; object-src 'self'" 115 | } else { 116 | jsonContent.content_security_policy = { 117 | extension_pages: "script-src 'self'; object-src 'self'" 118 | } 119 | } 120 | } 121 | 122 | return JSON.stringify(jsonContent, null, 2) 123 | } 124 | } 125 | ]) 126 | ] 127 | } 128 | 129 | if (config.mode === 'production') { 130 | config.plugins = (config.plugins || []).concat([ 131 | new webpack.DefinePlugin({ 132 | 'process.env': { 133 | NODE_ENV: '"production"' 134 | } 135 | }) 136 | ]) 137 | } 138 | 139 | if (config.mode === 'development') { 140 | config.plugins = (config.plugins || []).concat([ 141 | new webpack.DefinePlugin({ 142 | 'process.env': { 143 | NODE_ENV: '"development"', 144 | ICONS_URL: JSON.stringify(process.env.ICONS_URL) 145 | } 146 | }) 147 | ]) 148 | } 149 | 150 | if (process.env.HMR === 'true') { 151 | // https://github.com/rubenspgcavalcante/webpack-extension-reloader/issues/125 152 | // ExtensionReloader think `background.scripts` is must, but manifest V3 can use `service_worker` 153 | // config.plugins = (config.plugins || []).concat([ 154 | // new ExtensionReloader({ 155 | // manifest: path.join(__dirname, '/src/manifest.json') 156 | // }) 157 | // ]) 158 | } 159 | 160 | function transformHtml (content) { 161 | return ejs.render(content.toString(), { 162 | ...process.env 163 | }) 164 | } 165 | 166 | module.exports = config 167 | -------------------------------------------------------------------------------- /src/background/index.js: -------------------------------------------------------------------------------- 1 | import { IS_CHROME, IS_FIREFOX, isSupportExecutionVersion } from '../utils' 2 | import browser from 'webextension-polyfill' 3 | import TabsStateService from './TabsStateService' 4 | 5 | const tabsState = new TabsStateService() 6 | 7 | if (IS_CHROME && isSupportExecutionVersion) { 8 | /** 9 | * equivalent logic for Firefox is in content.js 10 | * Manifest V3 method of injecting content scripts (not yet supported in Firefox) 11 | Note: the "world" option in registerContentScripts is only available in Chrome v102+ 12 | MAIN The execution environment of the web page. This environment is shared with the web page, without isolation. 13 | */ 14 | browser.scripting.registerContentScripts( 15 | [ 16 | { 17 | id: 'injected', 18 | matches: [''], 19 | js: ['injected.js'], 20 | runAt: 'document_start', 21 | world: browser.scripting.ExecutionWorld.MAIN 22 | } 23 | ], 24 | function () { 25 | // When the content scripts are already registered, an error will be thrown. 26 | // It happens when the service worker process is incorrectly duplicated. 27 | if (browser.runtime.lastError) { 28 | console.error(browser.runtime.lastError) 29 | } 30 | } 31 | ) 32 | } 33 | 34 | browser.tabs.onUpdated.addListener(handleUpdated) 35 | browser.runtime.onStartup.addListener(() => { 36 | // clear state on startup. 37 | // note: chrome allows to use 'browser.storage.session' but it is available in chrome only. 38 | // for firefox a 'window.session' can be considered as alternative. 39 | // TODO: create polyfill for session store. 40 | tabsState.clear() 41 | }) 42 | 43 | async function setIcon (details) { 44 | // because manifest version is different 45 | if (IS_FIREFOX) { 46 | await browser.browserAction.setIcon(details) 47 | } else { 48 | await browser.action.setIcon(details) 49 | } 50 | } 51 | 52 | const setIconForTab = async (tabId) => { 53 | const tabs = await tabsState.get() 54 | const tab = tabs[tabId]; 55 | if (tab?.framework?.slug) { 56 | const slug = tab.framework.slug 57 | const iconPath = `icons/${slug}.png` 58 | try { 59 | await setIcon({tabId, path: iconPath}); 60 | } catch(e) { 61 | await setIcon({ 62 | tabId, 63 | path: 'icons/icon-128.png' 64 | }) 65 | } 66 | } else { 67 | await setIcon({ 68 | tabId, 69 | path: tab?.hasVue ? 'icons/icon-128.png' : 'icons/icon-grey-128.png' 70 | }) 71 | } 72 | } 73 | 74 | browser.storage.local.onChanged.addListener(async (payload) => { 75 | if (payload.settings) { 76 | const tabs = await browser.tabs.query({}); 77 | tabs.forEach(tab => { 78 | if (tab.id) { 79 | setIconForTab(tab.id) 80 | } 81 | }) 82 | } 83 | }) 84 | 85 | browser.runtime.onMessage.addListener( 86 | async function (message, sender, sendResponse) { 87 | if (message.action === 'analyze') { 88 | // when sending message from popup.js there's no sender.tab, so need to pass tabId 89 | const tabId = (sender.tab && sender.tab.id) || message.payload.tabId 90 | 91 | const tabs = await tabsState.get() 92 | // set/overwrite analyzed data on new tab/url 93 | if (!tabs[tabId] || tabs[tabId].url !== message.payload.url) { 94 | tabs[tabId] = message.payload 95 | } else { 96 | // temporary fix when hit CSP 97 | if (!message.payload.modules.length) delete message.payload.modules 98 | if (!message.payload.plugins.length) delete message.payload.plugins 99 | 100 | tabs[tabId] = { ...tabs[tabId], ...message.payload } 101 | } 102 | 103 | const showcase = tabs[tabId] 104 | if (showcase.hasVue && !showcase.slug) { 105 | try { 106 | if (typeof EventSource === 'undefined') { 107 | console.log('EventSource is not supported in current borwser!') 108 | return 109 | } 110 | const sse = new EventSource( 111 | `https://service.vuetelescope.com?url=${message.payload.url}` 112 | ) 113 | sse.addEventListener('message', async (event) => { 114 | try { 115 | const res = JSON.parse(event.data) 116 | if (!res.error && !res.isAdultContent) { 117 | showcase.isPublic = res.isPublic 118 | showcase.slug = res.slug 119 | sse.close() 120 | 121 | // temporary fix when hit CSP 122 | if (!showcase.modules.length && res.modules.length) { 123 | showcase.modules = res.modules 124 | } 125 | if (!showcase.plugins.length && res.plugins.length) { 126 | showcase.plugins = res.plugins 127 | } 128 | await tabsState.updateData(tabId, tabs[tabId]) 129 | } else { 130 | throw new Error('API call to VT failed') 131 | } 132 | } catch (err) { 133 | sse.close() 134 | } 135 | }) 136 | } catch (err) {} 137 | } 138 | await tabsState.updateData(tabId, tabs[tabId]) 139 | await setIconForTab(tabId); 140 | } else if (!sender.tab) { 141 | if (message.action === 'getShowcase') { 142 | const tabs = await tabsState.get() 143 | return { payload: tabs[message.payload.tabId] } 144 | } 145 | } 146 | } 147 | ) 148 | 149 | // when tab updated 150 | async function handleUpdated (tabId, changeInfo, tabInfo) { 151 | if (changeInfo.status === 'complete') { 152 | const tabs = await tabsState.get() 153 | if (!tabs[tabId]) return 154 | // tabsStorage[tabId].url = tabInfo.url 155 | browser.tabs.sendMessage(tabId, { 156 | from: 'background', 157 | to: 'injected', 158 | action: 'analyze', 159 | payload: {} 160 | }) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200429172340.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [1.9.0](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.8.0...v1.9.0) (2023-06-19) 6 | 7 | 8 | ### Features 9 | 10 | * add option to display framework icon ([#21](https://github.com/nuxtlabs/vue-telescope-extensions/issues/21)) ([1526345](https://github.com/nuxtlabs/vue-telescope-extensions/commit/152634513ee0b0e07468541483de3a7b35c7e245)), closes [/github.com/nuxtlabs/vue-telescope-extensions/pull/21#issuecomment-1582693379](https://github.com/nuxtlabs//github.com/nuxtlabs/vue-telescope-extensions/pull/21/issues/issuecomment-1582693379) 11 | 12 | 13 | ### Bug Fixes 14 | 15 | * typo in error message ([#22](https://github.com/nuxtlabs/vue-telescope-extensions/issues/22)) ([742cbc8](https://github.com/nuxtlabs/vue-telescope-extensions/commit/742cbc836a9f01adde9e36b8aec61941d8b250e3)) 16 | 17 | ## [1.8.0](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.7.2...v1.8.0) (2023-04-26) 18 | 19 | 20 | ### Features 21 | 22 | * support Nuxt version ([37d49c8](https://github.com/nuxtlabs/vue-telescope-extensions/commit/37d49c82d4666b4290c9e16f24f8bc15866182db)) 23 | 24 | ### [1.7.2](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.7.1...v1.7.2) (2023-04-25) 25 | 26 | 27 | ### Bug Fixes 28 | 29 | * use `runtime.getURL` instead of `extension.getURL` ([#20](https://github.com/nuxtlabs/vue-telescope-extensions/issues/20)) ([e92ed0b](https://github.com/nuxtlabs/vue-telescope-extensions/commit/e92ed0bb3ca5ee2f6d954787430e43d96c13cf26)) 30 | 31 | ### [1.7.1](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.7.0...v1.7.1) (2023-04-17) 32 | 33 | ## [1.7.0](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.6.1...v1.7.0) (2023-04-13) 34 | 35 | 36 | ### Features 37 | 38 | * Improve script injection and migrate to Manifest V3 ([#18](https://github.com/nuxtlabs/vue-telescope-extensions/issues/18)) ([8ea5079](https://github.com/nuxtlabs/vue-telescope-extensions/commit/8ea50793b8a1ff35a718ac05accbf2cc6ede1f73)) 39 | 40 | ### [1.6.1](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.6.0...v1.6.1) (2022-09-21) 41 | 42 | ## [1.6.0](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.5.8...v1.6.0) (2022-09-21) 43 | 44 | 45 | ### Features 46 | 47 | * upgrade to latest vta ([8dfbd2f](https://github.com/nuxtlabs/vue-telescope-extensions/commit/8dfbd2f801a72326b8ef4b3815e1e67386122d3d)) 48 | 49 | 50 | ### Bug Fixes 51 | 52 | * Correct vue version docs url on vue link ([#12](https://github.com/nuxtlabs/vue-telescope-extensions/issues/12)) ([0c3f526](https://github.com/nuxtlabs/vue-telescope-extensions/commit/0c3f526dc66e7c045c60ec2d0516f3987d1e6955)) 53 | 54 | ### [1.5.8](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.5.7...v1.5.8) (2022-05-04) 55 | 56 | ### [1.5.7](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.5.6...v1.5.7) (2022-04-08) 57 | 58 | 59 | ### Bug Fixes 60 | 61 | * update vta ([e64ec3b](https://github.com/nuxtlabs/vue-telescope-extensions/commit/e64ec3b5caff07f175da52cd6211468ce2888698)) 62 | 63 | ### [1.5.6](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.5.5...v1.5.6) (2022-01-24) 64 | 65 | ### [1.5.5](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.5.4...v1.5.5) (2021-11-10) 66 | 67 | 68 | ### Bug Fixes 69 | 70 | * Correct vue version docs url on vue link ([#11](https://github.com/nuxtlabs/vue-telescope-extensions/issues/11)) ([8a37586](https://github.com/nuxtlabs/vue-telescope-extensions/commit/8a37586141def69c3c291d525ad808ceee72898c)) 71 | 72 | ### [1.5.4](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.5.3...v1.5.4) (2021-05-11) 73 | 74 | ### [1.5.3](https://github.com/nuxtlabs/vue-telescope-extensions/compare/v1.5.2...v1.5.3) (2021-02-22) 75 | 76 | ### [1.5.2](https://github.com/nuxt-company/vue-telescope-extensions/compare/v1.5.1...v1.5.2) (2021-01-25) 77 | 78 | ### [1.5.1](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.5.0...v1.5.1) (2021-01-22) 79 | 80 | ## [1.5.0](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.4.5...v1.5.0) (2021-01-22) 81 | 82 | 83 | ### Features 84 | 85 | * user vue telescope naming ([314c8c8](https://github.com/nuxt-company/vue-telemetry-extensions/commit/314c8c8f9735540aa9ff8276913a20bf11c5e3fe)) 86 | 87 | ### [1.4.5](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.4.4...v1.4.5) (2020-12-14) 88 | 89 | 90 | ### Bug Fixes 91 | 92 | * display vue 3 version ([9ff29a1](https://github.com/nuxt-company/vue-telemetry-extensions/commit/9ff29a10d0e19e6adc6f81695739e1c0a5b83f11)) 93 | 94 | ### [1.4.4](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.4.3...v1.4.4) (2020-12-14) 95 | 96 | ### [1.4.3](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.4.2...v1.4.3) (2020-11-18) 97 | 98 | ### [1.4.2](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.4.1...v1.4.2) (2020-10-27) 99 | 100 | ### [1.4.1](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.4.0...v1.4.1) (2020-10-19) 101 | 102 | ## [1.4.0](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.3.0...v1.4.0) (2020-09-18) 103 | 104 | 105 | ### Features 106 | 107 | * use SSE for analyzer service ([#5](https://github.com/nuxt-company/vue-telemetry-extensions/issues/5)) ([5d06eb4](https://github.com/nuxt-company/vue-telemetry-extensions/commit/5d06eb48bd93238faadc8725adc916c6502c4289)) 108 | 109 | ## [1.3.0](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.2.3...v1.3.0) (2020-09-12) 110 | 111 | 112 | ### Features 113 | 114 | * add new detectors ([f6ccf11](https://github.com/nuxt-company/vue-telemetry-extensions/commit/f6ccf11465016b93408c613758dcc108ff2aebbe)) 115 | 116 | ### [1.2.3](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.2.2...v1.2.3) (2020-09-09) 117 | 118 | ### [1.2.2](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.2.1...v1.2.2) (2020-09-09) 119 | 120 | 121 | ### Bug Fixes 122 | 123 | * handle could not save ([92993c0](https://github.com/nuxt-company/vue-telemetry-extensions/commit/92993c0e349ca93661c67185dbd672527a6f8255)) 124 | 125 | ### [1.2.1](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.2.0...v1.2.1) (2020-09-08) 126 | 127 | 128 | ### Bug Fixes 129 | 130 | * call API only when slug is undefined ([28669e1](https://github.com/nuxt-company/vue-telemetry-extensions/commit/28669e1ddbbc0ed21dc6bb5306eb62f8007782b4)) 131 | 132 | ## [1.2.0](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.1.2...v1.2.0) (2020-09-08) 133 | 134 | 135 | ### Features 136 | 137 | * analyze locally ([#2](https://github.com/nuxt-company/vue-telemetry-extensions/issues/2)) ([ebdef2b](https://github.com/nuxt-company/vue-telemetry-extensions/commit/ebdef2bc968336c9a16d78a7743cf67cbd1df6b1)) 138 | 139 | ### [1.1.2](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.1.0...v1.1.2) (2020-08-18) 140 | 141 | 142 | ### Bug Fixes 143 | 144 | * handle fonts and remove basic auth ([41b901c](https://github.com/nuxt-company/vue-telemetry-extensions/commit/41b901c1275e53c3b53bcaec90ebd2e3456d146c)) 145 | 146 | ### [1.1.1](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.1.0...v1.1.1) (2020-07-30) 147 | 148 | 149 | ### Bug Fixes 150 | 151 | * handle fonts and remove basic auth ([f630107](https://github.com/nuxt-company/vue-telemetry-extensions/commit/f630107e69e6c08f37a65c252ac2696f80549512)) 152 | 153 | ## [1.1.0](https://github.com/nuxt-company/vue-telemetry-extensions/compare/v1.0.0...v1.1.0) (2020-07-27) 154 | 155 | 156 | ### Features 157 | 158 | * handle refresh ([b0418ed](https://github.com/nuxt-company/vue-telemetry-extensions/commit/b0418ed08adb75bbbbe5ba52fe14c399c3ca6f62)) 159 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200507104748.vue: -------------------------------------------------------------------------------- 1 | 100 | 101 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200507105625.vue: -------------------------------------------------------------------------------- 1 | 100 | 101 | -------------------------------------------------------------------------------- /src/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200507095248.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200507101946.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200507103021.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200507103254.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200507101751.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200507101901.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200507102024.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200507102118.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200507102257.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | -------------------------------------------------------------------------------- /.history/src/popup/App_20200507102320.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | --------------------------------------------------------------------------------