9 Commits

Author SHA1 Message Date
9ef6da3efb [Feature]
All checks were successful
Node.js CI / build-and-test (push) Successful in 43s
增加文章信息和状态显示

[Base]
为 BetterHr 组件添加 class 透传 (未测试)
精简 server.js
2025-05-17 22:48:20 +08:00
2aea1eae7f [Feature]
All checks were successful
Node.js CI / build-and-test (push) Successful in 24s
为 SSR 的导航菜单添加链接
2025-05-16 09:31:13 +08:00
063a1330d7 [Feature]
All checks were successful
Node.js CI / build-and-test (push) Successful in 31s
为 Markdown 启用自定义 Hr 组件
2025-05-16 09:01:11 +08:00
0607e6241b [BugFix]
All checks were successful
Node.js CI / build-and-test (push) Successful in 25s
修复链接转换器不显示格式错误的 BUG
2025-05-14 18:00:56 +08:00
89538823fc [BUGFix]
All checks were successful
Node.js CI / build-and-test (push) Successful in 35s
修复书签删除问题

[Feature]
使用表单保证 HTML 语义化
更新关于页面
2025-05-14 17:49:44 +08:00
a59ebeb487 优化打包配置, 移除不必要的 chunck
All checks were successful
Node.js CI / build-and-test (push) Successful in 41s
更新依赖
移除不必要的注释
优化 "关于" 页面
启用 SASS/SCSS
2025-05-13 17:28:17 +08:00
32d9c8a4aa 删除多余代码 2025-05-08 08:46:27 +08:00
b9c163fd06 优化 404 页面请求逻辑
All checks were successful
Node.js CI / build-and-test (push) Successful in 37s
2025-05-02 22:54:47 +08:00
2f6b3524c4 优化产物上传
All checks were successful
Node.js CI / build-and-test (push) Successful in 30s
2025-05-02 16:52:31 +08:00
18 changed files with 849 additions and 3996 deletions

View File

@ -55,5 +55,8 @@ jobs:
- name: 上传产物到远程服务器 - name: 上传产物到远程服务器
run: | run: |
scp output.zip default@10.0.0.3:/srv/publish/ao3-mirror-ssr/${{ steps.extract_tag.outputs.tag }}.zip TAG=${{ steps.extract_tag.outputs.tag }}
scp output.zip default@10.0.0.3:/srv/publish/ao3-mirror-ssr/${TAG}.zip
ssh default@10.0.0.3 sh -c 'cd /srv/publish/ao3-mirror-ssr/; rm -f latest.zip; ln -s ${TAG}.zip latest.zip'

4445
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,27 +12,23 @@
}, },
"dependencies": { "dependencies": {
"@mdui/icons": "^1.0.2", "@mdui/icons": "^1.0.2",
"axios": "^1.8.1", "axios": "^1.9.0",
"compress-json": "^3.1.1", "compress-json": "^3.1.1",
"compression": "^1.8.0",
"cookie-parser": "^1.4.7", "cookie-parser": "^1.4.7",
"express": "^5.0.1", "express": "^5.1.0",
"idb": "^8.0.2", "idb": "^8.0.3",
"mdui": "^2.1.3", "mdui": "^2.1.3",
"pako": "^2.1.0", "pinia": "^3.0.2",
"pinia": "^3.0.1",
"sirv": "^3.0.1",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-client-only": "^2.1.0", "vue-router": "^4.5.1"
"vue-router": "^4.5.0"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue": "^5.2.4",
"@vitejs/plugin-vue-jsx": "^4.1.1", "@vitejs/plugin-vue-jsx": "^4.1.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"vite": "^6.1.1", "sass": "^1.88.0",
"vite": "^6.3.5",
"vite-plugin-md": "^0.21.5", "vite-plugin-md": "^0.21.5",
"vite-plugin-pwa": "^0.21.1", "vite-plugin-vue-devtools": "^7.7.6"
"vite-plugin-vue-devtools": "^7.7.2"
} }
} }

View File

@ -16,6 +16,11 @@ const templateHtml = isProduction
const app = express() const app = express()
app.use(cookieParser()); app.use(cookieParser());
const MESSAGE = {
404: 'Not Found',
0: 'Unknown'
}
// Add Vite or respective production middlewares // Add Vite or respective production middlewares
/** @type {import('vite').ViteDevServer | undefined} */ /** @type {import('vite').ViteDevServer | undefined} */
let vite let vite
@ -28,9 +33,9 @@ if (!isProduction) {
}) })
app.use(vite.middlewares) app.use(vite.middlewares)
} else { } else {
const compression = (await import('compression')).default //const compression = (await import('compression')).default
const sirv = (await import('sirv')).default const sirv = (await import('sirv')).default
app.use(compression()) //app.use(compression())
app.use(base, sirv('./dist/client', { extensions: [] })) app.use(base, sirv('./dist/client', { extensions: [] }))
} }
@ -42,19 +47,30 @@ app.use('*all', async (req, res) => {
/** @type {string} */ /** @type {string} */
let template let template
/** @type {import('./src/entry-server.js').render} */ /** @type {import('./src/entry-server.js').render} */
let render let render, getRoute
if (!isProduction) { if (!isProduction) {
// Always read fresh template in development // Always read fresh template in development
template = await fs.readFile('./index.html', 'utf-8') template = await fs.readFile('./index.html', 'utf-8')
template = await vite.transformIndexHtml(url, template) template = await vite.transformIndexHtml(url, template)
render = (await vite.ssrLoadModule('/src/entry-server.js')).render const module = await vite.ssrLoadModule('/src/entry-server.js')
render = module.render
getRoute = module.getRoute
} else { } else {
template = templateHtml template = templateHtml
render = (await import('./dist/server/entry-server.js')).render const module = await import('./dist/server/entry-server.js')
render = module.render
getRoute = module.getRoute
} }
const { stream, piniaState } = await render(url, req.cookies, req.headers.host) const { router, code } = await getRoute(url)
if (code != 200 && !req.accepts('html')) {
res.status(code).set({ 'Content-Type': 'text/plain' })
res.write(MESSAGE[code] || MESSAGE[0])
res.end()
return
}
const { stream, piniaState } = await render(router, req.cookies, req.headers.host)
const [htmlStart, htmlEnd] = template.split('<!--app-html-->') const [htmlStart, htmlEnd] = template.split('<!--app-html-->')
res.status(200).set({ 'Content-Type': 'text/html' }) res.status(code).set({ 'Content-Type': 'text/html' })
res.write(htmlStart) res.write(htmlStart)
for await (const chunk of stream) { for await (const chunk of stream) {
if (res.closed) break if (res.closed) break

View File

@ -98,7 +98,7 @@ onMounted(async () => {
<li v-for="item in routeStore.allRoutes" <li v-for="item in routeStore.allRoutes"
:key="item.path" :key="item.path"
:class="{ 'active-item' : item.path == $route.path }" :class="{ 'active-item' : item.path == $route.path }"
>{{ item.name }}</li> ><a :href="item.path">{{ item.name }}</a></li>
</ul> </ul>
</template></ClientOnly> </template></ClientOnly>
</nav> </nav>

View File

@ -1,8 +1,13 @@
import { decompress } from 'compress-json' import { decompress } from 'compress-json'
import './main.css' import './main.scss'
import { createApp } from './main'
const { app, pinia, router } = createApp() import { createApp } from './main'
import { createSSRRouter } from './router.js'
const { app, pinia } = createApp()
const router = createSSRRouter()
app.use(router)
if (window.__PINIA_STATE__) { if (window.__PINIA_STATE__) {
pinia.state.value = decompress(window.__PINIA_STATE__) pinia.state.value = decompress(window.__PINIA_STATE__)

View File

@ -1,19 +1,28 @@
import { renderToWebStream, renderToString } from 'vue/server-renderer' import { renderToWebStream, renderToString } from 'vue/server-renderer'
import { createApp } from './main' import { createApp } from './main'
export async function render(_url, cookies, host) { import { createSSRRouter } from './router.js'
const { app, pinia, router } = createApp()
export async function getRoute(_url) {
const router = createSSRRouter()
await router.push(_url) await router.push(_url)
await router.isReady() await router.isReady()
const route = router.currentRoute.value.matched[0]
const code = route.meta.code || 200
return { router, code }
}
export async function render(router, cookies, host) {
const { app, pinia } = createApp()
app.use(router)
const ctx = { const ctx = {
cookies, cookies,
host, host,
initialState: {} initialState: {}
} }
// await new Promise((resolve) => setTimeout(resolve, 0))
const stream = renderToWebStream(app, ctx) const stream = renderToWebStream(app, ctx)
const initialState = ctx.initialStat
const piniaState = pinia.state.value const piniaState = pinia.state.value
return { stream, initialState, piniaState } return { stream, piniaState }
} }

View File

@ -1,35 +0,0 @@
@import 'mdui/mdui.css';
/* @import './assets/typescale.css'; */
body {
font-family: Roboto,Noto Sans SC,PingFang SC,Lantinghei SC,Microsoft Yahei,Hiragino Sans GB,"Microsoft Sans Serif",WenQuanYi Micro Hei,sans-serif;
background-color: rgb(var(--mdui-color-background));
transition: opacity var(--mdui-motion-duration-short2) var(--mdui-motion-easing-linear);
}
mdui-card {
width: 100%;
padding: 0px 16px 16px;
}
mdui-text-field {
margin: 8px 0px;
}
.warn {
background-color: rgb(var(--mdui-color-error));
color: rgb(var(--mdui-color-on-error));
}
.warn-text {
color: rgb(var(--mdui-color-error));
}
.pre-break {
white-space: pre-line;
}
.no-select {
user-select: none;
}

View File

@ -2,19 +2,18 @@ import { createSSRApp } from 'vue'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import App from './App.vue' import App from './App.vue'
import { createSSRRouter } from './router.js'
import ClientOnly from './ssr/ClientOnly.vue' import ClientOnly from './ssr/ClientOnly.vue'
import Hr from './ui/BetterHr.vue' import Hr from './ui/BetterHr.vue'
import Form from './ui/Form.vue'
export function createApp() { export function createApp() {
const app = createSSRApp(App) const app = createSSRApp(App)
const router = createSSRRouter()
const pinia = createPinia() const pinia = createPinia()
app.use(pinia) app.use(pinia)
app.use(router)
app app
.component('ClientOnly', ClientOnly) .component('ClientOnly', ClientOnly)
.component('Hr', Hr) .component('Hr', Hr)
return { app, pinia, router } .component('Form', Form)
return { app, pinia }
} }

50
src/main.scss Normal file
View File

@ -0,0 +1,50 @@
@import 'mdui/mdui.css';
// @import './assets/typescale.css';
// 字体配置
$font-family: Roboto, Noto Sans SC, PingFang SC, Lantinghei SC,
Microsoft Yahei, Hiragino Sans GB, "Microsoft Sans Serif",
WenQuanYi Micro Hei, sans-serif;
// MDUI 变量简写
$bg-color: rgb(var(--mdui-color-background));
$error-color: rgb(var(--mdui-color-error));
$on-error-color: rgb(var(--mdui-color-on-error));
$transition-duration: var(--mdui-motion-duration-short2);
$transition-easing: var(--mdui-motion-easing-linear);
body {
font-family: $font-family;
background-color: $bg-color;
transition: opacity $transition-duration $transition-easing;
}
// MDUI 组件样式
mdui-card {
width: 98%;
padding: 8px;
}
mdui-text-field {
margin: 8px 0;
}
// 警告样式
.warn {
background-color: $error-color;
color: $on-error-color;
}
.warn-text {
color: $error-color;
}
// 通用工具类
.pre-break {
white-space: pre-line;
}
.no-select {
user-select: none;
}

View File

@ -41,7 +41,8 @@ export function createSSRRouter() {
component: () => import('./views/fallback/NotFound.vue'), component: () => import('./views/fallback/NotFound.vue'),
meta: { meta: {
title: "页面未找到", title: "页面未找到",
hidden: true hidden: true,
code: 404
} }
} }
]}) ]})

View File

@ -12,14 +12,27 @@ export const useWorkReadState = defineStore('workRead', () => {
const pesud = ref(null) const pesud = ref(null)
const title = ref(null) const title = ref(null)
const text = ref(null) const text = ref(null)
const publishedTime = ref(null)
const state = ref('') const state = ref('')
const publishedTime = ref(null)
const wordCount = ref(0)
const kudoCount = ref(0)
const hitCount = ref(0)
const category = ref([])
const fandom = ref([])
const lang = ref(null)
function setData(data) { function setData(data) {
id.value = data.workId id.value = data.workId
title.value = data.title title.value = data.title
summary.value = [escapeAndFormatText(data.summary)] summary.value = [escapeAndFormatText(data.summary)]
pesud.value = data.pesud pesud.value = data.pesud
text.value = data.text.split('\n\n') text.value = data.text.split('\n\n')
publishedTime.value = data.stats.publishedTime
wordCount.value = data.stats.wordCount
kudoCount.value = data.stats.kudoCount
hitCount.value = data.stats.hitCount
category.value = data.category
fandom.value = data.fandom
lang.value = data.lang
} }
async function loadWork(target) { async function loadWork(target) {
if (target == id.value || state.value == 'loading') return if (target == id.value || state.value == 'loading') return
@ -39,8 +52,14 @@ export const useWorkReadState = defineStore('workRead', () => {
summary, summary,
pesud, pesud,
text, text,
publishedTime,
state, state,
publishedTime,
wordCount,
kudoCount,
hitCount,
category,
fandom,
lang,
setData, setData,
loadWork loadWork
} }

View File

@ -14,9 +14,9 @@
组件库与工具链 组件库与工具链
--- ---
- MDUI 2 - MDUI 2 [mdui.org](https://mdui.org)
- Vue - Vue 3 [vuejs.org](https://vuejs.org)
- Vite - Vite 6 [vitejs.dev](https://vite.dev)
其他 其他
--- ---

View File

@ -1,11 +1,19 @@
<script setup>import 'mdui/components/divider.js'</script> <script setup>
import 'mdui/components/divider.js'
defineProps(['class']) // 接收外部 class 属性
</script>
<template> <template>
<ClientOnly><mdui-divider class='hr-divider'></mdui-divider> <ClientOnly>
<template #ssr><hr/></template></ClientOnly> <!-- class 传入 mdui-divider -->
<mdui-divider :class="['hr-divider', $attrs.class]"></mdui-divider>
<template #ssr><hr :class="['hr-divider', $attrs.class]" /></template>
</ClientOnly>
</template> </template>
<style scoped> <style scoped>
.hr-divider { .hr-divider {
margin: 8px 0px; margin: 8px 0px;
} }
</style> </style>

20
src/ui/Form.vue Normal file
View File

@ -0,0 +1,20 @@
<template>
<form @submit="handleSubmit">
<slot></slot>
</form>
</template>
<script setup>
import { ref } from 'vue'
const emit = defineEmits(['submit'])
function handleSubmit(event) {
event.preventDefault()
const form = event.target
const formData = new FormData(form)
const data = Object.fromEntries(formData.entries())
emit('submit', data, event)
}
</script>

View File

@ -9,13 +9,13 @@ import Intro from '../texts/intro.md'
const router = useRouter() const router = useRouter()
const src = ref('')
const srcText = ref(null)
const err = ref(false) const err = ref(false)
const srcText = ref(null)
function convert(from) { function convert(from) {
if( Number(from) ) { if( Number(from) ) {
return { return {
type: 's',
id: Number(from) id: Number(from)
} }
} else if (from.includes('https://archiveofourown.org/works/')) { } else if (from.includes('https://archiveofourown.org/works/')) {
@ -35,9 +35,10 @@ function convert(from) {
} }
} }
function onConvert() { function onConvert(data) {
const { id, cid } = convert(src.value) const { type, id, cid } = convert(data.src)
if (id == null) { console.log(type, id, cid)
if (type == null) {
err.value = true err.value = true
srcText.value?.focus() srcText.value?.focus()
} else { } else {
@ -56,17 +57,17 @@ function onConvert() {
<section id="converter"> <section id="converter">
<h2>链接转换</h2> <h2>链接转换</h2>
<p>输入完整链接或者 ID</p> <p>输入完整链接或者 ID</p>
<ClientOnly> <Form @submit="onConvert"><ClientOnly>
<mdui-text-field variant="filled" label="链接" placeholder="https://archiveofourown.org/works/114514" @input="src = $event.target.value" ref='srcText'> <mdui-text-field variant="filled" label="链接" name="src" placeholder="https://archiveofourown.org/works/114514" ref='srcText'>
<span v-if='err' slot="helper" class='warn-text'>链接格式错误!</span> <span v-if='err' slot="helper" class='warn-text'>链接格式错误!</span>
</mdui-text-field><br/> </mdui-text-field><br/>
<div style="display: flex"> <div style="display: flex">
<div style="flex-grow: 1"></div> <div style="flex-grow: 1"></div>
<mdui-button @click='onConvert'>-></mdui-button> <mdui-button type="submit">-></mdui-button>
</div> </div>
{{ src }}
<template #ssr> <template #ssr>
Padding... <input type="text" id="src" name="src" />
</template></ClientOnly> <input type="submit" />
</template></ClientOnly></Form>
</section> </section>
</template> </template>

View File

@ -22,6 +22,9 @@ import 'mdui/components/button.js'
import 'mdui/components/dropdown.js' import 'mdui/components/dropdown.js'
import 'mdui/components/menu.js' import 'mdui/components/menu.js'
import 'mdui/components/menu-item.js' import 'mdui/components/menu-item.js'
import 'mdui/components/collapse.js'
import 'mdui/components/collapse-item.js'
import 'mdui/components/card.js'
import '@mdui/icons/bookmark.js' import '@mdui/icons/bookmark.js'
@ -47,6 +50,12 @@ let bookmarkStore = null
let paragraphs = [] let paragraphs = []
let currentParagraph = null let currentParagraph = null
const categoryName = {
mm: "男/男",
ff: "女/女",
fm: '女/男'
}
async function addBookmark() { async function addBookmark() {
if (currentParagraph) { if (currentParagraph) {
const id = await bookmarkStore.add(workReadState.id, readIndex, currentParagraph.textContent.slice(0,20), '') const id = await bookmarkStore.add(workReadState.id, readIndex, currentParagraph.textContent.slice(0,20), '')
@ -120,7 +129,7 @@ function openBookmarkMenu(bk, index) {
async function deleteBookmark() { async function deleteBookmark() {
if (bookmarkSelect.value) { if (bookmarkSelect.value) {
bookmarkStore.del(bookmarkSelect.value.bk.id) bookmarkStore.del(bookmarkSelect.value.bk.id)
bookmarks.value.splice(bookmarkSelect.value.index) bookmarks.value.splice(bookmarkSelect.value.index,1)
bookmarkSelect.value = null bookmarkSelect.value = null
} }
} }
@ -187,10 +196,42 @@ onBeforeUnmount(() => {
<article> <article>
<h1 style="margin: auto">{{ workReadState.title }}</h1> <h1 style="margin: auto">{{ workReadState.title }}</h1>
<h4>{{ workReadState.pesud }}</h4> <h4>{{ workReadState.pesud }}</h4>
<mdui-card style="margin: 8px; padding: 0px;"><mdui-collapse acc>
<mdui-collapse-item value="info"><mdui-list-item class="infoblockhead" slot="header">
作品信息
</mdui-list-item><div class="infoblock"><dl>
<dt>分类</dt><ul>
<li v-for="item in workReadState.category" :key="item">
{{ categoryName[item] }}</li>
</ul>
<dt>原著</dt><ul>
<li v-for="item in workReadState.fandom" :key="item">
{{ item }}</li>
</ul>
<dt>语言</dt><dd>
{{ workReadState.lang }}
</dd>
</dl></div></mdui-collapse-item>
<mdui-collapse-item value="stats"><mdui-list-item class="infoblockhead" slot="header">
作品状态
</mdui-list-item><div class="infoblock"><dl>
<dt>发布时间</dt><dd>
{{ workReadState.publishedTime.year }} -
{{ workReadState.publishedTime.month }} -
{{ workReadState.publishedTime.date }}
</dd>
<dt>字数</dt><dd>
{{ workReadState.wordCount }}
</dd>
<dt>点击</dt><dd>
{{ workReadState.hitCount }}
</dd>
</dl></div></mdui-collapse-item>
</mdui-collapse></mdui-card>
<blockquote> <blockquote>
<p v-for="para in workReadState.summary" :key="para" v-html='para'></p> <p v-for="para in workReadState.summary" :key="para" v-html='para'></p>
</blockquote> </blockquote>
<Hr/> <Hr />
<div ref='content'> <div ref='content'>
<p v-for="(para, index) in workReadState.text" :key="para" :data-index="index">{{ para }}</p> <p v-for="(para, index) in workReadState.text" :key="para" :data-index="index">{{ para }}</p>
</div> </div>
@ -237,6 +278,31 @@ onBeforeUnmount(() => {
<template v-if="workReadState.state == 'ready'"> <template v-if="workReadState.state == 'ready'">
<h1>{{ workReadState.title }}</h1> <h1>{{ workReadState.title }}</h1>
<h2>{{ workReadState.pesud }}</h2> <h2>{{ workReadState.pesud }}</h2>
<dl>
<dt>分类</dt><ul>
<li v-for="item in workReadState.category" :key="item">
{{ categoryName[item] }}</li>
</ul>
<dt>原著</dt><ul>
<li v-for="item in workReadState.fandom" :key="item">
{{ item }}</li>
</ul>
<dt>语言</dt><dd>
{{ workReadState.lang }}
</dd>
<dt>发布时间</dt><dd>
{{ workReadState.publishedTime.year }} -
{{ workReadState.publishedTime.month }} -
{{ workReadState.publishedTime.date }}
</dd>
<dt>字数</dt><dd>
{{ workReadState.wordCount }}
</dd>
<dt>点击</dt><dd>
{{ workReadState.hitCount }}
</dd>
</dl>
<Hr />
<blockquote> <blockquote>
<p v-for="para in workReadState.summary" :key="para" v-html='para'></p> <p v-for="para in workReadState.summary" :key="para" v-html='para'></p>
</blockquote> </blockquote>
@ -254,4 +320,12 @@ onBeforeUnmount(() => {
z-index: 1000; /* 确保悬浮按钮在其他内容上方 */ z-index: 1000; /* 确保悬浮按钮在其他内容上方 */
animation: slideInFromRight var(--mdui-motion-duration-medium2) var(--mdui-motion-easing-standard); /* 动画时长和缓动效果 */ animation: slideInFromRight var(--mdui-motion-duration-medium2) var(--mdui-motion-easing-standard); /* 动画时长和缓动效果 */
} }
.infoblock {
margin: 8px 16px;
}
.infoblockhead {
background-color: rgb(var(--mdui-color-primary-container));
}
</style> </style>

View File

@ -4,7 +4,6 @@ import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx' import vueJsx from '@vitejs/plugin-vue-jsx'
import vueDevTools from 'vite-plugin-vue-devtools' import vueDevTools from 'vite-plugin-vue-devtools'
import { VitePWA } from 'vite-plugin-pwa';
import markdown from 'vite-plugin-md' import markdown from 'vite-plugin-md'
// https://vite.dev/config/ // https://vite.dev/config/
@ -20,20 +19,11 @@ export default defineConfig({
}), }),
vueJsx(), vueJsx(),
vueDevTools(), vueDevTools(),
markdown() markdown({
/*VitePWA({ markdownItSetup(md) {
name: '墨宇留香 - 渐进式 Web App 版本', md.renderer.rules.hr = () => "<Hr />"
short_name: '墨宇留香', }
start_url: '/index.html', })
display: 'standalone',
"background_color": "#808080",
"theme_color": "#7F3C5C",
"icons": [
{
"src": "/favicon.png",
"sizes": "507x580",
"type": "image/png"
}]})*/
], ],
resolve: { resolve: {
alias: { alias: {
@ -43,47 +33,33 @@ export default defineConfig({
build: { build: {
rollupOptions: { rollupOptions: {
output: { output: {
/*manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor';
}
}*/
manualChunks(id) { manualChunks(id) {
if (id.includes('node_modules')) { if (id.includes('node_modules')) {
const modules = id.toString().split('node_modules/')[1]; const modules = id.toString().split('node_modules/')[1];
const moduleNames = modules.split('/'); const moduleNames = modules.split('/');
const moduleName = moduleNames[0] const moduleName = moduleNames[0]
return `vendor/${moduleName}` return `vendor/${moduleName}`
} }
if (id.includes('src/views')) { if (id.includes('src/views')) {
const modules = id.toString().split('src/views/')[1]; const modules = id.toString().split('src/views/')[1];
const moduleName = modules.split('.')[0]; const moduleName = modules.split('.')[0];
return `page/${moduleName}`; return `page/${moduleName}`;
} }
if (id.includes('src/components')) { if (id.includes('src/components')) {
const modules = id.toString().split('src/components/')[1]; const modules = id.toString().split('src/components/')[1];
const moduleName = modules.split('.')[0]; const moduleName = modules.split('.')[0];
return `component/${moduleName}`; return `component/${moduleName}`;
} }
if (id.includes('src/texts')) { if (id.includes('src/texts')) {
const modules = id.toString().split('src/texts/')[1]; const modules = id.toString().split('src/texts/')[1];
const moduleName = modules.split('.')[0]; const moduleName = modules.split('.')[0];
return `text/${moduleName}`; return `text/${moduleName}`;
} }
if (id.includes('src/stores')) { if (id.includes('src/stores')) {
const modules = id.toString().split('src/stores/')[1]; const modules = id.toString().split('src/stores/')[1];
const moduleName = modules.split('.')[0]; const moduleName = modules.split('.')[0];
return `store/${moduleName}`; return `store/${moduleName}`;
} }
if (id.includes('src/router.js')) {
return `router`;
}
if (id.includes('src/utils.js')) {
return `utils`;
}
if (id.includes('src/App.vue')) {
return `App`;
}
} }
} }
}, },