Skip to content

Commit

Permalink
✨ add ssg pwa i18n
Browse files Browse the repository at this point in the history
  • Loading branch information
ileostar committed Jan 18, 2024
1 parent 081c2cf commit d1ed97d
Show file tree
Hide file tree
Showing 28 changed files with 384 additions and 63 deletions.
2 changes: 2 additions & 0 deletions apps/web/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
18 changes: 18 additions & 0 deletions apps/web/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM node:20-alpine as build-stage

WORKDIR /app
RUN corepack enable

COPY .npmrc package.json pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \
pnpm install --frozen-lockfile

COPY . .
RUN pnpm build

FROM nginx:stable-alpine as production-stage

COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
14 changes: 14 additions & 0 deletions apps/web/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defineConfig } from 'cypress'
import vitePreprocessor from 'cypress-vite'

export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3333',
chromeWebSecurity: false,
specPattern: 'cypress/e2e/**/*.spec.*',
supportFile: false,
setupNodeEvents(on) {
on('file:preprocessor', vitePreprocessor())
},
},
})
36 changes: 36 additions & 0 deletions apps/web/cypress/e2e/basic.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
context('Basic', () => {
beforeEach(() => {
cy.visit('/')
})

it('basic nav', () => {
cy.url()
.should('eq', 'http://localhost:3333/')

cy.contains('[Home Layout]')
.should('exist')

cy.get('#input')
.type('Vitesse{Enter}')
.url()
.should('eq', 'http://localhost:3333/hi/Vitesse')

cy.contains('[Default Layout]')
.should('exist')

cy.get('[btn]')
.click()
.url()
.should('eq', 'http://localhost:3333/')
})

it('markdown', () => {
cy.get('[data-test-id="about"]')
.click()
.url()
.should('eq', 'http://localhost:3333/about')

cy.get('.shiki')
.should('exist')
})
})
12 changes: 12 additions & 0 deletions apps/web/cypress/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"types": [
"cypress"
]
},
"include": [
"**/*.ts"
],
"exclude": []
}
23 changes: 15 additions & 8 deletions apps/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,27 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>vue3-vite-alova-ts-vitest</title>
</head>
<body>
<div id="app"></div>
<link rel="apple-touch-icon" href="/pwa-192x192.png" />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#00aba9" />
<meta name="msapplication-TileColor" content="#00aba9" />
<script>
(function() {
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
const setting = localStorage.getItem('color-schema') || 'auto'
;(function () {
const prefersDark =
window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches
const setting = localStorage.getItem('vueuse-color-scheme') || 'auto'
if (setting === 'dark' || (prefersDark && setting !== 'light'))
document.documentElement.classList.toggle('dark', true)
})()
</script>
</head>
<body class="font-sans">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<noscript>
This website requires JavaScript to function properly. Please enable
JavaScript to continue.
</noscript>
</body>
</html>
32 changes: 19 additions & 13 deletions apps/web/package.json
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
{
"name": "web",
"type": "module",
"version": "0.0.1",
"private": true,
"keywords": [
"template-vue",
"vite",
"alova"
],
"packageManager": "[email protected]",
"scripts": {
"dev": "vite --mode dev",
"pro": "vite --mode pro",
"build": "vue-tsc --noEmit && vite build --mode pro",
"build:dev": "vue-tsc --noEmit && vite build --mode dev",
"build:pro": "vue-tsc --noEmit && vite build --mode pro",
"build": "vite-ssg build",
"preview": "vite preview",
"preview-https": "serve dist",
"test": "vitest --mode dev",
"test:e2e": "cypress open",
"test:unit": "vitest",
"typecheck": "vue-tsc --noEmit",
"commit": "git add . && git-cz",
"push": "git push",
"lint": "eslint .",
"fix": "eslint src/**/*.* --fix",
"style": "stylelint \"src/**/*.(vue|scss|css)\" --fix",
"up": "taze major -I"
"up": "taze major -I",
"sizecheck": "npx vite-bundle-visualizer"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-emoji"
}
},
"dependencies": {
"@vueuse/core": "^10.7.2",
"@unhead/vue": "^1.8.9",
"@unocss/reset": "^0.58.1",
"@vueuse/core": "^10.7.0",
"@vueuse/head": "^2.0.0",
"alova": "^2.16.2",
"css-doodle": "^0.38.3",
"element-plus": "^2.5.1",
Expand All @@ -42,7 +42,6 @@
},
"devDependencies": {
"@alova/adapter-axios": "^1.2.2",
"@depazer/vite": "^1.0.0",
"@iconify-json/carbon": "^1.1.27",
"@iconify-json/mdi": "^1.1.64",
"@iconify-json/streamline-emojis": "^1.1.8",
Expand All @@ -58,6 +57,8 @@
"@vue-macros/volar": "^0.18.10",
"@vue/test-utils": "^2.4.3",
"commitizen": "^4.3.0",
"cypress": "^13.6.2",
"cypress-vite": "^1.5.0",
"cz-emoji": "1.3.2-canary.2",
"daisyui": "^3.9.4",
"dotenv": "^16.3.1",
Expand All @@ -75,7 +76,6 @@
"stylelint": "^16.1.0",
"stylelint-config-standard-vue": "^1.0.0",
"stylelint-order": "^6.0.4",
"taze": "^0.13.1",
"typescript": "^5.3.3",
"unocss": "^0.58.3",
"unplugin-auto-import": "^0.17.3",
Expand All @@ -84,9 +84,15 @@
"unplugin-vue-macros": "^2.7.9",
"unplugin-vue-router": "^0.7.0",
"vite": "^5.0.11",
"vite-bundle-visualizer": "^1.0.0",
"vite-plugin-inspect": "^0.8.1",
"vite-plugin-pwa": "^0.17.4",
"vite-plugin-restart": "^0.4.0",
"vite-plugin-vue-devtools": "1.0.0-rc.5",
"vite-plugin-vue-layouts": "^0.11.0",
"vite-plugin-webfont-dl": "^3.9.1",
"vite-ssg": "^0.23.6",
"vite-ssg-sitemap": "^0.6.1",
"vitest": "^1.2.0",
"vue-tsc": "^1.8.27",
"vue3-lottie": "^3.2.0"
Expand Down
3 changes: 3 additions & 0 deletions apps/web/public/_headers
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/assets/*
cache-control: max-age=31536000
cache-control: immutable
3 changes: 3 additions & 0 deletions apps/web/public/favicon-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/web/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed apps/web/public/image.png
Binary file not shown.
Binary file added apps/web/public/pwa-192x192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/web/public/pwa-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions apps/web/public/safari-pinned-tab.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion apps/web/public/vite.svg

This file was deleted.

36 changes: 19 additions & 17 deletions apps/web/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import { createRouter, createWebHistory } from 'vue-router/auto'
import { ViteSSG } from 'vite-ssg'
import { setupLayouts } from 'virtual:generated-layouts'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import Vue3Lottie from 'vue3-lottie'
import OnuUI from 'onu-ui'

// import Previewer from 'virtual:vue-component-preview'
import { routes } from 'vue-router/auto/routes'
import App from './App.vue'
import type { UserModule } from '../types/types'

import store from './store'
import '@unocss/reset/tailwind.css'
import './styles/main.css'
import 'onu-ui/dist/style.css'
import './styles/main.css'
import 'uno.css'
const app = createApp(App)

const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
extendRoutes: (routes) => setupLayouts(routes),
})
store.use(piniaPluginPersistedstate)
app.use(Vue3Lottie, { name: 'LottieAnimation' })
app.use(router)
app.use(store)
app.use(OnuUI)
app.mount('#app')
export const createApp = ViteSSG(
App,
{
routes: setupLayouts(routes),
base: import.meta.env.BASE_URL,
},
(ctx) => {
// install all modules under `modules/`
Object.values(import.meta.glob<{ install: UserModule }>('./modules/*.ts', { eager: true }))
.forEach(i => i.install?.(ctx))
// ctx.app.use(Previewer)
},
)
11 changes: 11 additions & 0 deletions apps/web/src/modules/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## Modules

A custom user module system. Place a `.ts` file with the following template, it will be installed automatically.

```ts
import type { UserModule } from '~/types'

export const install: UserModule = ({ app, router, isClient }) => {
// do something
}
```
50 changes: 50 additions & 0 deletions apps/web/src/modules/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { Locale } from 'vue-i18n'
import { createI18n } from 'vue-i18n'
import type { UserModule } from '../../types/types'

// Import i18n resources
// https://vitejs.dev/guide/features.html#glob-import
//
// Don't need this? Try vitesse-lite: https://github.com/antfu/vitesse-lite
const i18n = createI18n({
legacy: false,
locale: '',
messages: {},
})

const localesMap = Object.fromEntries(
Object.entries(import.meta.glob('../../locales/*.yml'))
.map(([path, loadLocale]) => [path.match(/([\w-]*)\.yml$/)?.[1], loadLocale]),
) as Record<Locale, () => Promise<{ default: Record<string, string> }>>

export const availableLocales = Object.keys(localesMap)

const loadedLanguages: string[] = []

function setI18nLanguage(lang: Locale) {
i18n.global.locale.value = lang as any
if (typeof document !== 'undefined')
document.querySelector('html')?.setAttribute('lang', lang)
return lang
}

export async function loadLanguageAsync(lang: string): Promise<Locale> {
// If the same language
if (i18n.global.locale.value === lang)
return setI18nLanguage(lang)

// If the language was already loaded
if (loadedLanguages.includes(lang))
return setI18nLanguage(lang)

// If the language hasn't been loaded yet
const messages = await localesMap[lang]()
i18n.global.setLocaleMessage(lang, messages.default)
loadedLanguages.push(lang)
return setI18nLanguage(lang)
}

export const install: UserModule = ({ app }) => {
app.use(i18n)
loadLanguageAsync('en')
}
9 changes: 9 additions & 0 deletions apps/web/src/modules/lottie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Vue3Lottie from 'vue3-lottie'
import type { UserModule } from '../../types/types'

// Setup Lottie
export const install: UserModule = ({ isClient, app }) => {
if (!isClient)
return
app.use(Vue3Lottie, { name: 'LottieAnimation' })
}
10 changes: 10 additions & 0 deletions apps/web/src/modules/onuui.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import OnuUI from 'onu-ui'

import type { UserModule } from '../../types/types'

// Setup Lottie
export const install: UserModule = ({ isClient, app }) => {
if (!isClient)
return
app.use(OnuUI)
}
Loading

0 comments on commit d1ed97d

Please sign in to comment.