├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .postcssrc.js ├── .travis.yml ├── LICENSE ├── README.md ├── babel.config.js ├── index.html ├── package.json ├── public ├── favicon.png └── index.html ├── src ├── App.vue ├── assets │ └── logo.png ├── bus.js ├── graphql │ └── apollo.js ├── lib │ ├── ECharts.js │ └── ElementUI.js ├── main.js ├── request.js ├── router │ └── index.js ├── utils.js └── views │ ├── AdvancedLayout.vue │ ├── BasicLayout.vue │ ├── Home.vue │ ├── MultiRepo.vue │ ├── MyStars.vue │ └── ReactVsVue.vue ├── static ├── .gitkeep └── react-vs-vue.png ├── vue.config.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | parser: 'babel-eslint' 7 | }, 8 | env: { 9 | browser: true, 10 | }, 11 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 12 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 13 | extends: ['plugin:vue/essential'], 14 | // required to lint *.vue files 15 | plugins: [ 16 | 'vue' 17 | ], 18 | // add your custom rules here 19 | rules: { 20 | // allow debugger during development 21 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "lts/*" 4 | 5 | script: yarn run lint 6 | 7 | cache: 8 | yarn: true 9 | directories: 10 | - "node_modules" 11 | 12 | # use latest yarn 13 | before_install: 14 | - curl -o- -L https://yarnpkg.com/install.sh | bash 15 | - export PATH=$HOME/.yarn/bin:$PATH 16 | 17 | # fix upath incompatibility 18 | install: yarn --ignore-engines 19 | 20 | # GitHub Pages deploy 21 | before_deploy: 22 | - npm run predeploy 23 | 24 | deploy: 25 | - provider: pages 26 | skip-cleanup: true 27 | github-token: $GITHUB_TOKEN # Set in travis-ci.org dashboard, marked secure 28 | keep-history: true 29 | local-dir: dist 30 | on: 31 | branch: master 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Doma 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Screenshot](https://i.loli.net/2018/05/29/5b0d1c3190a01.png) 2 | 3 | ### Features 4 | - Reveal a repository's star history. 5 | - How many stars have I earned this year? 6 | 7 | ### Motivation 8 | I want to know how many stars I've earned this year. 9 | 10 | ## Let me know 11 | The initial repo to query is [js-org/js.org](https://github.com/js-org/js.org) by default, as my gratitude to its domain name service. Besides, a random repo from a preset repo list may also be chosen as the initial repo. If your project has over 1k stars and wants to join the list, [Let me know](https://github.com/SevenOutman/Hubble/issues/2)! 12 | 13 | ## FAQ 14 | 15 | ### Access Token 16 | > **Note:** Hubble stores access tokens in your browser local storage and never transmits it anywhere. 17 | 18 | #### Why is it required? 19 | Hubble uses [GitHub GraphQL API](https://developer.github.com/v4/) to retrieve repository stargazers. The API requires requests to be authenticated, 20 | so Hubble will ask for your [GitHub personal access token](https://help.github.com/articles/creating-an-access-token-for-command-line-use). 21 | If you don't already have one, [create one](https://github.com/settings/tokens/new), then copy and paste it into the textbox. 22 | 23 | #### Why is access token preferred rather than "Login with GitHub"? 24 | Hubble's OAuth service is provided by Netlify on a free plan basis, which has a 1,000 total users limit. 25 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Hubble 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hubble", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint", 9 | "predeploy": "yarn run build && echo 'hubble.js.org' > dist/CNAME && echo '/* /index.html 200' > dist/_redirects" 10 | }, 11 | "dependencies": { 12 | "apollo-cache-inmemory": "^1.4.2", 13 | "apollo-client": "^2.4.12", 14 | "apollo-link": "^1.2.8", 15 | "apollo-link-http": "^1.5.11", 16 | "axios": "^0.18.0", 17 | "echarts": "^4.1.0", 18 | "element-ui": "^2.5.4", 19 | "graphql": "^14.1.1", 20 | "graphql-tag": "^2.10.1", 21 | "lodash.merge": "^4.6.1", 22 | "moment": "^2.24.0", 23 | "netlify-auth-providers": "^1.0.0-alpha5", 24 | "parse-link-header": "^1.0.1", 25 | "vue": "^2.5.22", 26 | "vue-apollo": "^3.0.0-beta.28", 27 | "vue-echarts": "^4.0.1", 28 | "vue-meta": "^1.5.8", 29 | "vue-router": "^3.0.1" 30 | }, 31 | "devDependencies": { 32 | "@vue/cli-plugin-babel": "^3.4.0", 33 | "@vue/cli-plugin-eslint": "^3.4.0", 34 | "@vue/cli-service": "^3.4.0", 35 | "babel-eslint": "^10.0.1", 36 | "eslint": "^5.8.0", 37 | "eslint-plugin-vue": "^5.0.0", 38 | "less": "^3.9.0", 39 | "less-loader": "^4.1.0", 40 | "prerender-spa-plugin": "^3.4.0", 41 | "vue-template-compiler": "^2.5.21" 42 | }, 43 | "eslintConfig": { 44 | "root": true, 45 | "env": { 46 | "node": true 47 | }, 48 | "extends": [ 49 | "plugin:vue/essential", 50 | "eslint:recommended" 51 | ], 52 | "rules": {}, 53 | "parserOptions": { 54 | "parser": "babel-eslint" 55 | } 56 | }, 57 | "postcss": { 58 | "plugins": { 59 | "autoprefixer": {} 60 | } 61 | }, 62 | "browserslist": [ 63 | "> 1%", 64 | "last 2 versions", 65 | "not ie <= 8" 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SevenOutman/Hubble/6a0bf1bc8526c7d5b52a582eec18990b3c88953a/public/favicon.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Hubble 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 |
29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 99 | 100 | 227 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SevenOutman/Hubble/6a0bf1bc8526c7d5b52a582eec18990b3c88953a/src/assets/logo.png -------------------------------------------------------------------------------- /src/bus.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue" 2 | 3 | export default new Vue() 4 | -------------------------------------------------------------------------------- /src/graphql/apollo.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { ApolloClient } from 'apollo-client' 3 | import { ApolloLink } from 'apollo-link' 4 | import { HttpLink } from 'apollo-link-http' 5 | import { InMemoryCache } from 'apollo-cache-inmemory' 6 | import VueApollo from 'vue-apollo' 7 | 8 | // @see https://github.com/apollographql/apollo-client/issues/2168#issuecomment-334122648 9 | const httpLink = ApolloLink.from([ 10 | (operation, forward) => { 11 | operation.setContext(context => ({ 12 | ...context, 13 | headers: { 14 | ...context.headers, 15 | Authorization: `token ${localStorage.getItem('access_token')}` 16 | } 17 | })) 18 | return forward(operation) 19 | }, 20 | new HttpLink({ 21 | // You should use an absolute URL here 22 | uri: 'https://api.github.com/graphql' 23 | }) 24 | ]) 25 | 26 | // Create the apollo client 27 | const apolloClient = new ApolloClient({ 28 | link: httpLink, 29 | cache: new InMemoryCache(), 30 | connectToDevTools: true, 31 | }) 32 | 33 | // Install the vue plugin 34 | Vue.use(VueApollo) 35 | 36 | const apolloProvider = new VueApollo({ 37 | defaultClient: apolloClient, 38 | }) 39 | 40 | export default apolloProvider.provide() 41 | -------------------------------------------------------------------------------- /src/lib/ECharts.js: -------------------------------------------------------------------------------- 1 | import ECharts from 'vue-echarts/components/ECharts' 2 | import 'echarts/lib/chart/line' 3 | import 'echarts/lib/chart/bar' 4 | import 'echarts/lib/component/tooltip' 5 | import 'echarts/lib/component/markPoint' 6 | import 'echarts/lib/component/markLine' 7 | import 'echarts/lib/component/dataZoom' 8 | 9 | export default ECharts 10 | -------------------------------------------------------------------------------- /src/lib/ElementUI.js: -------------------------------------------------------------------------------- 1 | import ElementUI from 'element-ui' 2 | import 'element-ui/lib/theme-chalk/index.css' 3 | 4 | export default ElementUI 5 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import Meta from 'vue-meta' 5 | import ElementUI from './lib/ElementUI' 6 | import ECharts from './lib/ECharts' 7 | import provide from './graphql/apollo' 8 | import App from './App' 9 | import router from './router' 10 | 11 | Vue.use(Meta) 12 | Vue.use(ElementUI) 13 | Vue.component('chart', ECharts) 14 | Vue.config.productionTip = false 15 | 16 | /* eslint-disable no-new */ 17 | new Vue({ 18 | el: '#app', 19 | router, 20 | provide, 21 | components: { App }, 22 | render: h => h(App), 23 | mounted() { 24 | // You'll need this for renderAfterDocumentEvent. 25 | document.dispatchEvent(new Event('render-event')) 26 | } 27 | }) 28 | -------------------------------------------------------------------------------- /src/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export const restRequest = (url, options = {}) => { 4 | const headers = { 5 | Accept: 'application/vnd.github.v3+json', 6 | } 7 | const token = localStorage.getItem('access_token') 8 | if (token) { 9 | headers.Authorization = `token ${token}` 10 | } 11 | return axios(url, { 12 | baseURL: 'https://api.github.com', 13 | headers, 14 | ...options 15 | }) 16 | } 17 | 18 | export const graphqlRequest = (query, variables) => { 19 | return axios(`/graphql`, { 20 | baseURL: 'https://api.github.com', 21 | method: 'post', 22 | data: { 23 | query, 24 | variables 25 | }, 26 | headers: { 27 | Authorization: `bearer ${localStorage.getItem('access_token')}`, 28 | }, 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import BasicLayout from '@/views/BasicLayout' 4 | import Home from '@/views/Home' 5 | import MyStars from '@/views/MyStars' 6 | import MultiRepo from '@/views/MultiRepo' 7 | import ReactVsVue from '@/views/ReactVsVue' 8 | 9 | Vue.use(Router) 10 | 11 | const router = new Router({ 12 | mode: 'history', 13 | routes: [ 14 | { 15 | path: '', 16 | component: BasicLayout, 17 | children: [ 18 | { 19 | path: '/', 20 | component: Home 21 | }, 22 | { 23 | path: '/my-stars-this-year', 24 | component: MyStars 25 | }, 26 | { 27 | path: '/repo-race', 28 | component: MultiRepo 29 | }, 30 | { 31 | path: '/react-vs-vue', 32 | component: ReactVsVue, 33 | meta: { 34 | title: 'React vs. Vue' 35 | } 36 | } 37 | ] 38 | }, 39 | ] 40 | }) 41 | 42 | // router.beforeEach((to, from, next) => { 43 | // if (to.meta && to.meta.title) { 44 | // document.title = `${to.meta.title} · Hubble` 45 | // } else { 46 | // document.title = 'Hubble' 47 | // } 48 | // next() 49 | // }) 50 | export default router 51 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import parseLinkHeader from 'parse-link-header' 3 | import { graphqlRequest } from '@/request'; 4 | 5 | function noop() { 6 | } 7 | 8 | export function restFetchStargazers(fullname, { 9 | onPageData = noop, 10 | onError = noop, 11 | onComplete = noop, 12 | }) { 13 | 14 | function fetchStargazersByPage(page = 1) { 15 | let fullData = [] 16 | 17 | return axios(`/repos/${fullname}/stargazers`, { 18 | baseURL: 'https://api.github.com', 19 | params: { 20 | page, 21 | per_page: 100, 22 | }, 23 | headers: { 24 | Accept: 'application/vnd.github.v3.star+json', 25 | Authorization: `token ${localStorage.getItem('access_token')}`, 26 | }, 27 | }).then(({ headers, data }) => { 28 | 29 | fullData = [...fullData, ...data] 30 | onPageData(data) 31 | const links = parseLinkHeader(headers['link']) 32 | if (links && links.next && +links.next.page) { 33 | fetchStargazersByPage(+links.next.page) 34 | } else { 35 | onComplete(fullData) 36 | } 37 | return data 38 | }) 39 | } 40 | 41 | 42 | return fetchStargazersByPage(1).catch(onError) 43 | } 44 | 45 | export function graphqlFetchStargazers(owner, name, { 46 | onPageData = noop, 47 | onError = noop, 48 | onComplete = noop, 49 | }) { 50 | 51 | const query = ` 52 | query RepoStars($owner: String!, $name: String!, $afterPointer: String) { 53 | repository(owner: $owner, name: $name) { 54 | createdAt 55 | stargazers(after: $afterPointer, first: 100) { 56 | edges { 57 | starredAt 58 | }, 59 | pageInfo { 60 | endCursor 61 | hasPreviousPage 62 | hasNextPage 63 | } 64 | totalCount 65 | } 66 | } 67 | } 68 | ` 69 | 70 | function fetchStargazersByPage(afterPointer = null) { 71 | let fullData = [] 72 | 73 | return axios(`/graphql`, { 74 | baseURL: 'https://api.github.com', 75 | method: 'post', 76 | data: { 77 | query, 78 | variables: { owner, name, afterPointer }, 79 | }, 80 | headers: { 81 | Authorization: `bearer ${localStorage.getItem('access_token')}`, 82 | }, 83 | }).then(({ data: { data: { repository: { stargazers: { edges, pageInfo: { endCursor, hasNextPage } } } } } }) => { 84 | fullData = [...fullData, ...edges] 85 | onPageData(edges) 86 | if (hasNextPage) { 87 | fetchStargazersByPage(endCursor) 88 | } else { 89 | onComplete(fullData) 90 | } 91 | return edges 92 | }) 93 | } 94 | 95 | 96 | return fetchStargazersByPage().catch(onError) 97 | } 98 | 99 | export function fetchStargazerCount(fullname) { 100 | const headers = { 101 | Accept: 'application/vnd.github.v3+json', 102 | } 103 | const token = localStorage.getItem('access_token') 104 | if (token) { 105 | headers.Authorization = `token ${token}` 106 | } 107 | return axios(`/repos/${fullname}`, { 108 | baseURL: 'https://api.github.com', 109 | headers 110 | }).then(({ data: { stargazers_count } }) => stargazers_count) 111 | } 112 | 113 | export function graphqlFetchStargazerCount(owner, name) { 114 | const query = ` 115 | query Repo($owner: String!, $name: String!) { 116 | repository(owner: $owner, name: $name) { 117 | stargazers(last: 1) { 118 | edges { 119 | starredAt 120 | node { 121 | login 122 | } 123 | } 124 | totalCount 125 | } 126 | } 127 | }` 128 | return axios(`/graphql`, { 129 | baseURL: 'https://api.github.com', 130 | method: 'post', 131 | data: { 132 | query, 133 | variables: { owner, name }, 134 | }, 135 | headers: { 136 | Authorization: `bearer ${localStorage.getItem('access_token')}`, 137 | }, 138 | }).then(({ data: { errors, data } }) => { 139 | if (errors) { 140 | return Promise.reject(errors) 141 | } 142 | const { repository: { stargazers: { totalCount, edges } } } = data 143 | return { total: totalCount, last: edges[0] } 144 | }) 145 | 146 | } 147 | 148 | export function graphqlFetchViewerBestarredRepos( 149 | { 150 | onPageData = noop, 151 | onComplete = noop, 152 | onError = noop 153 | }) { 154 | 155 | const query = ` 156 | query ViewerRepos($after: String) { 157 | viewer { 158 | login 159 | repositories(first: 100, after: $after, orderBy: { field: STARGAZERS, direction: DESC }) { 160 | nodes { 161 | nameWithOwner 162 | stargazers(first: 1, orderBy: { direction: DESC, field: STARRED_AT }) { 163 | edges { 164 | starredAt 165 | } 166 | totalCount 167 | } 168 | } 169 | pageInfo { 170 | endCursor 171 | hasNextPage 172 | } 173 | } 174 | } 175 | } 176 | ` 177 | 178 | const year = (new Date()).getFullYear() 179 | let viewer = '' 180 | let repositories = [] 181 | 182 | function fetchReposByPage(after = null) { 183 | 184 | return graphqlRequest(query, { after }) 185 | .then(({ data: { data: { viewer: { login, repositories: { nodes, pageInfo: { endCursor, hasNextPage } } } } } }) => { 186 | viewer = viewer || login 187 | const bestarredRepos = nodes.filter(({ stargazers: { totalCount } }) => totalCount > 0) 188 | const bestarredThisYear = bestarredRepos.filter(({ stargazers: { edges } }) => { 189 | const edge = edges[0] 190 | return edge && edge.starredAt.substr(0, 4) === `${year}` 191 | }) 192 | repositories = [...repositories, ...bestarredThisYear] 193 | onPageData({ viewer, repositories }) 194 | if (hasNextPage && bestarredRepos.length === nodes.length) { 195 | fetchReposByPage(endCursor) 196 | } else { 197 | onComplete({ viewer, repositories }) 198 | } 199 | return repositories 200 | }) 201 | } 202 | 203 | 204 | return fetchReposByPage().catch(onError) 205 | } 206 | 207 | 208 | export function graphqlFetchRepoStarsThisYear(owner, name, { 209 | onPageData = noop, 210 | onComplete = noop, 211 | onError = noop 212 | }) { 213 | 214 | const query = ` 215 | query RepoStars($owner: String!, $name: String!, $after: String) { 216 | repository(owner: $owner, name: $name) { 217 | stargazers(after: $after, first: 100, orderBy: { direction: DESC, field: STARRED_AT }) { 218 | edges { 219 | starredAt 220 | } 221 | pageInfo { 222 | endCursor 223 | hasPreviousPage 224 | hasNextPage 225 | } 226 | } 227 | } 228 | } 229 | ` 230 | 231 | const year = (new Date()).getFullYear() 232 | let fullData = [] 233 | 234 | function fetchStargazersAfter(after = null) { 235 | 236 | return graphqlRequest(query, { owner, name, after }) 237 | .then(({ data: { data: { repository: { stargazers: { edges, pageInfo: { hasNextPage, endCursor } } } } } }) => { 238 | const starsThisYear = edges.map(edge => edge.starredAt).filter(isoDate => isoDate.substr(0, 4) === `${year}`) 239 | fullData = [...fullData, ...starsThisYear] 240 | onPageData(starsThisYear) 241 | if (hasNextPage && starsThisYear.length === edges.length) { 242 | fetchStargazersAfter(endCursor) 243 | } else { 244 | onComplete(fullData) 245 | } 246 | return fullData 247 | }) 248 | } 249 | 250 | 251 | return fetchStargazersAfter().catch(onError) 252 | } 253 | -------------------------------------------------------------------------------- /src/views/AdvancedLayout.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 30 | 31 | 61 | -------------------------------------------------------------------------------- /src/views/BasicLayout.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 24 | 25 | 28 | -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 447 | 448 | 449 | 567 | -------------------------------------------------------------------------------- /src/views/MultiRepo.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | 586 | 587 | 693 | -------------------------------------------------------------------------------- /src/views/MyStars.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 222 | 223 | 224 | 326 | -------------------------------------------------------------------------------- /src/views/ReactVsVue.vue: -------------------------------------------------------------------------------- 1 | 132 | 133 | 817 | 818 | 974 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SevenOutman/Hubble/6a0bf1bc8526c7d5b52a582eec18990b3c88953a/static/.gitkeep -------------------------------------------------------------------------------- /static/react-vs-vue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SevenOutman/Hubble/6a0bf1bc8526c7d5b52a582eec18990b3c88953a/static/react-vs-vue.png -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const PrerenderSPAPlugin = require('prerender-spa-plugin') 3 | const Renderer = PrerenderSPAPlugin.PuppeteerRenderer 4 | 5 | module.exports = { 6 | configureWebpack: config => { 7 | if (process.env.NODE_ENV === 'production') { 8 | return { 9 | plugins: [ 10 | new PrerenderSPAPlugin({ 11 | staticDir: path.join(__dirname, 'dist'), 12 | routes: ['/react-vs-vue'], 13 | 14 | renderer: new Renderer({ 15 | inject: { 16 | headless: true, 17 | }, 18 | // @see https://github.com/chrisvfritz/prerender-spa-plugin/issues/200#issuecomment-391204354 19 | // REMOVED headless: false, 20 | 21 | // @see https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#chrome-headless-fails-due-to-sandbox-issues 22 | args: ['--no-sandbox', '--disable-setuid-sandbox'], 23 | renderAfterDocumentEvent: 'render-event', 24 | }), 25 | }), 26 | ], 27 | } 28 | } 29 | }, 30 | } 31 | --------------------------------------------------------------------------------