├── .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 | 
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 |
2 |
3 |

4 |
5 |
11 | {{ dialogBody }}
12 | You are not allowed to access the API.
13 |
14 | You might need to provide an access token.
15 | Follow this link to create one and paste it below (recommended).
21 |
22 |
23 |
24 |
25 | Save
26 |
27 | or simply
28 |
29 |
34 | Login with GitHub
35 |
36 | Why is this required?
41 |
42 |
43 |
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 |
2 |
23 |
24 |
25 |
30 |
31 |
61 |
--------------------------------------------------------------------------------
/src/views/BasicLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
24 |
25 |
28 |
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hubble
6 |
7 | Travel through GitHub Stars' history
8 |
9 |
10 |
27 | {{ errorMessage}}
28 |
29 |
30 |
31 | Share
32 |
33 |
34 |
35 |
36 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
447 |
448 |
449 |
567 |
--------------------------------------------------------------------------------
/src/views/MultiRepo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | {{ requesting ? 'Counting stars...' : 'Race'}}
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | Share
48 |
49 |
50 |
51 |
52 |
59 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
586 |
587 |
693 |
--------------------------------------------------------------------------------
/src/views/MyStars.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hubble
6 |
7 | Travel through GitHub Stars' history
8 |
9 |
10 |
11 |
13 |
14 | {{ !requesting ? `${viewer ? stargazersCount : 'Start'}` : `Counting stars (${stargazersCount})`}}
15 |
16 |
17 |
18 |
19 |
20 |
26 |
27 |
28 |
29 |
30 |
37 |
38 |
39 |
40 |
222 |
223 |
224 |
326 |
--------------------------------------------------------------------------------
/src/views/ReactVsVue.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hubble
6 |
7 | Travel through GitHub Stars' history
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 100k race, React vs. Vue
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Latest: {{ reactLast.starredAt | datetime }} by {{ reactLast.node.login }}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | Latest: {{ vueLast.starredAt | datetime }} by {{ vueLast.node.login }}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
50 |
51 | Share
52 |
53 |
54 |
55 |
56 |
63 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | or
81 |
129 |
130 |
131 |
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 |
--------------------------------------------------------------------------------