mirror of https://github.com/YunYouJun/valaxy
refactor: load virtual modules by import folder function
This commit is contained in:
parent
4d5b9460a0
commit
d76b956788
|
@ -93,27 +93,29 @@ For example, you can override the default font in 'styles/css-vars.scss'.
|
|||
- `text`: 文本选择图标。
|
||||
|
||||
```scss
|
||||
// $cursor-default = hexo-config('cursor.default');
|
||||
// $cursor-pointer = hexo-config('cursor.pointer');
|
||||
// $cursor-text = hexo-config('cursor.text');
|
||||
:root {
|
||||
--cursor-default: url('https://cdn.yunyoujun.cn/css/md-cursors/pointer.cur');
|
||||
--cursor-pointer: url('https://cdn.yunyoujun.cn/css/md-cursors/link.cur');
|
||||
--cursor-text: url('https://cdn.yunyoujun.cn/css/md-cursors/text.cur');
|
||||
}
|
||||
|
||||
body {
|
||||
cursor: url($cursor-default), auto;
|
||||
cursor: var(--cursor-default), auto;
|
||||
}
|
||||
|
||||
a {
|
||||
cursor: url($cursor-pointer), auto;
|
||||
cursor: var(--cursor-pointer), auto;
|
||||
|
||||
&:hover {
|
||||
cursor: url($cursor-pointer), auto;
|
||||
cursor: var(--cursor-pointer), auto;
|
||||
}
|
||||
}
|
||||
|
||||
.hty-icon-button {
|
||||
cursor: url($cursor-pointer), pointer;
|
||||
button {
|
||||
cursor: var(--cursor-pointer), pointer;
|
||||
}
|
||||
|
||||
input {
|
||||
cursor: url($cursor-text), auto;
|
||||
cursor: var(--cursor-text), text;
|
||||
}
|
||||
```
|
||||
|
|
|
@ -12,19 +12,9 @@ import AppLink from './components/AppLink.vue'
|
|||
import setupMain from './setup/main'
|
||||
|
||||
import { setupValaxyDevTools } from './utils/dev'
|
||||
// reset styles, load css before app
|
||||
// import '@unocss/reset/tailwind.css'
|
||||
// https://unocss.dev/guide/style-reset#tailwind-compat
|
||||
// minus the background color override for buttons to avoid conflicts with UI frameworks
|
||||
import '@unocss/reset/tailwind-compat.css'
|
||||
|
||||
// css
|
||||
import './styles/css/css-vars.css'
|
||||
|
||||
import './styles/css/main.css'
|
||||
// generate user styles
|
||||
import '/@valaxyjs/styles'
|
||||
import 'uno.css'
|
||||
import '#valaxy/styles'
|
||||
|
||||
const valaxyConfig = initValaxyConfig()
|
||||
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
// Types for virtual modules
|
||||
// `#valaxy/*` is an alias for `/@valaxyjs/*`, because TS will consider `/@valaxyjs/*` as an absolute path that we can't override
|
||||
|
||||
declare module 'virtual:valaxy-theme' {
|
||||
export default any
|
||||
}
|
||||
|
||||
declare module '#valaxy/styles' {
|
||||
// side-effects only
|
||||
}
|
||||
|
|
|
@ -66,6 +66,27 @@ html.dark {
|
|||
--va-c-text-3: rgba(235, 235, 245, 0.38);
|
||||
}
|
||||
|
||||
// bg
|
||||
:root {
|
||||
--va-c-bg: #ffffff;
|
||||
--va-c-bg-light: #ffffff;
|
||||
--va-c-bg-dark: #fafafa;
|
||||
--va-c-bg-opacity: rgba(255, 255, 255, 0.8);
|
||||
--va-c-bg-soft: #f9f9f9;
|
||||
--va-c-bg-alt: #f9f9f9;
|
||||
--va-c-bg-mute: #f1f1f1;
|
||||
}
|
||||
|
||||
html.dark {
|
||||
--va-c-bg: #1a1a1d;
|
||||
--va-c-bg-light: #202127;
|
||||
--va-c-bg-dark: #1a1a1a;
|
||||
--va-c-bg-opacity: rgba(0, 0, 0, 0.8);
|
||||
--va-c-bg-alt: #161618;
|
||||
--va-c-bg-soft: #202127;
|
||||
--va-c-bg-mute: #2f2f2f;
|
||||
}
|
||||
|
||||
/* code */
|
||||
:root {
|
||||
--va-code-line-height: 1.7;
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
:root {
|
||||
--va-c-bg: #ffffff;
|
||||
--va-c-bg-light: #ffffff;
|
||||
--va-c-bg-dark: #fafafa;
|
||||
--va-c-bg-opacity: rgba(255, 255, 255, 0.8);
|
||||
--va-c-bg-soft: #f9f9f9;
|
||||
--va-c-bg-alt: #f9f9f9;
|
||||
--va-c-bg-mute: #f1f1f1;
|
||||
}
|
||||
|
||||
html.dark {
|
||||
--va-c-bg: #1a1a1d;
|
||||
--va-c-bg-light: #202127;
|
||||
--va-c-bg-dark: #1a1a1a;
|
||||
--va-c-bg-opacity: rgba(0, 0, 0, 0.8);
|
||||
--va-c-bg-alt: #161618;
|
||||
--va-c-bg-soft: #202127;
|
||||
--va-c-bg-mute: #2f2f2f;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
html,
|
||||
body,
|
||||
#app {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
html {
|
||||
background-color: var(--va-c-bg);
|
||||
}
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
|
@ -10,3 +10,18 @@ $c-primary: #0078e7 !default;
|
|||
@use "css-i18n/src/styles/index.scss" as *;
|
||||
|
||||
// components import by yourself
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
html {
|
||||
background-color: var(--va-c-bg);
|
||||
}
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -3,21 +3,22 @@ import type { ResolvedValaxyOptions } from '../../options'
|
|||
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { dirname, join, resolve } from 'node:path'
|
||||
import { ensurePrefix } from '@antfu/utils'
|
||||
import consola from 'consola'
|
||||
import { colors } from 'consola/utils'
|
||||
import dayjs from 'dayjs'
|
||||
import fg from 'fast-glob'
|
||||
|
||||
import { Feed } from 'feed'
|
||||
|
||||
import fs from 'fs-extra'
|
||||
|
||||
import matter from 'gray-matter'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import ora from 'ora'
|
||||
|
||||
import ora from 'ora'
|
||||
import { getBorderCharacters, table } from 'table'
|
||||
import { matterOptions } from '../../plugins/markdown/transform/matter'
|
||||
import { ensurePrefix, isExternal } from '../../utils'
|
||||
import { isExternal } from '../../utils'
|
||||
import { getCreatedTime, getUpdatedTime } from '../../utils/date'
|
||||
|
||||
const markdown = MarkdownIt({
|
||||
|
|
|
@ -161,7 +161,12 @@ export async function getAlias(options: ResolvedValaxyOptions): Promise<AliasOpt
|
|||
{ find: 'valaxy/package.json', replacement: toAtFS(resolve(options.clientRoot, '../package.json')) },
|
||||
{ find: /^valaxy$/, replacement: toAtFS(resolve(options.clientRoot, 'index.ts')) },
|
||||
{ find: '@valaxyjs/client/', replacement: `${toAtFS(options.clientRoot)}/` },
|
||||
// virtual module to import theme
|
||||
// virtual module alias
|
||||
{
|
||||
find: /^#valaxy\/(.*)/,
|
||||
replacement: '/@valaxyjs/$1',
|
||||
},
|
||||
// import theme
|
||||
{ find: 'virtual:valaxy-theme', replacement: `${toAtFS(options.themeRoot)}/client/index.ts` },
|
||||
{ find: `valaxy-theme-${options.theme}/client`, replacement: `${toAtFS(resolve(options.themeRoot))}/client/index.ts` },
|
||||
{ find: `valaxy-theme-${options.theme}/`, replacement: `${toAtFS(resolve(options.themeRoot))}/` },
|
||||
|
|
|
@ -5,118 +5,22 @@
|
|||
import type { DefaultTheme, Pkg, SiteConfig } from 'valaxy/types'
|
||||
import type { Plugin, ResolvedConfig } from 'vite'
|
||||
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import type { PageDataPayload } from '../../../types'
|
||||
import type { ResolvedValaxyOptions, ValaxyServerOptions } from '../../options'
|
||||
import type { ValaxyNodeConfig } from '../../types'
|
||||
import consola from 'consola'
|
||||
import fs from 'fs-extra'
|
||||
import pascalCase from 'pascalcase'
|
||||
import { join, relative, resolve } from 'pathe'
|
||||
import { dim, yellow } from 'picocolors'
|
||||
import { defaultSiteConfig, mergeValaxyConfig, resolveSiteConfig, resolveUserThemeConfig } from '../../config'
|
||||
import { replaceArrMerge } from '../../config/merge'
|
||||
import { vLogger } from '../../logger'
|
||||
import { processValaxyOptions, resolveOptions, resolveThemeValaxyConfig } from '../../options'
|
||||
import { resolveImportPath, toAtFS } from '../../utils'
|
||||
import { isProd } from '../../utils/env'
|
||||
import { toAtFS } from '../../utils'
|
||||
import { countPerformanceTime } from '../../utils/performance'
|
||||
import { templates } from '../../virtual'
|
||||
import { createMarkdownToVueRenderFn } from '../markdown/markdownToVue'
|
||||
|
||||
function generateConfig(options: ResolvedValaxyOptions) {
|
||||
const routes = options.redirects.map<RouteRecordRaw>((redirect) => {
|
||||
return {
|
||||
path: redirect.from,
|
||||
redirect: redirect.to,
|
||||
}
|
||||
})
|
||||
options.config.runtimeConfig.redirects = {
|
||||
useVueRouter: isProd() ? options.config.siteConfig.redirects!.useVueRouter! : true,
|
||||
redirectRoutes: routes,
|
||||
}
|
||||
|
||||
return `export default ${JSON.stringify(JSON.stringify(options.config))}`
|
||||
}
|
||||
|
||||
/**
|
||||
* for /@valaxyjs/styles
|
||||
* @param roots
|
||||
*/
|
||||
async function generateStyles(roots: string[], options: ResolvedValaxyOptions) {
|
||||
const imports: string[] = []
|
||||
|
||||
// katex
|
||||
if (options.config.features?.katex) {
|
||||
imports.push(`import "${toAtFS(await resolveImportPath('katex/dist/katex.min.css', true))}"`)
|
||||
imports.push(`import "${toAtFS(join(options.clientRoot, 'styles/third/katex.scss'))}"`)
|
||||
}
|
||||
|
||||
for (const root of roots) {
|
||||
const styles: string[] = []
|
||||
|
||||
const autoloadNames = ['css-vars', 'index']
|
||||
autoloadNames.forEach((name) => {
|
||||
styles.push(join(root, 'styles', `${name}.css`))
|
||||
styles.push(join(root, 'styles', `${name}.scss`))
|
||||
})
|
||||
|
||||
for (const style of styles) {
|
||||
if (fs.existsSync(style))
|
||||
imports.push(`import "${toAtFS(style)}"`)
|
||||
}
|
||||
}
|
||||
|
||||
return imports.join('\n')
|
||||
}
|
||||
|
||||
function generateLocales(roots: string[]) {
|
||||
const imports: string[] = [
|
||||
'import { createDefu } from "defu"',
|
||||
'const messages = { "zh-CN": {}, en: {} }',
|
||||
`
|
||||
const replaceArrMerge = createDefu((obj, key, value) => {
|
||||
if (key && obj[key] && Array.isArray(obj[key]) && Array.isArray(value)) {
|
||||
obj[key] = value
|
||||
return true
|
||||
}
|
||||
})
|
||||
`,
|
||||
]
|
||||
const languages = ['zh-CN', 'en']
|
||||
|
||||
roots.forEach((root, i) => {
|
||||
languages.forEach((lang) => {
|
||||
const langYml = `${root}/locales/${lang}.yml`
|
||||
if (fs.existsSync(langYml) && fs.readFileSync(langYml, 'utf-8')) {
|
||||
const varName = lang.replace('-', '') + i
|
||||
imports.unshift(`import ${varName} from "${toAtFS(langYml)}"`)
|
||||
// pre override next
|
||||
imports.push(`messages['${lang}'] = replaceArrMerge(${varName}, messages['${lang}'])`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
imports.push('export default messages')
|
||||
return imports.join('\n')
|
||||
}
|
||||
|
||||
function generateAddons(options: ResolvedValaxyOptions) {
|
||||
const globalAddonComponents = options.addons
|
||||
.filter(v => v.global)
|
||||
.filter(v => fs.existsSync(join(v.root, './App.vue')))
|
||||
const spliceImportName = (str: string) => `Addon${pascalCase(str)}App`
|
||||
|
||||
const imports = globalAddonComponents
|
||||
.map(addon => `import ${spliceImportName(addon.name)} from "${addon.name}/App.vue"`)
|
||||
.join('\n')
|
||||
|
||||
const components = globalAddonComponents
|
||||
.map(addon => `{ component: ${spliceImportName(addon.name)}, props: ${JSON.stringify(addon.props)} }`)
|
||||
.join(',')
|
||||
|
||||
return `${imports}\n` + `export default [${components}]`
|
||||
}
|
||||
|
||||
/**
|
||||
* vue component render null
|
||||
*/
|
||||
|
@ -151,8 +55,6 @@ export async function createValaxyLoader(options: ResolvedValaxyOptions, serverO
|
|||
|
||||
const valaxyPrefix = '/@valaxy'
|
||||
|
||||
const roots = options.roots
|
||||
|
||||
let hasDeadLinks = false
|
||||
|
||||
let markdownToVue: Awaited<ReturnType<typeof createMarkdownToVueRenderFn>>
|
||||
|
@ -187,9 +89,13 @@ export async function createValaxyLoader(options: ResolvedValaxyOptions, serverO
|
|||
},
|
||||
|
||||
async load(id) {
|
||||
if (id === '/@valaxyjs/config')
|
||||
// stringify twice for \"
|
||||
return generateConfig(options)
|
||||
const template = templates.find(t => t.id === id)
|
||||
if (template) {
|
||||
return {
|
||||
code: await template.getContent.call(this, options),
|
||||
map: { mappings: '' },
|
||||
}
|
||||
}
|
||||
|
||||
if (id === '/@valaxyjs/context') {
|
||||
return `export default ${JSON.stringify(JSON.stringify({
|
||||
|
@ -201,16 +107,6 @@ export async function createValaxyLoader(options: ResolvedValaxyOptions, serverO
|
|||
// TODO: custom dynamic css vars
|
||||
// if (id === 'virtual:valaxy-css-vars') {}
|
||||
|
||||
// generate styles
|
||||
if (id === '/@valaxyjs/styles')
|
||||
return await generateStyles(roots, options)
|
||||
|
||||
if (id === '/@valaxyjs/locales')
|
||||
return generateLocales(roots)
|
||||
|
||||
if (id === '/@valaxyjs/addons')
|
||||
return generateAddons(options)
|
||||
|
||||
// root client
|
||||
if (id === '/@valaxyjs/AppVue')
|
||||
return generateAppVue(options.clientRoot)
|
||||
|
|
|
@ -8,24 +8,6 @@ export function isExternal(str: string) {
|
|||
return EXTERNAL_URL_RE.test(str)
|
||||
}
|
||||
|
||||
/**
|
||||
* slash path for windows
|
||||
* @param str
|
||||
*/
|
||||
export function slash(str: string) {
|
||||
return str.replace(/\\/g, '/')
|
||||
}
|
||||
|
||||
export function ensurePrefix(prefix: string, str: string) {
|
||||
if (!str.startsWith(prefix))
|
||||
return prefix + str
|
||||
return str
|
||||
}
|
||||
|
||||
export function toAtFS(path: string) {
|
||||
return `/@fs${ensurePrefix('/', slash(path))}`
|
||||
}
|
||||
|
||||
export function isPath(name: string) {
|
||||
return name.startsWith('/') || /^\.\.?[/\\]/.test(name)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,21 @@
|
|||
import { ensurePrefix, slash } from '@antfu/utils'
|
||||
import consola from 'consola'
|
||||
import { resolvePath } from 'mlly'
|
||||
import { resolveGlobal } from 'resolve-global'
|
||||
|
||||
export const isInstalledGlobally: { value?: boolean } = {}
|
||||
|
||||
/**
|
||||
* Resolve path for import url on Vite client side
|
||||
*/
|
||||
export async function resolveImportUrl(id: string) {
|
||||
return toAtFS(await resolveImportPath(id, true))
|
||||
}
|
||||
|
||||
export function toAtFS(path: string) {
|
||||
return `/@fs${ensurePrefix('/', slash(path))}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Before is CJS: use 'resolve'
|
||||
* ESM: use 'mlly'
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import type { VirtualModuleTemplate } from './types'
|
||||
import fs from 'fs-extra'
|
||||
import pascalCase from 'pascalcase'
|
||||
import { join } from 'pathe'
|
||||
|
||||
export const templateAddons: VirtualModuleTemplate = {
|
||||
id: '/@valaxyjs/addons',
|
||||
async getContent(options) {
|
||||
const globalAddonComponents = options.addons
|
||||
.filter(v => v.global)
|
||||
.filter(v => fs.existsSync(join(v.root, './App.vue')))
|
||||
const spliceImportName = (str: string) => `Addon${pascalCase(str)}App`
|
||||
|
||||
const imports = globalAddonComponents
|
||||
.map(addon => `import ${spliceImportName(addon.name)} from "${addon.name}/App.vue"`)
|
||||
.join('\n')
|
||||
|
||||
const components = globalAddonComponents
|
||||
.map(addon => `{ component: ${spliceImportName(addon.name)}, props: ${JSON.stringify(addon.props)} }`)
|
||||
.join(',')
|
||||
|
||||
return `${imports}\n` + `export default [${components}]`
|
||||
},
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import type { VirtualModuleTemplate } from './types'
|
||||
import { isProd } from '../utils/env'
|
||||
|
||||
export const templateConfig: VirtualModuleTemplate = {
|
||||
id: '/@valaxyjs/config',
|
||||
async getContent(options) {
|
||||
const routes = options.redirects.map<RouteRecordRaw>((redirect) => {
|
||||
return {
|
||||
path: redirect.from,
|
||||
redirect: redirect.to,
|
||||
}
|
||||
})
|
||||
options.config.runtimeConfig.redirects = {
|
||||
useVueRouter: isProd() ? options.config.siteConfig.redirects!.useVueRouter! : true,
|
||||
redirectRoutes: routes,
|
||||
}
|
||||
|
||||
// stringify twice for \"
|
||||
return `export default ${JSON.stringify(JSON.stringify(options.config))}`
|
||||
},
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import { templateAddons } from './addons'
|
||||
import { templateConfig } from './config'
|
||||
import { templateLocales } from './locales'
|
||||
import { templateStyles } from './styles'
|
||||
|
||||
export const templates = [
|
||||
templateAddons,
|
||||
templateConfig,
|
||||
templateLocales,
|
||||
templateStyles,
|
||||
]
|
|
@ -0,0 +1,37 @@
|
|||
import type { VirtualModuleTemplate } from './types'
|
||||
import fs from 'fs-extra'
|
||||
import { toAtFS } from '../utils'
|
||||
|
||||
export const templateLocales: VirtualModuleTemplate = {
|
||||
id: '/@valaxyjs/locales',
|
||||
async getContent({ roots }) {
|
||||
const imports: string[] = [
|
||||
'import { createDefu } from "defu"',
|
||||
'const messages = { "zh-CN": {}, en: {} }',
|
||||
`
|
||||
const replaceArrMerge = createDefu((obj, key, value) => {
|
||||
if (key && obj[key] && Array.isArray(obj[key]) && Array.isArray(value)) {
|
||||
obj[key] = value
|
||||
return true
|
||||
}
|
||||
})
|
||||
`,
|
||||
]
|
||||
const languages = ['zh-CN', 'en']
|
||||
|
||||
roots.forEach((root, i) => {
|
||||
languages.forEach((lang) => {
|
||||
const langYml = `${root}/locales/${lang}.yml`
|
||||
if (fs.existsSync(langYml) && fs.readFileSync(langYml, 'utf-8')) {
|
||||
const varName = lang.replace('-', '') + i
|
||||
imports.unshift(`import ${varName} from "${toAtFS(langYml)}"`)
|
||||
// pre override next
|
||||
imports.push(`messages['${lang}'] = replaceArrMerge(${varName}, messages['${lang}'])`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
imports.push('export default messages')
|
||||
return imports.join('\n')
|
||||
},
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import type { VirtualModuleTemplate } from './types'
|
||||
import { existsSync } from 'node:fs'
|
||||
|
||||
import { join } from 'node:path'
|
||||
import { resolveImportUrl, toAtFS } from '../utils'
|
||||
|
||||
export const templateStyles: VirtualModuleTemplate = {
|
||||
id: '/@valaxyjs/styles',
|
||||
async getContent({ clientRoot, roots, config }) {
|
||||
function resolveUrlOfClient(name: string) {
|
||||
return toAtFS(join(clientRoot, name))
|
||||
}
|
||||
|
||||
const imports: string[] = []
|
||||
|
||||
if (config.features?.katex) {
|
||||
imports.push(`import "${await resolveImportUrl('katex/dist/katex.min.css')}"`)
|
||||
imports.push(`import "${resolveUrlOfClient('styles/third/katex.scss')}"`)
|
||||
}
|
||||
|
||||
for (const root of roots) {
|
||||
const styles = [
|
||||
join(root, 'styles', 'index.ts'),
|
||||
join(root, 'styles', 'index.css'),
|
||||
join(root, 'styles', 'index.scss'),
|
||||
|
||||
join(root, 'styles', 'css-vars.css'),
|
||||
join(root, 'styles', 'css-vars.scss'),
|
||||
]
|
||||
|
||||
for (const style of styles) {
|
||||
if (existsSync(style)) {
|
||||
imports.push(`import "${toAtFS(style)}"`)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset styles, load css before app
|
||||
// import '@unocss/reset/tailwind.css'
|
||||
// https://unocss.dev/guide/style-reset#tailwind-compat
|
||||
// minus the background color override for buttons to avoid conflicts with UI frameworks
|
||||
imports.unshift(`import "${await resolveImportUrl('@unocss/reset/tailwind-compat.css')}"`)
|
||||
imports.push('import "uno.css"')
|
||||
|
||||
return imports.join('\n')
|
||||
},
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import type { Awaitable } from '@antfu/utils'
|
||||
import type { PluginContext } from 'rollup'
|
||||
import type { ResolvedValaxyOptions } from '../options'
|
||||
|
||||
export interface VirtualModuleTemplate {
|
||||
id: string
|
||||
getContent: (this: PluginContext, options: ResolvedValaxyOptions) => Awaitable<string>
|
||||
}
|
|
@ -17,6 +17,7 @@ export default defineConfig((options) => {
|
|||
format: ['esm'],
|
||||
minify: !options.watch,
|
||||
external: [
|
||||
'/@valaxyjs/',
|
||||
'/@valaxyjs/config',
|
||||
'/@valaxyjs/context',
|
||||
|
||||
|
|
Loading…
Reference in New Issue