Commit d4f9448a authored by Pooya Parsa's avatar Pooya Parsa

feat: rewrite project with hnpwa api

parent 2c64c349
import { initializeApp, database } from "firebase/app"
export async function createAPI({ config, version }) {
await import(/* webpackChunkName: "firebase" */ "firebase/database")
initializeApp(config)
return database().ref(version)
}
import Firebase from "firebase"
import LRU from "lru-cache"
export async function createAPI({ config, version }) {
let api
// this piece of code may run multiple times in development mode,
// so we attach the instantiated API to `process` to avoid duplications
if (process.__API__) {
api = process.__API__
} else {
Firebase.initializeApp(config)
api = process.__API__ = Firebase.database().ref(version)
api.onServer = true
// fetched item cache
api.cachedItems = LRU({
max: 1000,
maxAge: 1000 * 60 * 15 // 15 min cache
})
// cache the latest story ids
api.cachedIds = {}
;["top", "new", "show", "ask", "job"].forEach(type => {
api.child(`${type}stories`).on("value", snapshot => {
api.cachedIds[type] = snapshot.val()
})
})
}
return api
}
// This is aliased in webpack config based on server/client build
import { createAPI } from "create-api"
const logRequests = !!process.env.DEBUG_API
let api = {}
let _api = createAPI({
version: "/v0",
config: {
databaseURL: "https://hacker-news.firebaseio.com"
}
}).then(_api => {
api = _api
// warm the front page cache every 15 min
// make sure to do this only once across all requests
if (api.onServer) {
warmCache()
}
})
function warmCache() {
if (!api.cachedIds) return
fetchItems((api.cachedIds.top || []).slice(0, 30))
setTimeout(warmCache, 1000 * 60 * 15)
}
async function fetch(child) {
logRequests && console.log(`fetching ${child}...`)
await _api
const cache = api.cachedItems
if (cache && cache.has(child)) {
logRequests && console.log(`cache hit for ${child}.`)
return cache.get(child)
} else {
return new Promise((resolve, reject) => {
api.child(child).once(
"value",
snapshot => {
const val = snapshot.val()
// mark the timestamp when this item is cached
if (val) val.__lastUpdated = Date.now()
cache && cache.set(child, val)
logRequests && console.log(`fetched ${child}.`)
resolve(val)
},
reject
)
})
}
}
export function fetchIdsByType(type) {
return api.cachedIds && api.cachedIds[type]
? Promise.resolve(api.cachedIds[type])
: fetch(`${type}stories`)
}
export function fetchItem(id) {
return fetch(`item/${id}`)
}
export function fetchItems(ids) {
return Promise.all(ids.map(id => fetchItem(id)))
}
export function fetchUser(id) {
return fetch(`user/${id}`)
}
export async function watchList(type, cb) {
let first = true
await _api
const ref = api.child(`${type}stories`)
const handler = snapshot => {
if (first) {
first = false
} else {
cb(snapshot.val())
}
}
ref.on("value", handler)
return () => {
ref.off("value", handler)
}
}
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head>
{{ HEAD }}
<style>
#skip a { position:absolute; left:-10000px; top:auto; width:1px; height:1px; overflow:hidden; }
#skip a:focus { position:static; width:auto; height:auto; }
</style>
</head>
<body {{ BODY_ATTRS }}>
<div id="skip"><a href="#app">skip to content</a></div>
{{ APP }}
</body>
</html>
export const feeds = {
news: { title: "News", pages: 10 },
newest: { title: "Newest", pages: 12 },
ask: { title: "Ask", pages: 2 },
show: { title: "Show", pages: 2 },
jobs: { title: "Jobs", pages: 1 }
}
export const validFeeds = Object.keys(feeds)
export const lazy = (commit, task, optimistic, enabled) => {
// By default, do lazy operations only in client
if (enabled === undefined) {
enabled = process.client
}
// Non lazy mode
if (!enabled) {
return task().then(commit)
}
// Do real task in background
Promise.resolve(task(optimistic))
.then(commit)
.catch(console.error)
// Commit optimistic value and resolve
return Promise.resolve(commit(optimistic))
}
<template> <template>
<li v-if="comment" class="comment"> <li v-if="comment" class="comment">
<div class="by"> <div class="by">
<router-link :to="'/user/' + comment.by">{{ comment.by }}</router-link> <router-link :to="'/user/' + comment.user">{{ comment.user }}</router-link>
{{ comment.time | timeAgo }} ago {{ comment.time | timeAgo }} ago
</div> </div>
<div class="text" v-html="comment.text" /> <div class="text" v-html="comment.content" />
<div v-if="comment.kids && comment.kids.length" :class="{ open }" class="toggle"> <div v-if="comment.comments && comment.comments.length" :class="{ open }" class="toggle">
<a @click="open = !open">{{ open ? '[-]' : '[+] ' + pluralize(comment.kids.length) + ' collapsed' }} <a @click="open = !open">{{ open ? '[-]' : '[+] ' + pluralize(comment.comments.length) + ' collapsed' }}
</a> </a>
</div> </div>
<ul v-show="open" class="comment-children"> <ul v-show="open" class="comment-children">
<comment v-for="id in comment.kids" :key="id" :id="id" /> <comment v-for="childComment in comment.comments" :key="childComment.id" :comment="childComment" />
</ul> </ul>
</li> </li>
</template> </template>
...@@ -19,8 +19,8 @@ ...@@ -19,8 +19,8 @@
export default { export default {
name: "Comment", name: "Comment",
props: { props: {
id: { comment: {
type: String, type: Object,
required: true required: true
} }
}, },
...@@ -29,11 +29,6 @@ export default { ...@@ -29,11 +29,6 @@ export default {
open: true open: true
} }
}, },
computed: {
comment() {
return this.$store.state.items[this.id]
}
},
methods: { methods: {
pluralize: n => n + (n === 1 ? " reply" : " replies") pluralize: n => n + (n === 1 ? " reply" : " replies")
} }
......
<template> <template>
<li class="news-item"> <li class="news-item">
<span class="score">{{ item.score }}</span> <span class="score">{{ item.points }}</span>
<span class="title"> <span class="title">
<template v-if="item.url"> <template v-if="item.url">
<a :href="item.url" target="_blank" rel="noopener">{{ item.title }}</a> <a :href="item.url" target="_blank" rel="noopener">{{ item.title }}</a>
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<span class="meta"> <span class="meta">
<span v-if="item.type !== 'job'" class="by"> <span v-if="item.type !== 'job'" class="by">
by by
<router-link :to="'/user/' + item.by">{{ item.by }}</router-link> <router-link :to="'/user/' + item.user">{{ item.user }}</router-link>
</span> </span>
<span class="time"> <span class="time">
{{ item.time | timeAgo }} ago {{ item.time | timeAgo }} ago
......
import ItemList from "./ItemList.vue"
const camelize = str => str.charAt(0).toUpperCase() + str.slice(1)
// This is a factory function for dynamically creating root-level list views,
// since they share most of the logic except for the type of items to display.
// They are essentially higher order components wrapping ItemList.vue.
export default function createListView(type) {
return {
name: `${type}-stories-view`,
fetch({ store }) {
return store.dispatch("FETCH_LIST_DATA", { type })
},
head: {
title: camelize(type)
},
render(h) {
return h(ItemList, { props: { type } })
}
}
}
<template> <template>
<div class="news-list-nav"> <div class="news-list-nav">
<router-link v-if="page > 1" :to="`/${type}/${page - 1}`">&lt; prev</router-link> <router-link v-if="page > 1" :to="`/${feed}/${page - 1}`">&lt; prev</router-link>
<a v-else class="disabled">&lt; prev</a> <a v-else class="disabled">&lt; prev</a>
<span>{{ page }}/{{ maxPage }}</span> <span>{{ page }}/{{ maxPage }}</span>
<router-link v-if="hasMore" :to="`/${type}/${page + 1}`">more &gt;</router-link> <router-link v-if="hasMore" :to="`/${feed}/${page + 1}`">more &gt;</router-link>
<a v-else class="disabled">more &gt;</a> <a v-else class="disabled">more &gt;</a>
</div> </div>
</template> </template>
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<script> <script>
export default { export default {
props: { props: {
type: { feed: {
type: String, type: String,
required: true required: true
}, },
......
<script>
import Spinner from "./spinner"
export default {
functional: true,
props: {
loading: {
type: Boolean,
default: false
}
},
render(h, { props, children }) {
return props.loading
? h("div", { style: { "text-align": "center" } }, [
h(Spinner, { props: { show: true } })
])
: children
}
}
</script>
...@@ -5,11 +5,9 @@ ...@@ -5,11 +5,9 @@
<router-link to="/" exact> <router-link to="/" exact>
<img class="logo" src="~assets/logo.png" alt="logo"> <img class="logo" src="~assets/logo.png" alt="logo">
</router-link> </router-link>
<router-link to="/top">Top</router-link> <router-link v-for="(list, key) in feeds" :key="key" :to="`/${key}`">
<router-link to="/new">New</router-link> {{ list.title }}
<router-link to="/show">Show</router-link> </router-link>
<router-link to="/ask">Ask</router-link>
<router-link to="/job">Jobs</router-link>
<a class="github" href="https://github.com/nuxt/hackernews" target="_blank" rel="noopener"> <a class="github" href="https://github.com/nuxt/hackernews" target="_blank" rel="noopener">
Built with Nuxt.js Built with Nuxt.js
</a> </a>
...@@ -20,6 +18,8 @@ ...@@ -20,6 +18,8 @@
</template> </template>
<script> <script>
import { feeds } from "~/common/api"
export default { export default {
head() { head() {
return { return {
...@@ -28,6 +28,9 @@ export default { ...@@ -28,6 +28,9 @@ export default {
{ rel: "canonical", href: `https://hn.nuxtjs.org${this.$route.path}` } { rel: "canonical", href: `https://hn.nuxtjs.org${this.$route.path}` }
] ]
} }
},
computed: {
feeds: () => feeds
} }
} }
</script> </script>
......
export default ({ isDev, req, redirect }) => {
// Redirect to https
if (!isDev && req) {
const protocol =
req.headers["x-forwarded-proto"] ||
(req.connection.encrypted ? "https" : "http")
if (protocol === "http") {
return redirect(301, `https://${req.headers.host}${req.url}`)
}
}
}
module.exports = { module.exports = {
mode: "universal",
build: {
extend(config, { isClient, isDev }) {
// Run ESLint on save
if (isDev && isClient) {
config.module.rules.push({
enforce: "pre",
test: /\.(js|vue)$/,
loader: "eslint-loader",
exclude: /(node_modules)/
})
}
config.resolve.alias["create-api"] = `./create-api-${
isClient ? "client" : "server"
}.js`
},
vendor: ["firebase"]
},
head: { head: {
titleTemplate: "Nuxt HN | %s", titleTemplate: "Nuxt HN | %s",
meta: [ meta: [
...@@ -28,10 +10,7 @@ module.exports = { ...@@ -28,10 +10,7 @@ module.exports = {
{ property: "twitter:card", content: "summary_large_image" }, { property: "twitter:card", content: "summary_large_image" },
{ property: "twitter:site", content: "@nuxt_js" } { property: "twitter:site", content: "@nuxt_js" }
], ],
link: [ link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" }]
{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" },
{ rel: "dns-prefetch", href: "https://hacker-news.firebaseio.com" }
]
}, },
loading: { loading: {
color: "#59cc93" color: "#59cc93"
...@@ -46,15 +25,17 @@ module.exports = { ...@@ -46,15 +25,17 @@ module.exports = {
description: "HackerNews clone built with Nuxt.js", description: "HackerNews clone built with Nuxt.js",
theme_color: "#188269" theme_color: "#188269"
}, },
modules: ["@nuxtjs/pwa", "@nuxtjs/component-cache"], modules: ["@nuxtjs/pwa", "@nuxtjs/component-cache", "@nuxtjs/axios"],
plugins: [ axios: {
"~/plugins/vuex-router-sync", proxy: true
"~/plugins/filters", },
"~/plugins/components" proxy: {
], "/api": {
router: { target: "https://api.hnpwa.com/v0/",
middleware: ["https"] pathRewrite: { "^/api/": "" }
}
}, },
plugins: ["~/plugins/filters"],
render: { render: {
static: { static: {
maxAge: "1y", maxAge: "1y",
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
"node": ">=8.0" "node": ">=8.0"
}, },
"dependencies": { "dependencies": {
"@nuxtjs/axios": "^5.1.1",
"@nuxtjs/component-cache": "^1.1.1", "@nuxtjs/component-cache": "^1.1.1",
"@nuxtjs/pwa": "2.0.8", "@nuxtjs/pwa": "2.0.8",
"nuxt-edge": "^2.0.0-25364965.06067bf", "nuxt-edge": "^2.0.0-25364965.06067bf",
......
<template> <template>
<div class="news-view view"> <div class="view">
<item-list-nav :type="type" :page="page" :max-page="maxPage" /> <item-list-nav :feed="feed" :page="page" :max-page="maxPage" />
<transition :name="transition" mode="out-in">
<div v-if="displayedPage > 0" :key="displayedPage" class="news-list"> <lazy-wrapper :loading="loading">
<transition-group tag="ul" name="item"> <transition :name="transition" mode="out-in">
<item v-for="item in displayedItems" :key="item.id" :item="item" /> <div :key="displayedPage" class="news-list">
</transition-group> <transition-group tag="ul" name="item">
</div> <item v-for="item in displayedItems" :key="item.id" :item="item" />
</transition> </transition-group>
<item-list-nav :type="type" :page="page" :max-page="maxPage" /> </div>
</transition>
<item-list-nav :feed="feed" :page="page" :max-page="maxPage" />
</lazy-wrapper>
</div> </div>
</template> </template>
<script> <script>
import { watchList } from "../api" import Item from "~/components/item.vue"
import Item from "./Item.vue" import ItemListNav from "~/components/item-list-nav.vue"
import ItemListNav from "./ItemListNav.vue" import LazyWrapper from "~/components/lazy-wrapper"
import { feeds, validFeeds } from "~/common/api"
export default { export default {
name: "ItemList",
components: { components: {
Item, Item,
ItemListNav ItemListNav,
LazyWrapper
},
validate({ params: { feed } }) {
return validFeeds.includes(feed)
},
fetch({ store, params: { feed, page = 1 } }) {
return store.dispatch("FETCH_FEED", { feed, page })
}, },
props: {
type: { head() {
type: String, return {
required: true title: feeds[this.$route.params.feed].title
} }
}, },
data() { data() {
return { return {
transition: "slide-right", transition: "slide-right",
displayedPage: Number(this.$route.params.page) || 1, displayedPage: Number(this.page) || 1
displayedItems: this.$store.getters.activeItems
} }
}, },
computed: { computed: {
feed() {
return this.$route.params.feed
},
page() { page() {
return Number(this.$route.params.page) || 1 return Number(this.$route.params.page) || 1
}, },
maxPage() { maxPage() {
const { itemsPerPage, lists } = this.$store.state return feeds[this.feed].pages
return Math.ceil(lists[this.type].length / itemsPerPage) },
pageData() {
return this.$store.state.feeds[this.feed][this.page]
},
displayedItems() {
return this.pageData.map(id => this.$store.state.items[id])
},
loading() {
return this.displayedItems.length === 0
} }
}, },
watch: { watch: {
page(to, from) { page: "pageChanged"
console.log("Page changed", to, from)
this.loadItems(to, from)
}
}, },
async beforeMount() { mounted() {
if (this.$root._isMounted) { this.pageChanged(this.page)
this.loadItems(this.page)
}
// watch the current list for realtime updates
this.unwatchList = await watchList(this.type, ids => {
this.$store.commit("SET_LIST", { type: this.type, ids })
this.$store.dispatch("ENSURE_ACTIVE_ITEMS").then(() => {
this.displayedItems = this.$store.getters.activeItems
})
})
},
beforeDestroy() {
this.unwatchList()
}, },
methods: { methods: {
loadItems(to = this.page, from = -1) { pageChanged(to, from = -1) {
this.$nuxt.$loading.start() if (to < 0 || to > this.maxPage) {
this.$router.replace(`/${this.feed}/1`)
return
}
// Prefetch next page
this.$store this.$store
.dispatch("FETCH_LIST_DATA", { .dispatch("FETCH_FEED", {
type: this.type feed: this.feed,
}) page: this.page + 1,
.then(() => { prefetch: true
if (this.page < 0 || this.page > this.maxPage) {
this.$router.replace(`/${this.type}/1`)
return
}
this.transition =
from === -1 ? null : to > from ? "slide-left" : "slide-right"
this.displayedPage = to
this.displayedItems = this.$store.getters.activeItems
this.$nuxt.$loading.finish()
}) })
.catch(() => {})
this.transition =
from === -1 ? null : to > from ? "slide-left" : "slide-right"
this.displayedPage = to
} }
} }
} }
......
<script>
import createListView from "~/components/createListView"
export default createListView("ask")
</script>
<script> <script>
import { validFeeds } from "~/common/api"
export default { export default {
fetch({ redirect }) { fetch({ redirect }) {
redirect("/top") redirect("/" + validFeeds[0])
} }
} }
</script> </script>
<template> <template>
<div v-if="item" class="item-view view" > <div class="item-view view" >
<template v-if="item"> <div class="item-view-header">
<div class="item-view-header"> <a :href="item.url" target="_blank">
<a :href="item.url" target="_blank"> <h1 v-html="item.title" />
<h1 v-html="item.title" /> </a>
</a> <span v-if="item.url" class="host">
<span v-if="item.url" class="host"> ({{ item.url | host }})
({{ item.url | host }}) </span>
</span> <p class="meta">
<p class="meta"> {{ item.points }} points | by
{{ item.score }} points | by <router-link :to="'/user/' + item.user">{{ item.user }}</router-link>
<router-link :to="'/user/' + item.by">{{ item.by }}</router-link> {{ item.time | timeAgo }} ago
{{ item.time | timeAgo }} ago </p>
</p> </div>
</div> <div class="item-view-comments">
<div class="item-view-comments"> <lazy-wrapper :loading="item.loading">
<p class="item-view-comments-header"> <p class="item-view-comments-header">
{{ item.kids ? item.descendants + ' comments' : 'No comments yet.' }} {{ item.comments ? item.comments.length + ' comments' : 'No comments yet.' }}
<spinner :show="loading"/>
</p> </p>
<ul v-if="!loading" class="comment-children"> <ul class="comment-children">
<comment v-for="id in item.kids" :key="id" :id="id"/> <comment v-for="comment in item.comments" :key="comment.id" :comment="comment"/>
</ul> </ul>
</div> </lazy-wrapper>
</template> </div>
</div> </div>
</template> </template>
<script> <script>
import Spinner from "~/components/Spinner.vue" import Comment from "~/components/comment.vue"
import Comment from "~/components/Comment.vue" import LazyWrapper from "~/components/lazy-wrapper"
export default { export default {
name: "ItemView", name: "ItemView",
components: { Spinner, Comment }, components: { Comment, LazyWrapper },
data: () => ({
loading: true
}),
head() { head() {
return { return {
...@@ -46,54 +41,17 @@ export default { ...@@ -46,54 +41,17 @@ export default {
}, },
computed: { computed: {
id() {
return this.$route.params.id
},
item() { item() {
return this.$store.state.items[this.$route.params.id] return this.$store.state.items[this.id]
} }
}, },
// We only fetch the item itself before entering the view, because fetch({ store, params: { id } }) {
// it might take a long time to load threads with hundreds of comments return store.dispatch("FETCH_ITEM", { id })
// due to how the HN Firebase API works.
asyncData({ store, route: { params: { id } } }) {
return store.dispatch("FETCH_ITEMS", { ids: [id] })
},
// refetch comments if item changed
watch: {
item: "fetchComments"
},
// Fetch comments when mounted on the client
beforeMount() {
this.fetchComments()
},
methods: {
fetchComments() {
this.loading = true
fetchComments(this.$store, this.item).then(() => {
this.loading = false
})
}
}
}
// recursively fetch all descendent comments
function fetchComments(store, item) {
if (item && item.kids) {
return store
.dispatch("FETCH_ITEMS", {
ids: item.kids
})
.then(() =>
Promise.all(
item.kids.map(id => {
return fetchComments(store, store.state.items[id])
})
)
)
} }
return Promise.resolve()
} }
</script> </script>
......
<script>
import createListView from "~/components/createListView"
export default createListView("job")
</script>
<script>
import createListView from "~/components/createListView"
export default createListView("new")
</script>
<script>
import createListView from "~/components/createListView"
export default createListView("show")
</script>
<script>
import createListView from "~/components/createListView"
export default createListView("top")
</script>
...@@ -2,28 +2,34 @@ ...@@ -2,28 +2,34 @@
<div class="user-view view"> <div class="user-view view">
<template v-if="user"> <template v-if="user">
<h1>User : {{ user.id }}</h1> <h1>User : {{ user.id }}</h1>
<ul class="meta"> <lazy-wrapper :loading="user.loading">
<li> <ul class="meta">
<span class="label">Created:</span> {{ user.created | timeAgo }} ago</li> <li>
<li> <span class="label">Created:</span> {{ user.created_time | timeAgo }} ago</li>
<span class="label">Karma:</span> {{ user.karma }}</li> <li>
<li v-if="user.about" class="about" v-html="user.about" /> <span class="label">Karma:</span> {{ user.karma || '-' }}</li>
</ul> <li v-if="user.about" class="about" v-html="user.about" />
</ul>
</lazy-wrapper>
<p class="links"> <p class="links">
<a :href="'https://news.ycombinator.com/submitted?id=' + user.id">submissions</a> | <a :href="'https://news.ycombinator.com/submitted?id=' + user.id">submissions</a> |
<a :href="'https://news.ycombinator.com/threads?id=' + user.id">comments</a> <a :href="'https://news.ycombinator.com/threads?id=' + user.id">comments</a>
</p> </p>
</template> </template>
<template v-else-if="user === false"> <template v-else>
<h1>User not found.</h1> <h1>User not found.</h1>
</template> </template>
</div> </div>
</template> </template>
<script> <script>
import LazyWrapper from "~/components/lazy-wrapper"
export default { export default {
name: "UserView", name: "UserView",
components: { LazyWrapper },
computed: { computed: {
user() { user() {
return this.$store.state.users[this.$route.params.id] return this.$store.state.users[this.$route.params.id]
...@@ -31,9 +37,7 @@ export default { ...@@ -31,9 +37,7 @@ export default {
}, },
head() { head() {
return { return this.user ? this.user.id : "User not found"
title: this.user.id || "User not found"
}
}, },
fetch({ store, route: { params: { id } } }) { fetch({ store, route: { params: { id } } }) {
......
// Import common components to optimize chunks size
import "~/components/createListView"
import { sync } from "vuex-router-sync"
export default function({ app, store }) {
sync(store, app.router)
}
import { fetchUser, fetchItems, fetchIdsByType } from "../api"
export default {
// ensure data for rendering given list type
FETCH_LIST_DATA: ({ commit, dispatch }, { type }) => {
commit("SET_ACTIVE_TYPE", { type })
return fetchIdsByType(type)
.then(ids => commit("SET_LIST", { type, ids }))
.then(() => dispatch("ENSURE_ACTIVE_ITEMS"))
},
// ensure all active items are fetched
ENSURE_ACTIVE_ITEMS: ({ dispatch, getters }) => {
return dispatch("FETCH_ITEMS", {
ids: getters.activeIds
})
},
FETCH_ITEMS: ({ commit, state }, { ids }) => {
// on the client, the store itself serves as a cache.
// only fetch items that we do not already have, or has expired (3 minutes)
const now = Date.now()
ids = ids.filter(id => {
const item = state.items[id]
if (!item) {
return true
}
if (now - item.__lastUpdated > 1000 * 60 * 3) {
return true
}
return false
})
if (ids.length) {
return fetchItems(ids).then(items => commit("SET_ITEMS", { items }))
} else {
return Promise.resolve()
}
},
FETCH_USER: ({ commit, state }, { id }) => {
return state.users[id]
? Promise.resolve(state.users[id])
: fetchUser(id).then(user => commit("SET_USER", { id, user }))
}
}
export default {
// ids of the items that should be currently displayed based on
// current list type and current pagination
activeIds(state) {
const { activeType, itemsPerPage, lists } = state
if (!activeType) {
return []
}
const page = Number(state.route.params.page) || 1
const start = (page - 1) * itemsPerPage
const end = page * itemsPerPage
return lists[activeType].slice(start, end)
},
// items that should be currently displayed.
// this Array may not be fully fetched.
activeItems(state, getters) {
return getters.activeIds.map(id => state.items[id]).filter(_ => _)
}
}
import Vuex from "vuex" import Vue from "vue"
import actions from "./actions" import { validFeeds } from "~/common/api"
import mutations from "./mutations" import { lazy } from "~/common/utils"
import getters from "./getters" import { CancelToken } from "axios"
export default () => { export default {
return new Vuex.Store({ // =================================================
state: { // State
activeType: null, // =================================================
itemsPerPage: 20, state: () => {
const state = {
items: { items: {
/* [id: number]: Item */ /* [id: number]: Item */
}, },
users: { users: {
/* [id: string]: User */ /* [id: string]: User */
}, },
lists: { feeds: {
top: [ /* [page: number] : [ [id: number] ] */
/* number */
],
new: [],
show: [],
ask: [],
job: []
} }
}
validFeeds.forEach(feed => {
state.feeds[feed] = {}
})
return state
},
// =================================================
// Actions
// =================================================
actions: {
FETCH_FEED({ commit, state }, { feed, page, prefetch }) {
if (!prefetch) {
if (this.feedCancelSource) {
this.feedCancelSource.cancel(
"priorotize feed: " + feed + " page: " + page
)
}
this.feedCancelSource = CancelToken.source()
}
return lazy(
items => {
const ids = items.map(item => item.id)
commit("SET_FEED", { feed, ids, page })
commit("SET_ITEMS", { items })
},
() =>
this.$axios.$get(`/api/${feed}/${page}.json`, {
cancelToken: this.feedCancelSource && this.feedCancelSource.token
}),
(state.feeds[feed][page] || []).map(id => state.items[id])
)
},
FETCH_ITEM({ commit, state }, { id }) {
return lazy(
item => commit("SET_ITEM", { item }),
() => this.$axios.$get(`/api/item/${id}.json`),
Object.assign({ id, loading: true, comments: [] }, state.items[id])
)
},
FETCH_USER({ state, commit }, { id }) {
return lazy(
user => commit("SET_USER", { id, user }),
() => this.$axios.$get(`/api/user/${id}.json`),
Object.assign({ id, loading: true }, state.users[id])
)
}
},
// =================================================
// Mutations
// =================================================
mutations: {
SET_FEED: (state, { feed, ids, page }) => {
Vue.set(state.feeds[feed], page, ids)
},
SET_ITEM: (state, { item }) => {
if (item) {
Vue.set(state.items, item.id, item)
}
},
SET_ITEMS: (state, { items }) => {
items.forEach(item => {
if (item) {
Vue.set(state.items, item.id, item)
}
})
}, },
actions, SET_USER: (state, { id, user }) => {
mutations, Vue.set(state.users, id, user || false) /* false means user not found */
getters }
}) }
} }
import Vue from "vue"
export default {
SET_ACTIVE_TYPE: (state, { type }) => {
state.activeType = type
},
SET_LIST: (state, { type, ids }) => {
state.lists[type] = ids
},
SET_ITEMS: (state, { items }) => {
items.forEach(item => {
if (item) {
Vue.set(state.items, item.id, item)
}
})
},
SET_USER: (state, { id, user }) => {
Vue.set(state.users, id, user || false) /* false means user not found */
}
}
...@@ -192,6 +192,16 @@ ...@@ -192,6 +192,16 @@
version "0.2.6" version "0.2.6"
resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.6.tgz#a4b81ca8cdeb1acbc7923289a4a514f61b59db86" resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.6.tgz#a4b81ca8cdeb1acbc7923289a4a514f61b59db86"
"@nuxtjs/axios@^5.1.1":
version "5.1.1"
resolved "https://registry.yarnpkg.com/@nuxtjs/axios/-/axios-5.1.1.tgz#2aec0bfc6cee8dc682b05402af25c0c6bc72a70c"
dependencies:
"@nuxtjs/proxy" "^1.1.4"
axios "^0.18.0"
axios-retry "^3.0.2"
chalk "^2.3.2"
debug "^3.1.0"
"@nuxtjs/component-cache@^1.1.1": "@nuxtjs/component-cache@^1.1.1":
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/@nuxtjs/component-cache/-/component-cache-1.1.1.tgz#d94aa450e1b0df0451a2e938262234b534df9dfd" resolved "https://registry.yarnpkg.com/@nuxtjs/component-cache/-/component-cache-1.1.1.tgz#d94aa450e1b0df0451a2e938262234b534df9dfd"
...@@ -225,6 +235,12 @@ ...@@ -225,6 +235,12 @@
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/@nuxtjs/meta/-/meta-2.2.1.tgz#ad7c8658ef0357cdef56856a9ff21d595102e089" resolved "https://registry.yarnpkg.com/@nuxtjs/meta/-/meta-2.2.1.tgz#ad7c8658ef0357cdef56856a9ff21d595102e089"
"@nuxtjs/proxy@^1.1.4":
version "1.1.4"
resolved "https://registry.yarnpkg.com/@nuxtjs/proxy/-/proxy-1.1.4.tgz#71056d402d57c825a098c1651138a878ee6c0d23"
dependencies:
http-proxy-middleware "^0.17.4"
"@nuxtjs/pwa@2.0.8": "@nuxtjs/pwa@2.0.8":
version "2.0.8" version "2.0.8"
resolved "https://registry.yarnpkg.com/@nuxtjs/pwa/-/pwa-2.0.8.tgz#0e36ea1e3a63c1531f4e9e6b1d03a9390b09f8b5" resolved "https://registry.yarnpkg.com/@nuxtjs/pwa/-/pwa-2.0.8.tgz#0e36ea1e3a63c1531f4e9e6b1d03a9390b09f8b5"
...@@ -386,11 +402,17 @@ argparse@^1.0.7: ...@@ -386,11 +402,17 @@ argparse@^1.0.7:
dependencies: dependencies:
sprintf-js "~1.0.2" sprintf-js "~1.0.2"
arr-diff@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
dependencies:
arr-flatten "^1.0.1"
arr-diff@^4.0.0: arr-diff@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
arr-flatten@^1.1.0: arr-flatten@^1.0.1, arr-flatten@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
...@@ -428,6 +450,10 @@ array-uniq@^1.0.1: ...@@ -428,6 +450,10 @@ array-uniq@^1.0.1:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
array-unique@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
array-unique@^0.3.2: array-unique@^0.3.2:
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
...@@ -540,6 +566,19 @@ aws4@^1.2.1, aws4@^1.6.0: ...@@ -540,6 +566,19 @@ aws4@^1.2.1, aws4@^1.6.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
axios-retry@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.0.2.tgz#d644db74184858e4a5cf10f5ea88dd657bb4ba61"
dependencies:
is-retry-allowed "^1.1.0"
axios@^0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102"
dependencies:
follow-redirects "^1.3.0"
is-buffer "^1.1.5"
babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
version "6.26.0" version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
...@@ -1213,6 +1252,14 @@ brace-expansion@^1.1.7: ...@@ -1213,6 +1252,14 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0" balanced-match "^1.0.0"
concat-map "0.0.1" concat-map "0.0.1"
braces@^1.8.2:
version "1.8.5"
resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
dependencies:
expand-range "^1.8.1"
preserve "^0.2.0"
repeat-element "^1.1.2"
braces@^2.3.0, braces@^2.3.1: braces@^2.3.0, braces@^2.3.1:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb"
...@@ -2425,6 +2472,10 @@ etag@^1.8.1, etag@~1.8.1: ...@@ -2425,6 +2472,10 @@ etag@^1.8.1, etag@~1.8.1:
version "1.8.1" version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
eventemitter3@1.x.x:
version "1.2.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
events@^1.0.0: events@^1.0.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
...@@ -2440,6 +2491,12 @@ exif-parser@^0.1.9: ...@@ -2440,6 +2491,12 @@ exif-parser@^0.1.9:
version "0.1.12" version "0.1.12"
resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.12.tgz#58a9d2d72c02c1f6f02a0ef4a9166272b7760922" resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.12.tgz#58a9d2d72c02c1f6f02a0ef4a9166272b7760922"
expand-brackets@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
dependencies:
is-posix-bracket "^0.1.0"
expand-brackets@^2.1.4: expand-brackets@^2.1.4:
version "2.1.4" version "2.1.4"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
...@@ -2452,6 +2509,12 @@ expand-brackets@^2.1.4: ...@@ -2452,6 +2509,12 @@ expand-brackets@^2.1.4:
snapdragon "^0.8.1" snapdragon "^0.8.1"
to-regex "^3.0.1" to-regex "^3.0.1"
expand-range@^1.8.1:
version "1.8.2"
resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"
dependencies:
fill-range "^2.1.0"
express@^4.16.2: express@^4.16.2:
version "4.16.3" version "4.16.3"
resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53" resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53"
...@@ -2512,6 +2575,12 @@ external-editor@^2.0.1, external-editor@^2.0.4: ...@@ -2512,6 +2575,12 @@ external-editor@^2.0.1, external-editor@^2.0.4:
iconv-lite "^0.4.17" iconv-lite "^0.4.17"
tmp "^0.0.33" tmp "^0.0.33"
extglob@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
dependencies:
is-extglob "^1.0.0"
extglob@^2.0.4: extglob@^2.0.4:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
...@@ -2583,10 +2652,24 @@ file-type@^3.1.0: ...@@ -2583,10 +2652,24 @@ file-type@^3.1.0:
version "3.9.0" version "3.9.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
filename-regex@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
filesize@^3.5.11: filesize@^3.5.11:
version "3.6.1" version "3.6.1"
resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317"
fill-range@^2.1.0:
version "2.2.3"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723"
dependencies:
is-number "^2.1.0"
isobject "^2.0.0"
randomatic "^1.1.3"
repeat-element "^1.1.2"
repeat-string "^1.5.2"
fill-range@^4.0.0: fill-range@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
...@@ -2684,16 +2767,28 @@ flush-write-stream@^1.0.0: ...@@ -2684,16 +2767,28 @@ flush-write-stream@^1.0.0:
inherits "^2.0.1" inherits "^2.0.1"
readable-stream "^2.0.4" readable-stream "^2.0.4"
follow-redirects@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.4.1.tgz#d8120f4518190f55aac65bb6fc7b85fcd666d6aa"
dependencies:
debug "^3.1.0"
for-each@^0.3.2: for-each@^0.3.2:
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4"
dependencies: dependencies:
is-function "~1.0.0" is-function "~1.0.0"
for-in@^1.0.2: for-in@^1.0.1, for-in@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
for-own@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
dependencies:
for-in "^1.0.1"
foreach@^2.0.5: foreach@^2.0.5:
version "2.0.5" version "2.0.5"
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
...@@ -2827,6 +2922,19 @@ getpass@^0.1.1: ...@@ -2827,6 +2922,19 @@ getpass@^0.1.1:
dependencies: dependencies:
assert-plus "^1.0.0" assert-plus "^1.0.0"
glob-base@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
dependencies:
glob-parent "^2.0.0"
is-glob "^2.0.0"
glob-parent@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
dependencies:
is-glob "^2.0.0"
glob-parent@^3.1.0: glob-parent@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
...@@ -3103,6 +3211,22 @@ http-parser-js@>=0.4.0: ...@@ -3103,6 +3211,22 @@ http-parser-js@>=0.4.0:
version "0.4.11" version "0.4.11"
resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.11.tgz#5b720849c650903c27e521633d94696ee95f3529" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.11.tgz#5b720849c650903c27e521633d94696ee95f3529"
http-proxy-middleware@^0.17.4:
version "0.17.4"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz#642e8848851d66f09d4f124912846dbaeb41b833"
dependencies:
http-proxy "^1.16.2"
is-glob "^3.1.0"
lodash "^4.17.2"
micromatch "^2.3.11"
http-proxy@^1.16.2:
version "1.16.2"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742"
dependencies:
eventemitter3 "1.x.x"
requires-port "1.x.x"
http-signature@~1.1.0: http-signature@~1.1.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
...@@ -3315,6 +3439,16 @@ is-directory@^0.3.1: ...@@ -3315,6 +3439,16 @@ is-directory@^0.3.1:
version "0.3.1" version "0.3.1"
resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
is-dotfile@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
is-equal-shallow@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534"
dependencies:
is-primitive "^2.0.0"
is-extendable@^0.1.0, is-extendable@^0.1.1: is-extendable@^0.1.0, is-extendable@^0.1.1:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
...@@ -3325,6 +3459,10 @@ is-extendable@^1.0.1: ...@@ -3325,6 +3459,10 @@ is-extendable@^1.0.1:
dependencies: dependencies:
is-plain-object "^2.0.4" is-plain-object "^2.0.4"
is-extglob@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
is-extglob@^2.1.0, is-extglob@^2.1.1: is-extglob@^2.1.0, is-extglob@^2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
...@@ -3349,6 +3487,12 @@ is-function@^1.0.1, is-function@~1.0.0: ...@@ -3349,6 +3487,12 @@ is-function@^1.0.1, is-function@~1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5"
is-glob@^2.0.0, is-glob@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
dependencies:
is-extglob "^1.0.0"
is-glob@^3.1.0: is-glob@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
...@@ -3361,6 +3505,12 @@ is-glob@^4.0.0: ...@@ -3361,6 +3505,12 @@ is-glob@^4.0.0:
dependencies: dependencies:
is-extglob "^2.1.1" is-extglob "^2.1.1"
is-number@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
dependencies:
kind-of "^3.0.2"
is-number@^3.0.0: is-number@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
...@@ -3411,6 +3561,14 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: ...@@ -3411,6 +3561,14 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
dependencies: dependencies:
isobject "^3.0.1" isobject "^3.0.1"
is-posix-bracket@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
is-primitive@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
is-promise@^2.1.0: is-promise@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
...@@ -3425,6 +3583,10 @@ is-resolvable@^1.0.0: ...@@ -3425,6 +3583,10 @@ is-resolvable@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
is-retry-allowed@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
is-stream@^1.0.1: is-stream@^1.0.1:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
...@@ -3732,7 +3894,7 @@ lodash.uniq@^4.5.0: ...@@ -3732,7 +3894,7 @@ lodash.uniq@^4.5.0:
version "4.5.0" version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0: lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0:
version "4.17.5" version "4.17.5"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
...@@ -3832,6 +3994,24 @@ methods@~1.1.2: ...@@ -3832,6 +3994,24 @@ methods@~1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
micromatch@^2.3.11:
version "2.3.11"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
dependencies:
arr-diff "^2.0.0"
array-unique "^0.2.1"
braces "^1.8.2"
expand-brackets "^0.1.4"
extglob "^0.3.1"
filename-regex "^2.0.0"
is-extglob "^1.0.0"
is-glob "^2.0.1"
kind-of "^3.0.2"
normalize-path "^2.0.1"
object.omit "^2.0.0"
parse-glob "^3.0.4"
regex-cache "^0.4.2"
micromatch@^3.1.4, micromatch@^3.1.8: micromatch@^3.1.4, micromatch@^3.1.8:
version "3.1.10" version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
...@@ -4083,7 +4263,7 @@ nopt@^4.0.1: ...@@ -4083,7 +4263,7 @@ nopt@^4.0.1:
abbrev "1" abbrev "1"
osenv "^0.1.4" osenv "^0.1.4"
normalize-path@^2.1.1: normalize-path@^2.0.1, normalize-path@^2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
dependencies: dependencies:
...@@ -4232,6 +4412,13 @@ object.getownpropertydescriptors@^2.0.3: ...@@ -4232,6 +4412,13 @@ object.getownpropertydescriptors@^2.0.3:
define-properties "^1.1.2" define-properties "^1.1.2"
es-abstract "^1.5.1" es-abstract "^1.5.1"
object.omit@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
dependencies:
for-own "^0.1.4"
is-extendable "^0.1.1"
object.pick@^1.3.0: object.pick@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
...@@ -4396,6 +4583,15 @@ parse-bmfont-xml@^1.1.0: ...@@ -4396,6 +4583,15 @@ parse-bmfont-xml@^1.1.0:
xml-parse-from-string "^1.0.0" xml-parse-from-string "^1.0.0"
xml2js "^0.4.5" xml2js "^0.4.5"
parse-glob@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
dependencies:
glob-base "^0.3.0"
is-dotfile "^1.0.0"
is-extglob "^1.0.0"
is-glob "^2.0.0"
parse-headers@^2.0.0: parse-headers@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.1.tgz#6ae83a7aa25a9d9b700acc28698cd1f1ed7e9536" resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.1.tgz#6ae83a7aa25a9d9b700acc28698cd1f1ed7e9536"
...@@ -5071,6 +5267,10 @@ prepend-http@^1.0.0: ...@@ -5071,6 +5267,10 @@ prepend-http@^1.0.0:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
prettier@^1.11.1: prettier@^1.11.1:
version "1.11.1" version "1.11.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75"
...@@ -5201,6 +5401,13 @@ querystring@0.2.0, querystring@^0.2.0: ...@@ -5201,6 +5401,13 @@ querystring@0.2.0, querystring@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
randomatic@^1.1.3:
version "1.1.7"
resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c"
dependencies:
is-number "^3.0.0"
kind-of "^4.0.0"
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
version "2.0.6" version "2.0.6"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80"
...@@ -5317,6 +5524,12 @@ regenerator-transform@^0.10.0: ...@@ -5317,6 +5524,12 @@ regenerator-transform@^0.10.0:
babel-types "^6.19.0" babel-types "^6.19.0"
private "^0.1.6" private "^0.1.6"
regex-cache@^0.4.2:
version "0.4.4"
resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
dependencies:
is-equal-shallow "^0.1.3"
regex-not@^1.0.0, regex-not@^1.0.2: regex-not@^1.0.0, regex-not@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
...@@ -5376,7 +5589,7 @@ repeat-element@^1.1.2: ...@@ -5376,7 +5589,7 @@ repeat-element@^1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a"
repeat-string@^1.6.1: repeat-string@^1.5.2, repeat-string@^1.6.1:
version "1.6.1" version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
...@@ -5451,6 +5664,10 @@ require-uncached@^1.0.3: ...@@ -5451,6 +5664,10 @@ require-uncached@^1.0.3:
caller-path "^0.1.0" caller-path "^0.1.0"
resolve-from "^1.0.0" resolve-from "^1.0.0"
requires-port@1.x.x:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
resolve-from@^1.0.0: resolve-from@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment