Skip to content
This repository has been archived by the owner on Nov 28, 2020. It is now read-only.

Review dependencies and examples, make Vuex more explicit with TypeScript #16

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ yarn-error.log
*.iml
.nuxt
.vscode
!.vscode/settings.json
!.vscode/extensions.json
!.vscode/tasks.json

static/manifest*.json
static/sw.js
Expand Down
13 changes: 13 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"recommendations": [
"CoenraadS.bracket-pair-colorizer",
"editorconfig.editorconfig",
"gamunu.vscode-yarn",
"jasonnutter.search-node-modules",
"mrmlnc.vscode-attrs-sorter",
"octref.vetur",
"richie5um2.vscode-sort-json",
"sourcegraph.javascript-typescript",
"sysoev.language-stylus"
]
}
20 changes: 20 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
// https://code.visualstudio.com/docs/getstarted/settings
"editor.trimAutoWhitespace": true,
"editor.renderControlCharacters": true,
"editor.renderWhitespace": "all",
"files.trimTrailingWhitespace": true,
"typescript.tsdk": "node_modules/typescript/lib",
"vetur.validation.style": true,
"vetur.validation.template": true,
"[json]": {
"editor.formatOnSave": true
},
"[vue]": {
"editor.formatOnSave": true
},
"[typescript]": {
"editor.formatOnSave": true,
"editor.formatOnPaste": true
}
}
57 changes: 45 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Nuxt Hacker News TS

HackerNews clone built with Nuxt.js and TypeScript showcasing best practices of developing real life modern isomorphic Web Apps with [Nuxt](https://github.com/nuxt/nuxt.js). It features integrations with [TsLint](https://palantir.github.io/tslint/) (linting), [Prettier](https://prettier.io/) (code formatting), [Jest](https://jestjs.io/) (testing), [Axios](https://github.com/nuxt-community/axios-module) (http calls on steroids), [Storybook](https://storybook.js.org/)* (component playground).
HackerNews clone built with Nuxt.js and TypeScript showcasing best practices of developing real life modern isomorphic Web Apps with [Nuxt](https://github.com/nuxt/nuxt.js). It features integrations with [TsLint](https://palantir.github.io/tslint/) (linting), [Prettier](https://prettier.io/) (code formatting), [Jest](https://jestjs.io/) (testing), [Axios](https://github.com/nuxt-community/axios-module) (http calls on steroids), [Storybook](https://storybook.js.org/)\* (component playground).

<p align="center">
<a href="https://codesandbox.io/s/github/nuxt-community/hackernews-nuxt-ts" target="_blank">
Expand All @@ -23,48 +23,81 @@ Coming soon
- Prefetch/Preload JS + DNS + Data
- Critical Path CSS
- PWA experience using [PWA Module](https://github.com/nuxt-community/pwa-module) with almost _zero config_
- Enable optionnaly PWA, e.g. for development that's orthogonal to PWA features
- PRPL
- Hot reloading dev environment
- [TSLint](https://palantir.github.io/tslint/) and [Prettier](https://prettier.io/) integration
- Typescript 3
- TypeScript 3
- VSCode TypeScript bindings
- Storybook Integration (Coming Soon)
- Snapshot and Unit Tests with Jest and [Vue-Test-Utils](https://vue-test-utils.vuejs.org/) (Coming Soon)
- Snapshot and Unit Tests with Jest and [Vue-Test-Utils](https://vue-test-utils.vuejs.org/) (Coming Soon)
- VSCode setup for build into production, and run in dev with debugging helpers and logging

## Build Setup

**Requires Node.js 6+**

```bash
# install dependencies
npm install # or yarn
yarn

# serve in dev mode, with hot reload at localhost:3000
npm run dev
yarn dev

# build for production
npm run build
yarn build

# serve in production mode
npm start
yarn start

# run unit tests
npm run test
yarn test

# validate code with TSLint (with Prettier)
npm run lint
yarn lint

# validate and fix with TSLint (with Prettier)
npm run lintfix
yarn lintfix
```

### Production build

#### SPA

```bash
yarn
yarn build
```

## Links
For Nuxt JS version go [here](https://github.com/nuxt/hackernews)

For [Nuxt.js version, go to **nuxt/hackernews**](https://github.com/nuxt/hackernews)

This repository is originally ported from [vue-hackernews-2.0](https://github.com/vuejs/vue-hackernews-2.0) and [HackerNews Nuxt](https://github.com/nuxt/hackernews)

## Docs

- [vue-property-decorator](https://github.com/kaorun343/vue-property-decorator)
- [vuex-class](https://github.com/ktsn/vuex-class/)
- [vue-class-component](https://github.com/vuejs/vue-class-component)
- [vue-i18n](https://github.com/kazupon/vue-i18n) ([docs](https://kazupon.github.io/vue-i18n/))
- [Node.js HackerNews API **cheeaun/node-hnapi**](https://github.com/cheeaun/node-hnapi/) [example **/news?page=2**](https://api.hackerwebapp.com/news?page=2)

### Useful _TypeScript_ + _Nuxt.js_ community curated examples

- [nuxt-community/typescript-template](https://github.com/nuxt-community/typescript-template)
- [nuxt-community/pwa-module](https://github.com/nuxt-community/pwa-module) ([docs](https://pwa.nuxtjs.org/modules/workbox.html))
- [**nuxt/nuxt.js** in _examples/typescript_](https://github.com/nuxt/nuxt.js/tree/dev/examples/typescript)
- [**nuxt/nuxt.js** in _examples/typescript-vuex_](https://github.com/nuxt/nuxt.js/tree/dev/examples/typescript-vuex)

### Other useful _TypeScript_ + _Nuxt.js_ boilerplate/starter-kit projects

- [hisasann/typescript-nuxtjs-boilerplate](https://github.com/hisasann/typescript-nuxtjs-boilerplate)

### Other relevant links to the topic

- https://stackoverflow.com/q/52863117/852395

## License

MIT

7 changes: 6 additions & 1 deletion common/cache.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @ts-ignore
import express from "express"
import apicache from "apicache"

Expand All @@ -6,7 +7,11 @@ const app = express()
// https://github.com/kwhitley/apicache
app.use(apicache.middleware("15 minutes"))

// apicache.options({ debug: true })
const { NODE_ENV = "development" } = process.env

const debug = /^dev/i.test(NODE_ENV)

apicache.options({ debug })

export default {
path: "/api/",
Expand Down
50 changes: 42 additions & 8 deletions components/comment.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
<template>
<li v-if="comment" class="comment">
<div class="by">
<router-link :to="'/user/' + comment.user">{{ comment.user }}</router-link>
<nuxt-link :to="'/user/' + comment.user">{{ comment.user }}</nuxt-link>
{{ comment.time | timeAgo }} ago
</div>
<div class="text" v-html="comment.content"/>
<div v-if="comment.comments && comment.comments.length" :class="{ open }" class="toggle">
<div
v-if="comment.comments && comment.comments.length"
:class="{ open }"
class="toggle"
>
<a
@click="open = !open"
>{{ open ? '[-]' : '[+] ' + pluralize(comment.comments.length) + ' collapsed' }}</a>
>
<!--
We want minimal things going on here
So that when it's time to do i18n, it's just
going to be a matter of making sure we pass
strings through i18n’s $t() helpers.
-->
{{ pluralize(open, comment.comments.length) }}
</a>
</div>
<ul v-show="open" class="comment-children">
<comment
Expand All @@ -23,15 +35,28 @@
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"

@Component({})
@Component
export default class Comment extends Vue {
@Prop({ type: Object, required: true })
comment: any

open: boolean = true

pluralize(n) {
return n + (n === 1 ? " reply" : " replies")
pluralize (open, n) {
// Should we have vue-i18n;
// We could also leverage Intl.NumberFormat
// https://kazupon.github.io/vue-i18n/guide/number.html#custom-formatting
const number = n // this.$n(n)

// Should we have vue-i18n;
// We could use vue-i18n $tc helper
// Assuming 'replies' would be 'Replies collapsed | Reply collapsed | Replies collapsed'
// https://kazupon.github.io/vue-i18n/guide/pluralization.html
const textContent = n === 1 ? 'reply' : 'replies' // this.$tc('replies', n)

const append = open ? '' : 'collapsed' // this.$t('collapsed')

return `${number} ${textContent} ${append}`.trim()
}
}
</script>
Expand Down Expand Up @@ -78,15 +103,24 @@ export default class Comment extends Vue {
padding: 0.3em 0.5em;
border-radius: 4px;

a:before {
// +
// https://unicode-table.com/en/002B/
content: '\002B';
}

a {
color: #828282;
cursor: pointer;
}

&.open {
padding: 0;
background-color: transparent;
margin-bottom: -0.5em;
a:before {
// −
// https://unicode-table.com/en/2212/
content: '\2212';
}
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions components/item-list-nav.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<template>
<div class="news-list-nav">
<router-link v-if="page > 1" :to="`/${feed}/${page - 1}`">&lt; prev</router-link>
<nuxt-link v-if="page > 1" :to="`/${feed}/${page - 1}`">&lt; prev</nuxt-link>
<a v-else class="disabled">&lt; prev</a>
<span>{{ page }}/{{ maxPage }}</span>
<router-link v-if="hasMore" :to="`/${feed}/${page + 1}`">more &gt;</router-link>
<nuxt-link v-if="hasMore" :to="`/${feed}/${page + 1}`">more &gt;</nuxt-link>
<a v-else class="disabled">more &gt;</a>
</div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"

@Component({})
@Component
export default class ItemListNav extends Vue {
@Prop({ type: String, required: true })
feed!: string
Expand Down
18 changes: 10 additions & 8 deletions components/item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@
<span class="host">({{ item.url | host }})</span>
</template>
<template v-else>
<router-link :to="'/item/' + item.id">{{ item.title }}</router-link>
<nuxt-link :to="'/item/' + item.id">{{ item.title }}</nuxt-link>
</template>
</span>
<br>
<span class="meta">
<span v-if="item.type !== 'job'" class="by">by
<router-link :to="'/user/' + item.user">{{ item.user }}</router-link>
<span v-if="item.type !== 'job'" class="by">
by
<nuxt-link :to="'/user/' + item.user">{{ item.user }}</nuxt-link>
</span>
<span class="time">{{ item.time | timeAgo }} ago</span>
<span v-if="item.type !== 'job'" class="comments-link">|
<router-link :to="'/item/' + item.id">{{ item.comments_count }} comments</router-link>
<span v-if="item.type !== 'job'" class="comments-link">
|
<nuxt-link :to="'/item/' + item.id">{{ item.comments_count }} comments</nuxt-link>
</span>
</span>
<span v-if="item.type !== 'link'" class="label">{{ item.type }}</span>
Expand All @@ -26,13 +28,13 @@

<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"

import { Item } from "~/lib/models"
import { timeAgo } from "~/plugins/filters"

@Component({})
@Component
export default class NewsItem extends Vue {
@Prop({ type: Object, required: true })
item: any
item: Item

// http://ssr.vuejs.org/en/caching.html#component-level-caching
serverCacheKey({ item: { id, __lastUpdated, time } }) {
Expand Down
2 changes: 1 addition & 1 deletion components/lazy-wrapper.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

<script>
import Spinner from "./spinner.vue"
import Spinner from "~/components/spinner"

export default {
functional: true,
Expand Down
2 changes: 1 addition & 1 deletion components/spinner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"

@Component({})
@Component
export default class Spinner extends Vue {
@Prop({ type: Boolean, required: true })
show!: boolean
Expand Down
23 changes: 12 additions & 11 deletions layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
<div id="app">
<header class="header">
<nav class="inner" role="navigation">
<router-link to="/" exact>
<nuxt-link to="/" exact>
<img class="logo" src="~assets/logo.png" alt="logo">
</router-link>
<router-link v-for="(list, key) in feeds" :key="key" :to="`/${key}`">{{ list.title }}</router-link>
</nuxt-link>
<nuxt-link v-for="(list, key) in feeds" :key="key" :to="`/${key}`">{{ list.title }}</nuxt-link>
<a
class="github"
href="https://github.com/nuxt/hackernews"
href="https://github.com/nuxt-community/hackernews-nuxt-ts"
target="_blank"
rel="noopener banner"
>Built with Nuxt.js</a>
>Built with Nuxt.js using TypeScript</a>
</nav>
</header>
<nuxt nuxt-child-key="none" role="main"/>
Expand All @@ -22,9 +22,11 @@
import { Component, Vue } from "vue-property-decorator"

import { feeds } from "~/common/api"
import { NuxtPageHeadLink } from "~/lib/models"

@Component({
head() {
@Component
export default class Layout extends Vue {
head(): { link: NuxtPageHeadLink[] } {
// hack `(process as any)` to wait till this is fixed in Nuxt
const host = process.server
? this.$ssrContext.req.headers.host
Expand All @@ -37,8 +39,7 @@ import { feeds } from "~/common/api"
]
}
}
})
export default class Layout extends Vue {

get feeds() {
return feeds
}
Expand Down Expand Up @@ -87,8 +88,8 @@ a {
color: #fff;
}

&.router-link-active, &.nuxt-link-active {
color: #fff;
&.nuxt-link-active, &.nuxt-link-active {
text-decoration: underline solid #fff;
font-weight: 400;
}

Expand Down
3 changes: 3 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import * as runtime from "./runtime"

export { runtime }
Loading