mirror of https://github.com/vuejs/vitepress
commit
98135f7ce4
@ -1,8 +1,14 @@
|
||||
export default {
|
||||
async paths() {
|
||||
return [
|
||||
{ params: { id: 'foo' }, content: `# Foo` },
|
||||
{ params: { id: 'bar' }, content: `# Bar` }
|
||||
]
|
||||
import { defineRoutes } from 'vitepress'
|
||||
import paths from './paths'
|
||||
|
||||
export default defineRoutes({
|
||||
async paths(watchedFiles: string[]) {
|
||||
// console.log('watchedFiles', watchedFiles)
|
||||
return paths
|
||||
},
|
||||
watch: ['../data-loading/**/*.json'],
|
||||
async transformPageData(pageData) {
|
||||
// console.log('transformPageData', pageData.filePath)
|
||||
pageData.title += ' - transformed'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
export default [
|
||||
{ params: { id: 'foo' }, content: `# Foo` },
|
||||
{ params: { id: 'bar' }, content: `# Bar` }
|
||||
]
|
||||
@ -0,0 +1,27 @@
|
||||
# header 1
|
||||
|
||||
header 1 content
|
||||
|
||||
## header 1.1
|
||||
|
||||
header 1.1 content
|
||||
|
||||
### header 1.1.1
|
||||
|
||||
header 1.1.1 content
|
||||
|
||||
### header 1.1.2
|
||||
|
||||
header 1.1.2 content
|
||||
|
||||
## header 1.2
|
||||
|
||||
header 1.2 content
|
||||
|
||||
### header 1.2.1
|
||||
|
||||
header 1.2.1 content
|
||||
|
||||
### header 1.2.2
|
||||
|
||||
header 1.2.2 content
|
||||
@ -0,0 +1,77 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`node/postcss/isolateStyles > transforms selectors and skips keyframes 1`] = `
|
||||
"
|
||||
/* simple classes */
|
||||
.example:not(:where(.vp-raw, .vp-raw *)) { color: red; }
|
||||
.class-a:not(:where(.vp-raw, .vp-raw *)) { color: coral; }
|
||||
.class-b:not(:where(.vp-raw, .vp-raw *)) { color: deepskyblue; }
|
||||
|
||||
/* escaped colon in class */
|
||||
.baz\\:not\\(.bar\\):not(:where(.vp-raw, .vp-raw *)) { display: block; }
|
||||
.disabled\\:opacity-50:not(:where(.vp-raw, .vp-raw *)):disabled { opacity: .5; }
|
||||
|
||||
/* pseudos (class + element) */
|
||||
.button:not(:where(.vp-raw, .vp-raw *)):hover { color: pink; }
|
||||
.button:not(:where(.vp-raw, .vp-raw *)):focus:hover { color: hotpink; }
|
||||
.item:not(:where(.vp-raw, .vp-raw *))::before { content: '•'; }
|
||||
:not(:where(.vp-raw, .vp-raw *))::first-letter { color: pink; }
|
||||
:not(:where(.vp-raw, .vp-raw *))::before { content: ''; }
|
||||
|
||||
/* universal + :not */
|
||||
*:not(:where(.vp-raw, .vp-raw *)) { background-color: red; }
|
||||
*:not(:where(.vp-raw, .vp-raw *)):not(.b) { text-transform: uppercase; }
|
||||
|
||||
/* combinators */
|
||||
.foo:hover .bar:not(:where(.vp-raw, .vp-raw *)) { background: blue; }
|
||||
ul > li.active:not(:where(.vp-raw, .vp-raw *)) { color: green; }
|
||||
a + b ~ c:not(:where(.vp-raw, .vp-raw *)) { color: orange; }
|
||||
|
||||
/* ids + attribute selectors */
|
||||
#wow:not(:where(.vp-raw, .vp-raw *)) { color: yellow; }
|
||||
[data-world] .d:not(:where(.vp-raw, .vp-raw *)) { padding: 10px 20px; }
|
||||
|
||||
/* :root and chained tags */
|
||||
:not(:where(.vp-raw, .vp-raw *)):root { --bs-blue: #0d6efd; }
|
||||
:root .a:not(:where(.vp-raw, .vp-raw *)) { --bs-green: #bada55; }
|
||||
html:not(:where(.vp-raw, .vp-raw *)) { margin: 0; }
|
||||
body:not(:where(.vp-raw, .vp-raw *)) { padding: 0; }
|
||||
html body div:not(:where(.vp-raw, .vp-raw *)) { color: blue; }
|
||||
|
||||
/* grouping with commas */
|
||||
.a:not(:where(.vp-raw, .vp-raw *)), .b:not(:where(.vp-raw, .vp-raw *)) { color: red; }
|
||||
|
||||
/* multiple repeated groups to ensure stability */
|
||||
.a:not(:where(.vp-raw, .vp-raw *)), .b:not(:where(.vp-raw, .vp-raw *)) { color: coral; }
|
||||
.a:not(:where(.vp-raw, .vp-raw *)) { animation: glow 1s linear infinite alternate; }
|
||||
|
||||
/* nested blocks */
|
||||
.foo:not(:where(.vp-raw, .vp-raw *)) {
|
||||
svg:not(:where(.vp-raw, .vp-raw *)) { display: none; }
|
||||
.bar:not(:where(.vp-raw, .vp-raw *)) { display: inline; }
|
||||
}
|
||||
|
||||
/* standalone pseudos */
|
||||
:not(:where(.vp-raw, .vp-raw *)):first-child { color: pink; }
|
||||
:not(:where(.vp-raw, .vp-raw *)):hover { color: blue; }
|
||||
:not(:where(.vp-raw, .vp-raw *)):active { color: red; }
|
||||
|
||||
/* keyframes (should be ignored) */
|
||||
@keyframes fade {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
@-webkit-keyframes glow {
|
||||
from { color: coral; }
|
||||
to { color: red; }
|
||||
}
|
||||
@-moz-keyframes glow {
|
||||
from { color: coral; }
|
||||
to { color: red; }
|
||||
}
|
||||
@-o-keyframes glow {
|
||||
from { color: coral; }
|
||||
to { color: red; }
|
||||
}
|
||||
"
|
||||
`;
|
||||
@ -0,0 +1,93 @@
|
||||
import { postcssIsolateStyles } from 'node/postcss/isolateStyles'
|
||||
import postcss from 'postcss'
|
||||
|
||||
const INPUT_CSS = `
|
||||
/* simple classes */
|
||||
.example { color: red; }
|
||||
.class-a { color: coral; }
|
||||
.class-b { color: deepskyblue; }
|
||||
|
||||
/* escaped colon in class */
|
||||
.baz\\:not\\(.bar\\) { display: block; }
|
||||
.disabled\\:opacity-50:disabled { opacity: .5; }
|
||||
|
||||
/* pseudos (class + element) */
|
||||
.button:hover { color: pink; }
|
||||
.button:focus:hover { color: hotpink; }
|
||||
.item::before { content: '•'; }
|
||||
::first-letter { color: pink; }
|
||||
::before { content: ''; }
|
||||
|
||||
/* universal + :not */
|
||||
* { background-color: red; }
|
||||
*:not(.b) { text-transform: uppercase; }
|
||||
|
||||
/* combinators */
|
||||
.foo:hover .bar { background: blue; }
|
||||
ul > li.active { color: green; }
|
||||
a + b ~ c { color: orange; }
|
||||
|
||||
/* ids + attribute selectors */
|
||||
#wow { color: yellow; }
|
||||
[data-world] .d { padding: 10px 20px; }
|
||||
|
||||
/* :root and chained tags */
|
||||
:root { --bs-blue: #0d6efd; }
|
||||
:root .a { --bs-green: #bada55; }
|
||||
html { margin: 0; }
|
||||
body { padding: 0; }
|
||||
html body div { color: blue; }
|
||||
|
||||
/* grouping with commas */
|
||||
.a, .b { color: red; }
|
||||
|
||||
/* multiple repeated groups to ensure stability */
|
||||
.a, .b { color: coral; }
|
||||
.a { animation: glow 1s linear infinite alternate; }
|
||||
|
||||
/* nested blocks */
|
||||
.foo {
|
||||
svg { display: none; }
|
||||
.bar { display: inline; }
|
||||
}
|
||||
|
||||
/* standalone pseudos */
|
||||
:first-child { color: pink; }
|
||||
:hover { color: blue; }
|
||||
:active { color: red; }
|
||||
|
||||
/* keyframes (should be ignored) */
|
||||
@keyframes fade {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
@-webkit-keyframes glow {
|
||||
from { color: coral; }
|
||||
to { color: red; }
|
||||
}
|
||||
@-moz-keyframes glow {
|
||||
from { color: coral; }
|
||||
to { color: red; }
|
||||
}
|
||||
@-o-keyframes glow {
|
||||
from { color: coral; }
|
||||
to { color: red; }
|
||||
}
|
||||
`
|
||||
|
||||
describe('node/postcss/isolateStyles', () => {
|
||||
test('transforms selectors and skips keyframes', () => {
|
||||
const out = run(INPUT_CSS)
|
||||
expect(out.css).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('idempotent (running twice produces identical CSS)', () => {
|
||||
const first = run(INPUT_CSS).css
|
||||
const second = run(first).css
|
||||
expect(second).toBe(first)
|
||||
})
|
||||
})
|
||||
|
||||
function run(css: string, from = 'src/styles/vp-doc.css') {
|
||||
return postcss([postcssIsolateStyles()]).process(css, { from })
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
import { ModuleGraph } from 'node/utils/moduleGraph'
|
||||
|
||||
describe('node/utils/moduleGraph', () => {
|
||||
let graph: ModuleGraph
|
||||
|
||||
beforeEach(() => {
|
||||
graph = new ModuleGraph()
|
||||
})
|
||||
|
||||
it('should correctly delete a module and its dependents', () => {
|
||||
graph.add('A', ['B', 'C'])
|
||||
graph.add('B', ['D'])
|
||||
graph.add('C', [])
|
||||
graph.add('D', [])
|
||||
|
||||
expect(graph.delete('D')).toEqual(new Set(['D', 'B', 'A']))
|
||||
})
|
||||
|
||||
it('should handle shared dependencies correctly', () => {
|
||||
graph.add('A', ['B', 'C'])
|
||||
graph.add('B', ['D'])
|
||||
graph.add('C', ['D']) // Shared dependency
|
||||
graph.add('D', [])
|
||||
|
||||
expect(graph.delete('D')).toEqual(new Set(['A', 'B', 'C', 'D']))
|
||||
})
|
||||
|
||||
it('merges dependencies correctly', () => {
|
||||
// Add module A with dependency B
|
||||
graph.add('A', ['B'])
|
||||
// Merge new dependency C into module A (B should remain)
|
||||
graph.add('A', ['C'])
|
||||
|
||||
// Deleting B should remove A as well, since A depends on B.
|
||||
expect(graph.delete('B')).toEqual(new Set(['B', 'A']))
|
||||
})
|
||||
|
||||
it('handles cycles gracefully', () => {
|
||||
// Create a cycle: A -> B, B -> C, C -> A.
|
||||
graph.add('A', ['B'])
|
||||
graph.add('B', ['C'])
|
||||
graph.add('C', ['A'])
|
||||
|
||||
// Deleting any module in the cycle should delete all modules in the cycle.
|
||||
expect(graph.delete('A')).toEqual(new Set(['A', 'B', 'C']))
|
||||
})
|
||||
|
||||
it('cleans up dependencies when deletion', () => {
|
||||
// Setup A -> B relationship.
|
||||
graph.add('A', ['B'])
|
||||
graph.add('B', [])
|
||||
|
||||
// Deleting B should remove both B and A from the graph.
|
||||
expect(graph.delete('B')).toEqual(new Set(['B', 'A']))
|
||||
|
||||
// After deletion, add modules again.
|
||||
graph.add('C', [])
|
||||
graph.add('A', ['C']) // Now A depends only on C.
|
||||
|
||||
expect(graph.delete('C')).toEqual(new Set(['C', 'A']))
|
||||
})
|
||||
|
||||
it('handles independent modules', () => {
|
||||
// Modules with no dependencies.
|
||||
graph.add('X', [])
|
||||
graph.add('Y', [])
|
||||
|
||||
// Deletion of one should only remove that module.
|
||||
expect(graph.delete('X')).toEqual(new Set(['X']))
|
||||
expect(graph.delete('Y')).toEqual(new Set(['Y']))
|
||||
})
|
||||
})
|
||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
@ -1,2 +1,16 @@
|
||||
#!/usr/bin/env node
|
||||
// @ts-check
|
||||
|
||||
import module from 'node:module'
|
||||
|
||||
// https://github.com/vitejs/vite/blob/6c8a5a27e645a182f5b03a4ed6aa726eab85993f/packages/vite/bin/vite.js#L48-L63
|
||||
try {
|
||||
module.enableCompileCache?.()
|
||||
setTimeout(() => {
|
||||
try {
|
||||
module.flushCompileCache?.()
|
||||
} catch {}
|
||||
}, 10 * 1000).unref()
|
||||
} catch {}
|
||||
|
||||
import('../dist/node/cli.js')
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
{
|
||||
"plugins": {
|
||||
"postcss-rtlcss": {
|
||||
"ltrPrefix": ":where([dir=\"ltr\"])",
|
||||
"rtlPrefix": ":where([dir=\"rtl\"])"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,174 @@
|
||||
import {
|
||||
defineConfig,
|
||||
resolveSiteDataByRoute,
|
||||
type HeadConfig
|
||||
} from 'vitepress'
|
||||
import {
|
||||
groupIconMdPlugin,
|
||||
groupIconVitePlugin,
|
||||
localIconLoader
|
||||
} from 'vitepress-plugin-group-icons'
|
||||
import llmstxt from 'vitepress-plugin-llms'
|
||||
|
||||
const prod = !!process.env.NETLIFY
|
||||
|
||||
export default defineConfig({
|
||||
title: 'VitePress',
|
||||
|
||||
rewrites: {
|
||||
'en/:rest*': ':rest*'
|
||||
},
|
||||
|
||||
lastUpdated: true,
|
||||
cleanUrls: true,
|
||||
metaChunk: true,
|
||||
|
||||
markdown: {
|
||||
math: true,
|
||||
codeTransformers: [
|
||||
// We use `[!!code` in demo to prevent transformation, here we revert it back.
|
||||
{
|
||||
postprocess(code) {
|
||||
return code.replace(/\[\!\!code/g, '[!code')
|
||||
}
|
||||
}
|
||||
],
|
||||
config(md) {
|
||||
// TODO: remove when https://github.com/vuejs/vitepress/issues/4431 is fixed
|
||||
const fence = md.renderer.rules.fence!
|
||||
md.renderer.rules.fence = function (tokens, idx, options, env, self) {
|
||||
const { localeIndex = 'root' } = env
|
||||
const codeCopyButtonTitle = (() => {
|
||||
switch (localeIndex) {
|
||||
case 'es':
|
||||
return 'Copiar código'
|
||||
case 'fa':
|
||||
return 'کپی کد'
|
||||
case 'ko':
|
||||
return '코드 복사'
|
||||
case 'pt':
|
||||
return 'Copiar código'
|
||||
case 'ru':
|
||||
return 'Скопировать код'
|
||||
case 'zh':
|
||||
return '复制代码'
|
||||
case 'ja':
|
||||
return 'コードをコピー'
|
||||
default:
|
||||
return 'Copy code'
|
||||
}
|
||||
})()
|
||||
return fence(tokens, idx, options, env, self).replace(
|
||||
'<button title="Copy Code" class="copy"></button>',
|
||||
`<button title="${codeCopyButtonTitle}" class="copy"></button>`
|
||||
)
|
||||
}
|
||||
md.use(groupIconMdPlugin)
|
||||
}
|
||||
},
|
||||
|
||||
sitemap: {
|
||||
hostname: 'https://vitepress.dev',
|
||||
transformItems(items) {
|
||||
return items.filter((item) => !item.url.includes('migration'))
|
||||
}
|
||||
},
|
||||
|
||||
head: [
|
||||
[
|
||||
'link',
|
||||
{ rel: 'icon', type: 'image/svg+xml', href: '/vitepress-logo-mini.svg' }
|
||||
],
|
||||
[
|
||||
'link',
|
||||
{ rel: 'icon', type: 'image/png', href: '/vitepress-logo-mini.png' }
|
||||
],
|
||||
['meta', { name: 'theme-color', content: '#5f67ee' }],
|
||||
['meta', { property: 'og:type', content: 'website' }],
|
||||
['meta', { property: 'og:site_name', content: 'VitePress' }],
|
||||
[
|
||||
'meta',
|
||||
{
|
||||
property: 'og:image',
|
||||
content: 'https://vitepress.dev/vitepress-og.jpg'
|
||||
}
|
||||
],
|
||||
['meta', { property: 'og:url', content: 'https://vitepress.dev/' }],
|
||||
[
|
||||
'script',
|
||||
{
|
||||
src: 'https://cdn.usefathom.com/script.js',
|
||||
'data-site': 'AZBRSFGG',
|
||||
'data-spa': 'auto',
|
||||
defer: ''
|
||||
}
|
||||
]
|
||||
],
|
||||
|
||||
themeConfig: {
|
||||
logo: { src: '/vitepress-logo-mini.svg', width: 24, height: 24 },
|
||||
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/vuejs/vitepress' }
|
||||
],
|
||||
|
||||
search: {
|
||||
provider: 'algolia',
|
||||
options: {
|
||||
appId: '8J64VVRP8K',
|
||||
apiKey: '52f578a92b88ad6abde815aae2b0ad7c',
|
||||
indexName: 'vitepress',
|
||||
askAi: 'YaVSonfX5bS8'
|
||||
}
|
||||
},
|
||||
|
||||
carbonAds: { code: 'CEBDT27Y', placement: 'vuejsorg' }
|
||||
},
|
||||
|
||||
locales: {
|
||||
root: { label: 'English', lang: 'en-US', dir: 'ltr' },
|
||||
zh: { label: '简体中文', lang: 'zh-Hans', dir: 'ltr' },
|
||||
pt: { label: 'Português', lang: 'pt-BR', dir: 'ltr' },
|
||||
ru: { label: 'Русский', lang: 'ru-RU', dir: 'ltr' },
|
||||
es: { label: 'Español', lang: 'es', dir: 'ltr' },
|
||||
ko: { label: '한국어', lang: 'ko-KR', dir: 'ltr' },
|
||||
fa: { label: 'فارسی', lang: 'fa-IR', dir: 'rtl' },
|
||||
ja: { label: '日本語', lang: 'ja', dir: 'ltr' }
|
||||
},
|
||||
|
||||
vite: {
|
||||
plugins: [
|
||||
groupIconVitePlugin({
|
||||
customIcon: {
|
||||
vitepress: localIconLoader(
|
||||
import.meta.url,
|
||||
'../public/vitepress-logo-mini.svg'
|
||||
),
|
||||
firebase: 'logos:firebase'
|
||||
}
|
||||
}),
|
||||
prod &&
|
||||
llmstxt({
|
||||
workDir: 'en',
|
||||
ignoreFiles: ['index.md']
|
||||
})
|
||||
],
|
||||
experimental: {
|
||||
enableNativePlugin: true
|
||||
}
|
||||
},
|
||||
|
||||
transformPageData: prod
|
||||
? (pageData, ctx) => {
|
||||
const site = resolveSiteDataByRoute(
|
||||
ctx.siteConfig.site,
|
||||
pageData.relativePath
|
||||
)
|
||||
const title = `${pageData.title || site.title} | ${pageData.description || site.description}`
|
||||
;((pageData.frontmatter.head ??= []) as HeadConfig[]).push(
|
||||
['meta', { property: 'og:locale', content: site.lang }],
|
||||
['meta', { property: 'og:title', content: title }]
|
||||
)
|
||||
}
|
||||
: undefined
|
||||
})
|
||||
@ -1,20 +0,0 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
import { shared } from './shared'
|
||||
import { en } from './en'
|
||||
import { zh } from './zh'
|
||||
import { pt } from './pt'
|
||||
import { ru } from './ru'
|
||||
import { es } from './es'
|
||||
import { ko } from './ko'
|
||||
|
||||
export default defineConfig({
|
||||
...shared,
|
||||
locales: {
|
||||
root: { label: 'English', ...en },
|
||||
zh: { label: '简体中文', ...zh },
|
||||
pt: { label: 'Português', ...pt },
|
||||
ru: { label: 'Русский', ...ru },
|
||||
es: { label: 'Español', ...es },
|
||||
ko: { label: '한국어', ...ko }
|
||||
}
|
||||
})
|
||||
@ -1,77 +0,0 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
import { search as zhSearch } from './zh'
|
||||
import { search as ptSearch } from './pt'
|
||||
import { search as ruSearch } from './ru'
|
||||
import { search as esSearch } from './es'
|
||||
import { search as koSearch } from './ko'
|
||||
|
||||
export const shared = defineConfig({
|
||||
title: 'VitePress',
|
||||
|
||||
rewrites: {
|
||||
'en/:rest*': ':rest*'
|
||||
},
|
||||
|
||||
lastUpdated: true,
|
||||
cleanUrls: true,
|
||||
metaChunk: true,
|
||||
|
||||
markdown: {
|
||||
math: true,
|
||||
codeTransformers: [
|
||||
// We use `[!!code` in demo to prevent transformation, here we revert it back.
|
||||
{
|
||||
postprocess(code) {
|
||||
return code.replace(/\[\!\!code/g, '[!code')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
sitemap: {
|
||||
hostname: 'https://vitepress.dev',
|
||||
transformItems(items) {
|
||||
return items.filter((item) => !item.url.includes('migration'))
|
||||
}
|
||||
},
|
||||
|
||||
/* prettier-ignore */
|
||||
head: [
|
||||
['link', { rel: 'icon', type: 'image/svg+xml', href: '/vitepress-logo-mini.svg' }],
|
||||
['link', { rel: 'icon', type: 'image/png', href: '/vitepress-logo-mini.png' }],
|
||||
['meta', { name: 'theme-color', content: '#5f67ee' }],
|
||||
['meta', { property: 'og:type', content: 'website' }],
|
||||
['meta', { property: 'og:locale', content: 'en' }],
|
||||
['meta', { property: 'og:title', content: 'VitePress | Vite & Vue Powered Static Site Generator' }],
|
||||
['meta', { property: 'og:site_name', content: 'VitePress' }],
|
||||
['meta', { property: 'og:image', content: 'https://vitepress.dev/vitepress-og.jpg' }],
|
||||
['meta', { property: 'og:url', content: 'https://vitepress.dev/' }],
|
||||
['script', { src: 'https://cdn.usefathom.com/script.js', 'data-site': 'AZBRSFGG', 'data-spa': 'auto', defer: '' }]
|
||||
],
|
||||
|
||||
themeConfig: {
|
||||
logo: { src: '/vitepress-logo-mini.svg', width: 24, height: 24 },
|
||||
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/vuejs/vitepress' }
|
||||
],
|
||||
|
||||
search: {
|
||||
provider: 'algolia',
|
||||
options: {
|
||||
appId: '8J64VVRP8K',
|
||||
apiKey: '52f578a92b88ad6abde815aae2b0ad7c',
|
||||
indexName: 'vitepress',
|
||||
locales: {
|
||||
...zhSearch,
|
||||
...ptSearch,
|
||||
...ruSearch,
|
||||
...esSearch,
|
||||
...koSearch
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
carbonAds: { code: 'CEBDT27Y', placement: 'vuejsorg' }
|
||||
}
|
||||
})
|
||||
@ -0,0 +1,5 @@
|
||||
import Theme from 'vitepress/theme'
|
||||
import 'virtual:group-icons.css'
|
||||
import './styles.css'
|
||||
|
||||
export default Theme
|
||||
@ -0,0 +1,43 @@
|
||||
:root:where(:lang(fa)) {
|
||||
--vp-font-family-base:
|
||||
'Vazirmatn', 'Inter', ui-sans-serif, system-ui, sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
|
||||
:root {
|
||||
--vp-home-hero-name-color: transparent;
|
||||
--vp-home-hero-name-background: -webkit-linear-gradient(
|
||||
120deg,
|
||||
#bd34fe 30%,
|
||||
#41d1ff
|
||||
);
|
||||
--vp-home-hero-image-background-image: linear-gradient(
|
||||
-45deg,
|
||||
#bd34fe 50%,
|
||||
#47caff 50%
|
||||
);
|
||||
--vp-home-hero-image-filter: blur(44px);
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
:root {
|
||||
--vp-home-hero-image-filter: blur(56px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
:root {
|
||||
--vp-home-hero-image-filter: blur(68px);
|
||||
}
|
||||
}
|
||||
|
||||
.VPHero .VPImage {
|
||||
filter: drop-shadow(-2px 4px 6px rgba(0, 0, 0, 0.2));
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
/* used in reference/default-theme-search */
|
||||
img[src='/search.png'] {
|
||||
width: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
@ -0,0 +1,258 @@
|
||||
import { createRequire } from 'module'
|
||||
import { defineAdditionalConfig, type DefaultTheme } from 'vitepress'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const pkg = require('vitepress/package.json')
|
||||
|
||||
export default defineAdditionalConfig({
|
||||
description: 'ژنراتور استاتیک وبسایت با Vite و Vue',
|
||||
|
||||
// prettier-ignore
|
||||
head: [
|
||||
['link', { rel: 'preconnect', href: 'https://fonts.googleapis.com' }],
|
||||
['link', { rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: '' }],
|
||||
['link', { href: 'https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100..900&display=swap', rel: 'stylesheet' }],
|
||||
],
|
||||
|
||||
themeConfig: {
|
||||
nav: nav(),
|
||||
|
||||
search: { options: searchOptions() },
|
||||
|
||||
sidebar: {
|
||||
'/fa/guide/': { base: '/fa/guide/', items: sidebarGuide() },
|
||||
'/fa/reference/': { base: '/fa/reference/', items: sidebarReference() }
|
||||
},
|
||||
|
||||
editLink: {
|
||||
pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
|
||||
text: 'ویرایش این صفحه در گیتهاب'
|
||||
},
|
||||
|
||||
footer: {
|
||||
message: 'انتشار یافته تحت لایسنس MIT',
|
||||
copyright: 'حق نسخهبرداری © 2019-کنون Evan You'
|
||||
},
|
||||
|
||||
docFooter: {
|
||||
prev: 'قبلی',
|
||||
next: 'بعدی'
|
||||
},
|
||||
|
||||
outline: {
|
||||
label: 'در این صفحه'
|
||||
},
|
||||
|
||||
lastUpdated: {
|
||||
text: 'آخرین بهروزرسانی'
|
||||
},
|
||||
|
||||
notFound: {
|
||||
title: 'صفحه پیدا نشد',
|
||||
quote:
|
||||
'اما اگر جهت خود را تغییر ندهید و همچنان به جستجو ادامه دهید، ممکن است در نهایت به جایی برسید که در حال رفتن به آن هستید.',
|
||||
linkLabel: 'برو به خانه',
|
||||
linkText: 'من را به خانه ببر'
|
||||
},
|
||||
|
||||
langMenuLabel: 'تغییر زبان',
|
||||
returnToTopLabel: 'بازگشت به بالا',
|
||||
sidebarMenuLabel: 'منوی جانبی',
|
||||
darkModeSwitchLabel: 'تم تاریک',
|
||||
lightModeSwitchTitle: 'رفتن به حالت روشن',
|
||||
darkModeSwitchTitle: 'رفتن به حالت تاریک',
|
||||
siteTitle: 'ویتپرس'
|
||||
}
|
||||
})
|
||||
|
||||
function nav(): DefaultTheme.NavItem[] {
|
||||
return [
|
||||
{
|
||||
text: 'راهنما',
|
||||
link: 'fa/guide/what-is-vitepress',
|
||||
activeMatch: '/guide/'
|
||||
},
|
||||
{
|
||||
text: 'مرجع',
|
||||
link: 'fa/reference/site-config',
|
||||
activeMatch: '/reference/'
|
||||
},
|
||||
{
|
||||
text: pkg.version,
|
||||
items: [
|
||||
{
|
||||
text: '1.6.4',
|
||||
link: 'https://vuejs.github.io/vitepress/v1/fa/'
|
||||
},
|
||||
{
|
||||
text: 'Changelog',
|
||||
link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md'
|
||||
},
|
||||
{
|
||||
text: 'مشارکت',
|
||||
link: 'https://github.com/vuejs/vitepress/blob/main/.github/contributing.md'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function sidebarGuide(): DefaultTheme.SidebarItem[] {
|
||||
return [
|
||||
{
|
||||
text: 'معرفی',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: 'ویتپرس چیست؟', link: 'what-is-vitepress' },
|
||||
{ text: 'شروع کار', link: 'getting-started' },
|
||||
{ text: 'مسیریابی', link: 'routing' },
|
||||
{ text: 'استقرار', link: 'deploy' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'نوشتن',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: 'افزونههای Markdown', link: 'markdown' },
|
||||
{ text: 'مدیریت منابع', link: 'asset-handling' },
|
||||
{ text: 'Frontmatter', link: 'frontmatter' },
|
||||
{ text: 'استفاده از Vue در Markdown', link: 'using-vue' },
|
||||
{ text: 'بینالمللی سازی', link: 'i18n' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'شخصیسازی',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: 'استفاده از تم شخصی', link: 'custom-theme' },
|
||||
{
|
||||
text: 'گسترش تم پیشفرض',
|
||||
link: 'extending-default-theme'
|
||||
},
|
||||
{ text: 'بارگیری داده در زمان Build', link: 'data-loading' },
|
||||
{ text: 'سازگاری SSR', link: 'ssr-compat' },
|
||||
{ text: 'اتصال به CMS', link: 'cms' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'آزمایشی',
|
||||
collapsed: false,
|
||||
items: [
|
||||
{ text: 'حالت MPA', link: 'mpa-mode' },
|
||||
{ text: 'جنریت کردن Sitemap', link: 'sitemap-generation' }
|
||||
]
|
||||
},
|
||||
{ text: 'پیکربندی و مرجع API', base: 'fa/reference/', link: 'site-config' }
|
||||
]
|
||||
}
|
||||
|
||||
function sidebarReference(): DefaultTheme.SidebarItem[] {
|
||||
return [
|
||||
{
|
||||
text: 'مرجع',
|
||||
base: 'fa/reference/',
|
||||
items: [
|
||||
{ text: 'پیکربندی Site', link: 'site-config' },
|
||||
{ text: 'پیکربندی Frontmatter', link: 'frontmatter-config' },
|
||||
{ text: 'Runtime API', link: 'runtime-api' },
|
||||
{ text: 'CLI', link: 'cli' },
|
||||
{
|
||||
text: 'تم پیشفرض',
|
||||
base: 'fa/reference/default-theme-',
|
||||
items: [
|
||||
{ text: 'بررسی اجمالی', link: 'config' },
|
||||
{ text: 'ناوبری', link: 'nav' },
|
||||
{ text: 'نوار کنار صفحه', link: 'sidebar' },
|
||||
{ text: 'صفحه اصلی', link: 'home-page' },
|
||||
{ text: 'پاورقی', link: 'footer' },
|
||||
{ text: 'طرح', link: 'layout' },
|
||||
{ text: 'نشان', link: 'badge' },
|
||||
{ text: 'صفحه تیم', link: 'team-page' },
|
||||
{ text: 'لینکهای قبلی / بعدی', link: 'prev-next-links' },
|
||||
{ text: 'ویرایش لینک', link: 'edit-link' },
|
||||
{ text: 'Timestamp آخرین بهروزرسانی', link: 'last-updated' },
|
||||
{ text: 'جستجو', link: 'search' },
|
||||
{ text: 'تبلیغات Carbon', link: 'carbon-ads' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function searchOptions(): Partial<DefaultTheme.AlgoliaSearchOptions> {
|
||||
return {
|
||||
placeholder: 'جستجوی مستندات',
|
||||
translations: {
|
||||
button: {
|
||||
buttonText: 'جستجو',
|
||||
buttonAriaLabel: 'جستجو'
|
||||
},
|
||||
modal: {
|
||||
searchBox: {
|
||||
clearButtonTitle: 'پاک کردن جستجو',
|
||||
clearButtonAriaLabel: 'پاک کردن جستجو',
|
||||
closeButtonText: 'بستن',
|
||||
closeButtonAriaLabel: 'بستن',
|
||||
placeholderText: 'جستجوی مستندات',
|
||||
placeholderTextAskAi: 'از هوش مصنوعی بپرسید: ',
|
||||
placeholderTextAskAiStreaming: 'در حال پاسخ...',
|
||||
searchInputLabel: 'جستجو',
|
||||
backToKeywordSearchButtonText: 'بازگشت به جستجوی کلیدواژه',
|
||||
backToKeywordSearchButtonAriaLabel: 'بازگشت به جستجوی کلیدواژه'
|
||||
},
|
||||
startScreen: {
|
||||
recentSearchesTitle: 'جستجوهای اخیر',
|
||||
noRecentSearchesText: 'هیچ جستجوی اخیر',
|
||||
saveRecentSearchButtonTitle: 'ذخیره در تاریخچه جستجو',
|
||||
removeRecentSearchButtonTitle: 'حذف از تاریخچه جستجو',
|
||||
favoriteSearchesTitle: 'علاقهمندیها',
|
||||
removeFavoriteSearchButtonTitle: 'حذف از علاقهمندیها',
|
||||
recentConversationsTitle: 'گفتگوهای اخیر',
|
||||
removeRecentConversationButtonTitle: 'حذف این گفتگو از تاریخچه'
|
||||
},
|
||||
errorScreen: {
|
||||
titleText: 'عدم امکان دریافت نتایج',
|
||||
helpText: 'اتصال شبکه خود را بررسی کنید'
|
||||
},
|
||||
noResultsScreen: {
|
||||
noResultsText: 'هیچ نتیجهای یافت نشد',
|
||||
suggestedQueryText: 'میتوانید جستجوی دیگری امتحان کنید',
|
||||
reportMissingResultsText: 'فکر میکنید باید نتیجهای نمایش داده شود؟',
|
||||
reportMissingResultsLinkText: 'برای ارسال بازخورد کلیک کنید'
|
||||
},
|
||||
resultsScreen: {
|
||||
askAiPlaceholder: 'از هوش مصنوعی بپرسید: '
|
||||
},
|
||||
askAiScreen: {
|
||||
disclaimerText:
|
||||
'پاسخها توسط هوش مصنوعی تولید میشوند و ممکن است خطا داشته باشند. لطفاً بررسی کنید.',
|
||||
relatedSourcesText: 'منابع مرتبط',
|
||||
thinkingText: 'در حال پردازش...',
|
||||
copyButtonText: 'کپی',
|
||||
copyButtonCopiedText: 'کپی شد!',
|
||||
copyButtonTitle: 'کپی',
|
||||
likeButtonTitle: 'پسندیدم',
|
||||
dislikeButtonTitle: 'نپسندیدم',
|
||||
thanksForFeedbackText: 'از بازخورد شما سپاسگزاریم!',
|
||||
preToolCallText: 'در حال جستجو...',
|
||||
duringToolCallText: 'در حال جستجو برای ',
|
||||
afterToolCallText: 'جستجو انجام شد',
|
||||
aggregatedToolCallText: 'جستجو انجام شد'
|
||||
},
|
||||
footer: {
|
||||
selectText: 'انتخاب',
|
||||
submitQuestionText: 'ارسال پرسش',
|
||||
selectKeyAriaLabel: 'کلید Enter',
|
||||
navigateText: 'حرکت',
|
||||
navigateUpKeyAriaLabel: 'کلید جهت بالا',
|
||||
navigateDownKeyAriaLabel: 'کلید جهت پایین',
|
||||
closeText: 'بستن',
|
||||
backToSearchText: 'بازگشت به جستجو',
|
||||
closeKeyAriaLabel: 'کلید Escape',
|
||||
poweredByText: 'جستجو توسط'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# اتصال به یک سیستم مدیریت محتوا (CMS) {#connecting-to-a-cms}
|
||||
|
||||
## گامهای کلی {#general-workflow}
|
||||
|
||||
اتصال ویتپرس به یک سیستم مدیریت محتوا به طور عمده بر اساس [مسیریابی پویا](./routing#dynamic-routes) خواهد بود. حتماً قبل از شروع، با روش کار آن آشنا شوید.
|
||||
|
||||
از آنجایی که هر سیستم مدیریت محتوا به طریقی متفاوت کار میکند، در اینجا تنها میتوانیم یک جریان کاری عمومی را ارائه دهیم که شما باید آن را برای حالت خاص خودتان سفارشی کنید.
|
||||
|
||||
1. اگر سیستم مدیریت محتوا نیاز به احراز هویت دارد، یک فایل `.env` برای ذخیره توکنهای API خود ایجاد کنید و آن را بارگذاری کنید:
|
||||
|
||||
```js
|
||||
// posts/[id].paths.js
|
||||
import { loadEnv } from 'vitepress'
|
||||
|
||||
const env = loadEnv('', process.cwd())
|
||||
```
|
||||
|
||||
2. دادههای مورد نیاز را از سیستم مدیریت محتوا بازیابی کرده و به شکل دادههای مسیر مناسب فرمت کنید:
|
||||
|
||||
```js
|
||||
export default {
|
||||
async paths() {
|
||||
// از کتابخانه مشتری مربوط به سیستم مدیریت محتوا استفاده کنید اگر نیاز دارید
|
||||
const data = await (await fetch('https://my-cms-api', {
|
||||
headers: {
|
||||
// توکن در صورت لزوم
|
||||
}
|
||||
})).json()
|
||||
|
||||
return data.map(entry => {
|
||||
return {
|
||||
params: { id: entry.id, /* عنوان، نویسندگان، تاریخ و غیره */ },
|
||||
content: entry.content
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. نمایش محتوا در صفحه:
|
||||
|
||||
```md
|
||||
# {{ $params.title }}
|
||||
|
||||
- نوشته شده توسط {{ $params.author }} در تاریخ {{ $params.date }}
|
||||
|
||||
<!-- @content -->
|
||||
```
|
||||
|
||||
## راهنماهای ادغام {#integration-guides}
|
||||
|
||||
اگر راهنمایی درباره ادغام ویتپرس با یک سیستم مدیریت محتوا خاص نوشتهاید، لطفاً از لینک "ویرایش این صفحه" زیر استفاده کنید تا آن را ارسال کنید!
|
||||
@ -0,0 +1,220 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# استفاده از یک تم سفارشی {#using-a-custom-theme}
|
||||
|
||||
## Resolve کردن تم {#theme-resolving}
|
||||
|
||||
میتوانید با ایجاد یک فایل `.vitepress/theme/index.js` یا `.vitepress/theme/index.ts` (فایل ورودی تم) تم سفارشی را فعال کنید:
|
||||
|
||||
```
|
||||
.
|
||||
├─ docs # ریشه پروژه
|
||||
│ ├─ .vitepress
|
||||
│ │ ├─ theme
|
||||
│ │ │ └─ index.js # ورودی تم
|
||||
│ │ └─ config.js # فایل پیکربندی
|
||||
│ └─ index.md
|
||||
└─ package.json
|
||||
```
|
||||
|
||||
وقتی ویتپرس حضور یک فایل ورودی تم را شناسایی کند، همواره از تم سفارشی به جای تم پیشفرض استفاده میکند. با این حال، شما میتوانید [تم پیشفرض را گسترش دهید](./extending-default-theme) تا سفارشیسازیهای پیشرفتهتری را روی آن اعمال کنید.
|
||||
|
||||
## رابط تم {#theme-interface}
|
||||
|
||||
یک تم سفارشی ویتپرس به عنوان یک شی تعریف میشود که شامل رابط زیر است:
|
||||
|
||||
```ts
|
||||
interface Theme {
|
||||
/**
|
||||
* کامپوننت لایهی ریشه برای هر صفحه
|
||||
* @required
|
||||
*/
|
||||
Layout: Component
|
||||
/**
|
||||
* تقویت نمونه Vue اپلیکیشن
|
||||
* @optional
|
||||
*/
|
||||
enhanceApp?: (ctx: EnhanceAppContext) => Awaitable<void>
|
||||
/**
|
||||
* گسترش یک تم دیگر، با فراخوانی `enhanceApp` آن پیش از ما
|
||||
* @optional
|
||||
*/
|
||||
extends?: Theme
|
||||
}
|
||||
|
||||
interface EnhanceAppContext {
|
||||
app: App // نمونه Vue اپلیکیشن
|
||||
router: Router // نمونه روتر ویتپرس
|
||||
siteData: Ref<SiteData> // متادیتاهای سطح سایت
|
||||
}
|
||||
```
|
||||
|
||||
فایل ورودی تم باید تم را به عنوان export پیشفرض خود export کند:
|
||||
|
||||
```js [.vitepress/theme/index.js]
|
||||
|
||||
// شما میتوانید فایلهای Vue را مستقیماً در ورودی تم وارد کنید
|
||||
// ویتپرس با @vitejs/plugin-vue پیشتنظیم شده است.
|
||||
import Layout from './Layout.vue'
|
||||
|
||||
export default {
|
||||
Layout,
|
||||
enhanceApp({ app, router, siteData }) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
export پیشفرض تنها قراردادی برای یک تم سفارشی است و تنها ویژگی `Layout` لازم است. بنابراین، به شیء تم ویتپرس میتوان به عنوان یک کامپوننت Vue ساده ترتیب داد.
|
||||
|
||||
درون کامپوننت لایهی خود، دقیقاً مانند یک برنامه Vite + Vue 3 عادی عمل میکند. با این وجود، توجه داشته باشید که تم همچنین باید [سازگار با SSR](./ssr-compat) باشد.
|
||||
|
||||
## ساخت یک لایه {#building-a-layout}
|
||||
|
||||
بیشترین لایهی پایهای نیازمند دارای یک کامپوننت `<Content />` است:
|
||||
|
||||
```vue [.vitepress/theme/Layout.vue]
|
||||
<template>
|
||||
<h1>طرح سفارشی!</h1>
|
||||
|
||||
<!-- اینجا محتوای markdown نمایش داده میشود -->
|
||||
<Content />
|
||||
</template>
|
||||
```
|
||||
|
||||
لایهی بالا به سادگی تمام محتوای markdown هر صفحه را به عنوان HTML نمایش میدهد. اولین بهبودی که میتوانیم اعمال کنیم، مدیریت خطاهای 404 است:
|
||||
|
||||
```vue{1-4,9-12}
|
||||
<script setup>
|
||||
import { useData } from 'vitepress'
|
||||
const { page } = useData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>طرح سفارشی!</h1>
|
||||
|
||||
<div v-if="page.isNotFound">
|
||||
صفحه 404 سفارشی!
|
||||
</div>
|
||||
<Content v-else />
|
||||
</template>
|
||||
```
|
||||
|
||||
کمککننده [`useData()`](../reference/runtime-api#usedata) اطلاعات اجرایی مورد نیاز ما را برای رندر شرایطی صفحات مختلف فراهم میکند. یکی از دیگر اطلاعاتی که ما میتوانیم به آن دسترسی داشته باشیم، اطلاعات اولیه صفحه فعلی است. ما میتوانیم از این اطلاعات برای اجازه دادن به کاربر برای کنترل لایه در هر صفحه استفاده کنیم. به عنوان مثال، کاربر میتواند مشخص کند که صفحه باید از یک طرح صفحه خانه خاص استفاده کند با:
|
||||
|
||||
```md
|
||||
---
|
||||
layout: home
|
||||
---
|
||||
```
|
||||
|
||||
و ما میتوانیم تم خود را تنظیم کنیم تا با این موضوع برخورد کند:
|
||||
|
||||
```vue{3,12-14}
|
||||
<script setup>
|
||||
import { useData } from 'vitepress'
|
||||
const { page, frontmatter } = useData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>طرح سفارشی!</h1>
|
||||
|
||||
<div v-if="page.isNotFound">
|
||||
صفحه 404 سفارشی!
|
||||
</div>
|
||||
<div v-if="frontmatter.layout === 'home'">
|
||||
صفحه خانه سفارشی!
|
||||
</div>
|
||||
<Content v-else />
|
||||
</template>
|
||||
```
|
||||
|
||||
طبیعتا، شما میتوانید لایهی خود را به کامپوننتهای بیشتری تقسیم کنید:
|
||||
|
||||
```vue{3-5,12-15}
|
||||
<script setup>
|
||||
import { useData } from 'vitepress'
|
||||
import NotFound from './NotFound.vue'
|
||||
import Home from './Home.vue'
|
||||
import Page from './Page.vue'
|
||||
|
||||
const { page, frontmatter } = useData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>طرح سفارشی!</h1>
|
||||
|
||||
<NotFound v-if="page.isNotFound" />
|
||||
<Home v-if="frontmatter.layout === 'home'" />
|
||||
<Page v-else /> <!-- <Page /> با `<Content />` را نمایش میدهد -->
|
||||
</template>
|
||||
```
|
||||
|
||||
برای همه چیزی که در کامپوننتهای تم موجود است، به [مستندات API اجرایی](../reference/runtime-api) مراجعه کنید. به علاوه، شما میتوانید از [بارگذاری داده در زمان ساخت](./data-loading) استفاده کنید تا لایههای مبتنی بر داده را تولید کنید - به عنوان مثال، یک صفحه که تمام پستهای وبلاگ در پروژه فعلی را لیست میکند.
|
||||
|
||||
## توزیع یک تم سفارشی {#distributing-a-custom-theme}
|
||||
|
||||
آسانترین روش برای توزیع یک تم سفارشی ارائه آن به عنوان [قالب مخزن در GitHub](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository) است.
|
||||
|
||||
اگر میخواهید تم را به عنوان یک بسته npm توزیع کنید، مراحل زیر را دنبال کنید:
|
||||
|
||||
1. شیء تم را به عنوان export پیشفرض در ورودی بستهتان export کنید.
|
||||
|
||||
2. اگر امکان دارد، تعریف نوع پیکربندی تم خود را به عنوان `ThemeConfig` export کنید.
|
||||
|
||||
3. اگر تم شما نیاز به تنظیم پیکربندی ویتپرس دارد، پیکربندی را تحت یک زیرمسیر بسته (مانند `my-theme/config`) export کنید تا کاربر بتواند آن را گسترش دهد.
|
||||
|
||||
4. گزینههای پیکربندی تم را مستند کنید (هم از طریق فایل پیکربندی و هم از طریق frontmatter).
|
||||
|
||||
5. دستورالعملهای روشنی برای مصرف تم خود ارائه دهید (مانند زیر).
|
||||
|
||||
## مصرف یک تم سفارشی {#consuming-a-custom-theme}
|
||||
|
||||
برای مصرف یک تم خارجی، آن را از ورودی تم سفارشی وارد و دوباره export کنید:
|
||||
|
||||
```js [.vitepress/theme/index.js]
|
||||
import Theme from 'awesome-vitepress-theme'
|
||||
|
||||
export default Theme
|
||||
```
|
||||
|
||||
اگر تم نیاز به گسترش دارد:
|
||||
|
||||
```js [.vitepress/theme/index.js]
|
||||
import Theme from 'awesome-vitepress-theme'
|
||||
|
||||
export default {
|
||||
extends: Theme,
|
||||
enhanceApp(ctx) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
اگر تم نیاز به پیکربندی خاص ویتپرس دارد، شما همچنین باید آن را در پیکربندی خود گسترش دهید:
|
||||
|
||||
```ts [.vitepress/config.ts]
|
||||
import baseConfig from 'awesome-vitepress-theme/config'
|
||||
|
||||
export default {
|
||||
// گسترش پیکربندی پایهی تم (اگر لازم باشد)
|
||||
extends: baseConfig
|
||||
}
|
||||
```
|
||||
|
||||
سرانجام، اگر تم انواع خود را برای پیکربندی تماش ارائه میدهد:
|
||||
|
||||
```ts [.vitepress/config.ts]
|
||||
import baseConfig from 'awesome-vitepress-theme/config'
|
||||
import { defineConfigWithTheme } from 'vitepress'
|
||||
import type { ThemeConfig } from 'awesome-vitepress-theme'
|
||||
|
||||
export default defineConfigWithTheme<ThemeConfig>({
|
||||
extends: baseConfig,
|
||||
themeConfig: {
|
||||
// نوع `ThemeConfig` است
|
||||
}
|
||||
})
|
||||
```
|
||||
@ -0,0 +1,246 @@
|
||||
# بارگذاری داده در زمان ساخت {#build-time-data-loading}
|
||||
|
||||
ویتپرس یک ویژگی به نام **بارگذارهای داده** ارائه میدهد که به شما این امکان را میدهد که دادههای دلخواه را بارگیری کنید و آنها را از صفحات یا اجزا وارد کنید. بارگذاری داده فقط **در زمان ساخت** اجرا میشود: دادههای حاصل به صورت JSON در بسته JavaScript نهایی سریالیزه میشوند.
|
||||
|
||||
بارگذارهای داده میتوانند برای بارگیری دادههای از راه دور یا تولید فرادادهها بر اساس فایلهای محلی استفاده شوند. به عنوان مثال، میتوانید از بارگذارهای داده استفاده کنید تا تمام صفحات API محلی خود را تجزیه کنید و به طور خودکار یک فهرست از تمام ورودیهای API تولید کنید.
|
||||
|
||||
## استفاده ابتدایی {#basic-usage}
|
||||
|
||||
یک فایل بارگذار داده باید با `.data.js` یا `.data.ts` پایان یابد. فایل باید یک صادرات پیشفرض از یک شی با متد `load()` داشته باشد:
|
||||
|
||||
```js [example.data.js]
|
||||
export default {
|
||||
load() {
|
||||
return {
|
||||
hello: 'world'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
ماژول بارگذار فقط در Node.js ارزیابی میشود، بنابراین شما میتوانید API های Node و وابستگیهای npm را به عنوان نیازهای خود وارد کنید.
|
||||
|
||||
سپس میتوانید داده را از این فایل در صفحات `.md` و اجزا `.vue` با استفاده از صادرات نامگذاری شده `data` وارد کنید:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { data } from './example.data.js'
|
||||
</script>
|
||||
|
||||
<pre>{{ data }}</pre>
|
||||
```
|
||||
|
||||
خروجی:
|
||||
|
||||
```json
|
||||
{
|
||||
"hello": "world"
|
||||
}
|
||||
```
|
||||
|
||||
شما متوجه خواهید شد که بارگذار داده خودش داده را صادر نمیکند. ویتپرس پشت صحنه متد `load()` را فراخوانی میکند و به طور ضمنی نتیجه را از طریق صادرات نامگذاری شده `data` ارائه میدهد.
|
||||
|
||||
این کار حتی اگر بارگذار async باشد انجام میشود:
|
||||
|
||||
```js
|
||||
export default {
|
||||
async load() {
|
||||
// دریافت داده از راه دور
|
||||
return (await fetch('...')).json()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## داده از فایلهای محلی {#data-from-local-files}
|
||||
|
||||
وقتی نیاز به تولید داده بر اساس فایلهای محلی دارید، باید از گزینه `watch` در بارگذار داده استفاده کنید تا تغییرات اعمال شده به این فایلها بتواند به روزرسانیهای سریع منجر شود.
|
||||
|
||||
گزینه `watch` همچنین در آنجا مفید است که میتوانید از [الگوهای glob](https://github.com/mrmlnc/fast-glob#pattern-syntax) برای تطابق با چندین فایل استفاده کنید. الگوها میتوانند نسبت به فایل بارگذار خود نسبی باشند و تابع `load()` فایلهای تطابق یافته را به عنوان مسیرهای مطلق دریافت میکند.
|
||||
|
||||
مثال زیر نشان میدهد که چگونه فایلهای CSV را بارگذاری کرده و آنها را با استفاده از [csv-parse](https://github.com/adaltas/node-csv/tree/master/packages/csv-parse/) به JSON تبدیل میکند. این فایل تنها در زمان ساخت اجرا میشود، بنابراین شما نیازی به ارسال پارسر CSV به مشتری ندارید!
|
||||
|
||||
```js
|
||||
import fs from 'node:fs'
|
||||
import { parse } from 'csv-parse/sync'
|
||||
|
||||
export default {
|
||||
watch: ['./data/*.csv'],
|
||||
load(watchedFiles) {
|
||||
// watchedFiles یک آرایه از مسیرهای مطلق فایلهای تطابق یافته خواهد بود.
|
||||
// تولید یک آرایه از فرادادههای پست وبلاگ که میتواند برای نمایش
|
||||
// یک لیست در طرح استفاده شود
|
||||
return watchedFiles.map((file) => {
|
||||
return parse(fs.readFileSync(file, 'utf-8'), {
|
||||
columns: true,
|
||||
skip_empty_lines: true
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `createContentLoader` {#createcontentloader}
|
||||
|
||||
وقتی که در حال ساختن یک سایت متمرکز بر محتوا هستیم، اغلب نیاز به ایجاد یک "بایگانی" یا "فهرست" صفحه داریم: یک صفحه که ما همه ورودیهای موجود در مجموعه محتوای خود را لیست میکنیم، به عنوان مثال پستهای وبلاگ یا صفحات API. ما میتوانیم این کار را مستقیماً با API بارگذار داده انجام دهیم، اما از آنجا که این یک حالت استفاده رایج است، ویتپرس همچنین یک کمککننده به نام `createContentLoader` را فراهم میکند تا این فرآیند را سادهتر کند:
|
||||
|
||||
```js [posts.data.js]
|
||||
import { createContentLoader } from 'vitepress'
|
||||
|
||||
export default createContentLoader('posts/*.md', /* گزینهها */)
|
||||
```
|
||||
|
||||
کمککننده یک الگوی glob را نسبت به [دایرکتوری منبع](./routing#source-directory) مشخص میکند و یک شی `{ watch، load }` را که میتواند به عنوان صادرات پیشفرض در یک فایل بارگذار داده استفاده شود، برمیگرداند. همچنین پیادهسازی حافظه پنهانی بر اساس برچسبهای تغییر مدیریت
|
||||
|
||||
میکند تا عملکرد توسعه را بهبود بخشد.
|
||||
|
||||
لطفاً توجه داشته باشید که بارگذار فقط با فایلهای Markdown کار میکند - فایلهای غیر-Markdown تطابق یافته حذف میشوند.
|
||||
|
||||
داده بارگذاری شده یک آرایه با نوع `ContentData[]` خواهد بود:
|
||||
|
||||
```ts
|
||||
interface ContentData {
|
||||
// آدرس URL برای صفحه. به عنوان مثال /posts/hello.html (شامل پایه نمیشود)
|
||||
// تکرار دستی یا استفاده از `transform` سفارشی برای نرمال کردن مسیرها
|
||||
url: string
|
||||
// اطلاعات frontmatter صفحه
|
||||
frontmatter: Record<string, any>
|
||||
|
||||
// موارد زیر فقط وقتی که گزینههای مربوط فعال باشند
|
||||
// ما در زیر آنها را بررسی میکنیم
|
||||
src: string | undefined
|
||||
html: string | undefined
|
||||
excerpt: string | undefined
|
||||
}
|
||||
```
|
||||
|
||||
به طور پیشفرض، تنها `url` و `frontmatter` ارائه میشوند. این به خاطر این است که داده بارگذاری شده به عنوان JSON در بسته مشتری نهایی درج میشود، بنابراین ما باید در مورد اندازه آن محتاط باشیم. در زیر مثالی از استفاده از داده برای ساخت یک صفحه فهرست کمینه وبلاگ آورده شده است:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { data as posts } from './posts.data.js'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>همه پستهای وبلاگ</h1>
|
||||
<ul>
|
||||
<li v-for="post of posts">
|
||||
<a :href="post.url">{{ post.frontmatter.title }}</a>
|
||||
<span>توسط {{ post.frontmatter.author }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
```
|
||||
|
||||
### گزینهها {#options}
|
||||
|
||||
احتمالاً داده پیشفرض به تمام نیازها پاسخ نمیدهد - شما میتوانید با استفاده از گزینهها به تبدیل دادهها مشترک شوید:
|
||||
|
||||
```js [posts.data.js]
|
||||
import { createContentLoader } from 'vitepress'
|
||||
|
||||
export default createContentLoader('posts/*.md', {
|
||||
includeSrc: true, // آیا منبع اصلی مارکداون را اضافه کنیم؟
|
||||
render: true, // آیا صفحه HTML را نیز شامل کنیم؟
|
||||
excerpt: true, // آیا خلاصه را نیز شامل کنیم؟
|
||||
transform(rawData) {
|
||||
// نقشهبرداری، مرتبسازی یا فیلتر کردن دادههای اصلی به دلخواه.
|
||||
// نتیجه نهایی آنچه است که به مشتری ارسال خواهد شد.
|
||||
return rawData.sort((a, b) => {
|
||||
return +new Date(b.frontmatter.date) - +new Date(a.frontmatter.date)
|
||||
}).map((page) => {
|
||||
page.src // منبع اصلی مارکداون
|
||||
page.html // صفحه HTML کامل
|
||||
page.excerpt // خلاصه HTML (محتوای بالای اولین `---`)
|
||||
return {/* ... */}
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
بررسی کنید که چگونه در [وبلاگ Vue.js](https://github.com/vuejs/blog/blob/main/.vitepress/theme/posts.data.ts) استفاده شده است.
|
||||
|
||||
API `createContentLoader` همچنین میتواند در داخل [هوکهای ساخت](../reference/site-config#build-hooks) استفاده شود:
|
||||
|
||||
```js [.vitepress/config.js]
|
||||
export default {
|
||||
async buildEnd() {
|
||||
const posts = await createContentLoader('posts/*.md').load()
|
||||
// تولید فایلهای بر اساس فرادادههای پستها، مثلاً فید RSS
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**انواع**
|
||||
|
||||
```ts
|
||||
interface ContentOptions<T = ContentData[]> {
|
||||
/**
|
||||
* آیا منبع اصلی را اضافه کنیم؟
|
||||
* @default false
|
||||
*/
|
||||
includeSrc?: boolean
|
||||
|
||||
/**
|
||||
* آیا منبع را به HTML تبدیل کرده و در داده شامل کنیم؟
|
||||
* @default false
|
||||
*/
|
||||
render?: boolean
|
||||
|
||||
/**
|
||||
* اگر `boolean` باشد، آیا باید خلاصه را تجزیه و شامل کنیم؟ (به صورت HTML)
|
||||
*
|
||||
* اگر `function` باشد، کنترل نحوه استخراج خلاصه از محتوا.
|
||||
*
|
||||
* اگر `string` باشد، تعیین کنید که چگونه جداکننده سفارشی باید برای استخراج خلاصه استفاده شود.
|
||||
* جداکننده پیشفرض `---` است اگر `excerpt` `true` باشد.
|
||||
*
|
||||
* @see https://github.com/jonschlinkert/gray-matter#optionsexcerpt
|
||||
* @see https://github.com/jonschlinkert/gray-matter#optionsexcerpt_separator
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
excerpt?:
|
||||
| boolean
|
||||
| ((file: { data: { [key: string]: any }; content: string; excerpt?: string }, options?: any) => void)
|
||||
| string
|
||||
|
||||
/**
|
||||
* تبدیل داده. توجه داشته باشید که داده به عنوان JSON در بسته مشتری درج خواهد شد
|
||||
* اگر از اجزا یا فایلهای مارکداون وارد شود.
|
||||
*/
|
||||
transform?: (data: ContentData[]) => T | Promise<T>
|
||||
}
|
||||
```
|
||||
|
||||
## بارگذارهای داده تایپ شده {#typed-data-loaders}
|
||||
|
||||
زمان استفاده از TypeScript، میتوانید بارگذار و صادرات `data` خود را به این شکل تایپ کنید:
|
||||
|
||||
```ts
|
||||
import { defineLoader } from 'vitepress'
|
||||
|
||||
export interface Data {
|
||||
// نوع داده
|
||||
}
|
||||
|
||||
declare const data: Data
|
||||
export { data }
|
||||
|
||||
export default defineLoader({
|
||||
// گزینههای بارگذاری با تایپ چک شده
|
||||
watch: ['...'],
|
||||
async load(): Promise<Data> {
|
||||
// ...
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## پیکربندی {#configuration}
|
||||
|
||||
برای دریافت اطلاعات پیکربندی در داخل یک بارگذار، میتوانید از کدی مانند زیر استفاده کنید:
|
||||
|
||||
```ts
|
||||
import type { SiteConfig } from 'vitepress'
|
||||
|
||||
const config: SiteConfig = (globalThis as any).VITEPRESS_CONFIG
|
||||
```
|
||||
@ -0,0 +1,48 @@
|
||||
# Frontmatter
|
||||
|
||||
## استفاده {#usage}
|
||||
|
||||
ویتپرس پشتیبانی از frontmatter YAML در تمام فایلهای Markdown را دارد و آنها را با استفاده از [gray-matter](https://github.com/jonschlinkert/gray-matter) تجزیه میکند. Frontmatter باید در بالای فایل Markdown قرار داشته باشد (قبل از هر عنصر از جمله برچسبهای `<script>`) و باید به صورت YAML معتبر واقع در بین خطوط خط کشیده شود. به عنوان مثال:
|
||||
|
||||
```md
|
||||
---
|
||||
title: مستندات با ویتپرس
|
||||
editLink: true
|
||||
---
|
||||
```
|
||||
|
||||
بسیاری از گزینههای پیکربندی سایت یا پیشفرض در تمام frontmatter گزینههای متناظر دارند. شما میتوانید از frontmatter برای لغو عملکرد خاص برای صفحه فعلی استفاده کنید. برای جزئیات بیشتر، به [مرجع پیکربندی Frontmatter](../reference/frontmatter-config) مراجعه کنید.
|
||||
|
||||
همچنین میتوانید دادههای اختصاصی frontmatter خود را تعریف کنید تا در بیانیههای پویا Vue در صفحه استفاده شود.
|
||||
|
||||
## دسترسی به دادههای Frontmatter {#accessing-frontmatter-data}
|
||||
|
||||
دادههای frontmatter میتوانند از طریق متغیر global ویژه `$frontmatter` دسترسی داشته باشند:
|
||||
|
||||
اینجا یک مثال از نحوه استفاده از آن در فایل Markdown شما است:
|
||||
|
||||
```md
|
||||
---
|
||||
title: مستندات با ویتپرس
|
||||
editLink: true
|
||||
---
|
||||
|
||||
# {{ $frontmatter.title }}
|
||||
|
||||
محتوای راهنما
|
||||
```
|
||||
|
||||
شما همچنین میتوانید دادههای frontmatter صفحه فعلی را در `<script setup>` با استفاده از راهنمای [`useData()`](../reference/runtime-api#usedata) به دست آورید.
|
||||
|
||||
## فرمتهای جایگزین Frontmatter {#alternative-frontmatter-formats}
|
||||
|
||||
ویتپرس همچنین از نحوه نوشتاری frontmatter JSON با استفاده از تکیهگاههای آغازین و پایانی در آکولاد پشتیبانی میکند:
|
||||
|
||||
```json
|
||||
---
|
||||
{
|
||||
"title": "عنوان",
|
||||
"editLink": true
|
||||
}
|
||||
---
|
||||
```
|
||||
@ -0,0 +1,111 @@
|
||||
# بینالمللیسازی {#internationalization}
|
||||
|
||||
برای استفاده از ویژگیهای داخلی بینالمللیسازی، نیاز است که یک ساختار دایرکتوری به شکل زیر ایجاد کنید:
|
||||
|
||||
```
|
||||
docs/
|
||||
├─ es/
|
||||
│ ├─ foo.md
|
||||
├─ fr/
|
||||
│ ├─ foo.md
|
||||
├─ foo.md
|
||||
```
|
||||
|
||||
سپس در `docs/.vitepress/config.ts` به شکل زیر عمل کنید:
|
||||
|
||||
```ts [docs/.vitepress/config.ts]
|
||||
import { defineConfig } from 'vitepress'
|
||||
|
||||
export default defineConfig({
|
||||
// ویژگیهای مشترک و دیگر موارد در سطح بالا...
|
||||
|
||||
locales: {
|
||||
root: {
|
||||
label: 'انگلیسی',
|
||||
lang: 'en'
|
||||
},
|
||||
fr: {
|
||||
label: 'فرانسوی',
|
||||
lang: 'fr', // اختیاری، به عنوان `lang` در `html` tag اضافه خواهد شد
|
||||
link: '/fr/guide' // پیشفرض /fr/ -- در منوی ترجمهها نمایش داده میشود، میتواند خارجی باشد
|
||||
|
||||
// سایر ویژگیهای مختص به هر زبان...
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
میتوانید خصوصیات زیر را برای هر زبان (شامل ریشه) نیز تغییر دهید:
|
||||
|
||||
```ts
|
||||
interface LocaleSpecificConfig<ThemeConfig = any> {
|
||||
lang?: string
|
||||
dir?: string
|
||||
title?: string
|
||||
titleTemplate?: string | boolean
|
||||
description?: string
|
||||
head?: HeadConfig[] // با ورودیهای head موجود ادغام خواهد شد، meta tags تکراری به طور خودکار حذف میشوند
|
||||
themeConfig?: ThemeConfig // ادغام سطح بالا، میتوان اطلاعات مشترک را در ورودی themeConfig اضافه کرد
|
||||
}
|
||||
```
|
||||
|
||||
برای جزئیات درباره تنظیمات پیشفرض، به رابط `DefaultTheme.Config` در [اینجا](https://github.com/vuejs/vitepress/blob/main/types/default-theme.d.ts) مراجعه کنید. لطفاً خصوصیات `themeConfig.algolia` یا `themeConfig.carbonAds` را در سطح زبان تغییر ندهید. برای استفاده از جستجوی چندزبانه، به [مستندات Algolia](../reference/default-theme-search#i18n) مراجعه کنید.
|
||||
|
||||
**نکته حرفهای:** فایل پیکربندی را میتوانید در `docs/.vitepress/config/index.ts` نیز ذخیره کنید. این کار به شما کمک میکند که با ایجاد یک فایل پیکربندی برای هر زبان و سپس ادغام و صدور آنها از `index.ts`، موارد را سازماندهی کنید.
|
||||
|
||||
## دایرکتوری جداگانه برای هر زبان {#separate-directory-for-each-locale}
|
||||
|
||||
ساختار زیر به طور کاملاً صحیح است:
|
||||
|
||||
```
|
||||
docs/
|
||||
├─ en/
|
||||
│ ├─ foo.md
|
||||
├─ es/
|
||||
│ ├─ foo.md
|
||||
├─ fr/
|
||||
├─ foo.md
|
||||
```
|
||||
|
||||
با این حال، ویتپرس به طور پیشفرض به `/` به `/en/` هدایت نمیکند. برای این کار باید سرور خود را پیکربندی کنید. به عنوان مثال، در Netlify، میتوانید فایل `docs/public/_redirects` را به این شکل اضافه کنید:
|
||||
|
||||
```
|
||||
/* /es/:splat 302 Language=es
|
||||
/* /fr/:splat 302 Language=fr
|
||||
/* /en/:splat 302
|
||||
```
|
||||
|
||||
**نکته حرفهای:** در صورت استفاده از روش فوق، میتوانید از کوکی `nf_lang` برای ثبت انتخاب زبان کاربر استفاده کنید:
|
||||
|
||||
```ts [docs/.vitepress/theme/index.ts]
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import Layout from './Layout.vue'
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
Layout
|
||||
}
|
||||
```
|
||||
|
||||
```vue [docs/.vitepress/theme/Layout.vue]
|
||||
<script setup lang="ts">
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import { useData } from 'vitepress'
|
||||
import { watchEffect } from 'vue'
|
||||
|
||||
const { lang } = useData()
|
||||
watchEffect(() => {
|
||||
if (inBrowser) {
|
||||
document.cookie = `nf_lang=${lang.value}; expires=Mon, 1 Jan 2030 00:00:00 UTC; path=/`
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DefaultTheme.Layout />
|
||||
</template>
|
||||
```
|
||||
|
||||
## پشتیبانی از RTL (آزمایشی) {#rtl-support-experimental}
|
||||
|
||||
برای پشتیبانی از RTL، `dir: 'rtl'` را در پیکربندی مشخص کنید و از پلاگینهای PostCSS RTLCSS مانند <https://github.com/MohammadYounes/rtlcss>، <https://github.com/vkalinichev/postcss-rtl> یا <https://github.com/elchininet/postcss-rtlcss> استفاده کنید. باید پلاگین PostCSS خود را به کارگیری `:where([dir="ltr"])` و `:where([dir="rtl"])` به عنوان پیشوندها جلوگیری از مشکلات اولویت CSS استفاده کنید.
|
||||
@ -0,0 +1,23 @@
|
||||
# مهاجرت از ویتپرس 0.x
|
||||
|
||||
اگر از نسخه 0.x ویتپرس میآیید، تغییرات قابل توجهی به دلیل ویژگیها و بهبودهای جدید وجود دارد. لطفاً این راهنما را دنبال کنید تا ببینید چگونه برنامه خود را به ویتپرس جدیدتر منتقل کنید.
|
||||
|
||||
## پیکربندی برنامه
|
||||
|
||||
- ویژگی بینالمللیسازی هنوز اجرا نشده است.
|
||||
|
||||
## پیکربندی تم
|
||||
|
||||
- گزینه `sidebar` ساختار خود را تغییر داده است.
|
||||
- کلید `children` حالا به نام `items` نامیده میشود.
|
||||
- در حال حاضر ممکن است مورد بالادستی حاوی `link` نباشد. ما قصد داریم این گزینه را بازگردانیم.
|
||||
- `repo`، `repoLabel`، `docsDir`، `docsBranch`، `editLinks`، `editLinkText` به منظور API انعطافپذیرتر حذف شدهاند.
|
||||
- برای اضافه کردن لینک GitHub با آیکون به نوار ناوبری، از ویژگی [پیوندهای اجتماعی](../reference/default-theme-nav#navigation-links) استفاده کنید.
|
||||
- برای اضافه کردن ویژگی "ویرایش این صفحه"، از ویژگی [پیوند ویرایش](../reference/default-theme-edit-link) استفاده کنید.
|
||||
- گزینه `lastUpdated` حالا به `config.lastUpdated` و `themeConfig.lastUpdatedText` تقسیم شده است.
|
||||
- `carbonAds.carbon` به `carbonAds.code` تغییر کرده است.
|
||||
|
||||
## پیکربندی Frontmatter
|
||||
|
||||
- گزینه `home: true` به `layout: home` تغییر کرده است. همچنین، تنظیمات مربوط به صفحه اصلی بسیار تغییر کردهاند تا ویژگیهای اضافی را ارائه دهند. برای جزئیات بیشتر، [راهنمای صفحه اصلی](../reference/default-theme-home-page) را ببینید.
|
||||
- گزینه `footer` به [`themeConfig.footer`](../reference/default-theme-config#footer) منتقل شده است.
|
||||
@ -0,0 +1,30 @@
|
||||
# مهاجرت از VuePress
|
||||
|
||||
## پیکربندی
|
||||
|
||||
### نوار کناری
|
||||
|
||||
نوار کناری دیگر به طور خودکار از frontmatter پر نمیشود. شما میتوانید [frontmatter را خودتان بخوانید](https://github.com/vuejs/vitepress/issues/572#issuecomment-1170116225) تا نوار کناری به طور پویا پر شود. ابزارهای [اضافی برای این منظور](https://github.com/vuejs/vitepress/issues/96) ممکن است در آینده ارائه شود.
|
||||
|
||||
## Markdown
|
||||
|
||||
### تصاویر
|
||||
|
||||
برخلاف VuePress، ویتپرس وقتی شما از تصویر استاتیک استفاده میکنید، [`base`](./asset-handling#base-url) پیکربندی شما را به طور خودکار مدیریت میکند.
|
||||
|
||||
بنابراین، اکنون میتوانید تصاویر را بدون استفاده از تگ `img` نمایش دهید.
|
||||
|
||||
```diff
|
||||
- <img :src="$withBase('/foo.png')" alt="foo">
|
||||
+ 
|
||||
```
|
||||
|
||||
::: warning
|
||||
برای تصاویر پویا، همچنان نیاز به استفاده از `withBase` به طوری که در [راهنمای Base URL](./asset-handling#base-url) نشان داده شده است، دارید.
|
||||
:::
|
||||
|
||||
از عبارت `!<img.*withBase\('(.*)'\).*alt="([^"]*)".*>` برای جستجو و جایگزینی با `` استفاده کنید تا تمام تصاویر را با سینتکس `` جایگزین کنید.
|
||||
|
||||
---
|
||||
|
||||
ادامه دارد...
|
||||
@ -0,0 +1,23 @@
|
||||
# حالت MPA <Badge type="warning" text="آزمایشی" /> {#mpa-mode}
|
||||
|
||||
حالت MPA (برنامه چند صفحه) میتواند از طریق خط فرمان با `vitepress build --mpa` فعال شود، یا از طریق تنظیمات با گزینه `mpa: true`.
|
||||
|
||||
در حالت MPA، همه صفحات به طور پیشفرض بدون هیچ جاوااسکریپتی رندر میشوند. به همین دلیل، سایت تولیدی احتمالاً امتیاز بهتری از ابزارهای آزمایشی در اولین بازدید دریافت خواهد کرد.
|
||||
|
||||
با این حال، به دلیل عدم وجود مسیریابی SPA، لینکهای متقاطع به بازنشانی کامل صفحه منتهی میشوند. ناوبری پس از بارگیری در حالت MPA حساسیت به همان اندازه با حالت SPA نخواهد داشت.
|
||||
|
||||
همچنین توجه داشته باشید که عدم وجود JS به طور پیشفرض به این معنی است که شما اساساً Vue را به عنوان یک زبان قالببندی سمت سرور استفاده میکنید. هیچ کنترل کننده رویدادی در مرورگر اضافه نمیشود، بنابراین هیچ تعاملی وجود نخواهد داشت. برای بارگیری JS سمت کلاینت، شما باید از تگ خاص `<script client>` استفاده کنید:
|
||||
|
||||
```html
|
||||
<script client>
|
||||
document.querySelector('h1').addEventListener('click', () => {
|
||||
console.log('JavaScript سمت کلاینت!')
|
||||
})
|
||||
</script>
|
||||
|
||||
# سلام
|
||||
```
|
||||
|
||||
`<script client>` یک ویژگی تنها برای ویتپرس است، نه یک ویژگی Vue. این در هر دو فایل `.md` و `.vue` کار میکند، اما فقط در حالت MPA. اسکریپتهای کلاینت در تمام اجزای تم با هم بسته میشوند، در حالی که اسکریپت کلاینت برای یک صفحه خاص، فقط برای آن صفحه تقسیم میشود.
|
||||
|
||||
توجه داشته باشید که `<script client>` به عنوان **کد مؤلفه مؤلفه Vue** ارزیابی نمیشود: به عنوان یک ماژول جاوااسکریپت معمولی پردازش میشود. به همین دلیل، حالت MPA فقط باید در صورتی استفاده شود که سایت شما به تعامل کمینهای از جانب کلاینت نیاز دارد.
|
||||
@ -0,0 +1,58 @@
|
||||
# جنریت کردن Sitemap {#sitemap-generation}
|
||||
|
||||
ویتپرس با پشتیبانی بیرونی برای تولید فایل `sitemap.xml` برای سایت شما ارائه میشود. برای فعالسازی آن، موارد زیر را به فایل `.vitepress/config.js` خود اضافه کنید:
|
||||
|
||||
```ts
|
||||
export default {
|
||||
sitemap: {
|
||||
hostname: 'https://example.com'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
برای داشتن تگهای `<lastmod>` در فایل `sitemap.xml` خود، میتوانید گزینه [`lastUpdated`](../reference/default-theme-last-updated) را فعال کنید.
|
||||
|
||||
## گزینهها {#options}
|
||||
|
||||
پشتیبانی از sitemap توسط ماژول [`sitemap`](https://www.npmjs.com/package/sitemap) ارائه شده است. میتوانید هر گزینهای که توسط این ماژول پشتیبانی میشود را به گزینه `sitemap` در فایل پیکربندی خود منتقل کنید. این گزینهها به طور مستقیم به سازنده `SitemapStream` منتقل میشوند. برای جزئیات بیشتر به [مستندات sitemap](https://www.npmjs.com/package/sitemap#options-you-can-pass) مراجعه کنید. مثال:
|
||||
|
||||
```ts
|
||||
export default {
|
||||
sitemap: {
|
||||
hostname: 'https://example.com',
|
||||
lastmodDateOnly: false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
اگر از `base` در پیکربندی خود استفاده میکنید، باید آن را به گزینه `hostname` اضافه کنید:
|
||||
|
||||
```ts
|
||||
export default {
|
||||
base: '/my-site/',
|
||||
sitemap: {
|
||||
hostname: 'https://example.com/my-site/'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## هوک `transformItems` {#transformitems-hook}
|
||||
|
||||
میتوانید از هوک `sitemap.transformItems` برای اصلاح موارد sitemap قبل از نوشتن آنها به فایل `sitemap.xml` استفاده کنید. این هوک با یک آرایه از موارد sitemap فراخوانی میشود و انتظار دارد که یک آرایه از موارد sitemap بازگردانده شود. مثال:
|
||||
|
||||
```ts
|
||||
export default {
|
||||
sitemap: {
|
||||
hostname: 'https://example.com',
|
||||
transformItems: (items) => {
|
||||
// اضافه کردن موارد جدید یا اصلاح/فیلتر کردن موارد موجود
|
||||
items.push({
|
||||
url: '/extra-page',
|
||||
changefreq: 'monthly',
|
||||
priority: 0.8
|
||||
})
|
||||
return items
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -0,0 +1,35 @@
|
||||
---
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: ویتپرس
|
||||
text: سازنده سایتهای ایستا به کمک Vite و Vue
|
||||
tagline: تبدیل Markdown به مستندات زیبا در چند دقیقه
|
||||
actions:
|
||||
- theme: brand
|
||||
text: ویتپرس چیست؟
|
||||
link: fa/guide/what-is-vitepress
|
||||
- theme: alt
|
||||
text: شروع سریع
|
||||
link: fa/guide/getting-started
|
||||
- theme: alt
|
||||
text: گیتهاب
|
||||
link: https://github.com/vuejs/vitepress
|
||||
image:
|
||||
src: /vitepress-logo-large.svg
|
||||
alt: ویتپرس
|
||||
|
||||
features:
|
||||
- icon: 📝
|
||||
title: تمرکز روی محتوا
|
||||
details: ایجاد سایتهای مستندسازی زیبا بدون زحمت و فقط با Markdown
|
||||
- icon: <svg xmlns="http://www.w3.org/2000/svg" width="30" viewBox="0 0 256 256.32"><defs><linearGradient id="a" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"/><stop offset="100%" stop-color="#BD34FE"/></linearGradient><linearGradient id="b" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"/><stop offset="8.333%" stop-color="#FFDD35"/><stop offset="100%" stop-color="#FFA800"/></linearGradient></defs><path fill="url(#a)" d="M255.153 37.938 134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"/><path fill="url(#b)" d="M185.432.063 96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028 72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"/></svg>
|
||||
title: لذت از تجربه توسعه با Vite
|
||||
details: شروع فوری سرور، بهروزرسانیهای سریع و استفاده از افزونههای اکوسیستم Vite
|
||||
- icon: <svg xmlns="http://www.w3.org/2000/svg" width="30" viewBox="0 0 256 220.8"><path fill="#41B883" d="M204.8 0H256L128 220.8 0 0h97.92L128 51.2 157.44 0h47.36Z"/><path fill="#41B883" d="m0 0 128 220.8L256 0h-51.2L128 132.48 50.56 0H0Z"/><path fill="#35495E" d="M50.56 0 128 133.12 204.8 0h-47.36L128 51.2 97.92 0H50.56Z"/></svg>
|
||||
title: شخصیسازی با Vue
|
||||
details: استفاده مستقیم از syntax و کامپوننتهای Vue در Markdown، یا ایجاد تمهای شخصی به کمک Vue
|
||||
- icon: 🚀
|
||||
title: ارسال سایت های سریع
|
||||
details: بارگذاری اولیه سریع با HTML ایستا، ناوبری سریع پس از بارگیری با مسیریابی سمت کلاینت
|
||||
---
|
||||
@ -0,0 +1,73 @@
|
||||
# رابط خط فرمان {#command-line-interface}
|
||||
|
||||
## `vitepress dev` {#vitepress-dev}
|
||||
|
||||
شروع سرور توسعه ویتپرس با استفاده از دایرکتوری مشخص به عنوان ریشه. به طور پیشفرض از دایرکتوری فعلی استفاده میشود. دستور `dev` همچنین میتواند حذف شود زمانی که در دایرکتوری فعلی اجرا میشود.
|
||||
|
||||
### استفاده {#usage}
|
||||
|
||||
```sh
|
||||
# شروع در دایرکتوری فعلی، بدون `dev`
|
||||
vitepress
|
||||
|
||||
# شروع در زیردایرکتوری
|
||||
vitepress dev [root]
|
||||
```
|
||||
|
||||
### گزینهها {#options}
|
||||
|
||||
| گزینه | توضیحات |
|
||||
| --------------- | ----------------------------------------------------------------- |
|
||||
| `--open [path]` | باز کردن مرورگر در زمان راهاندازی (`boolean \| string`) |
|
||||
| `--port <port>` | تعیین پورت (`number`) |
|
||||
| `--base <path>` | مسیر پایه عمومی (پیشفرض: `/`) (`string`) |
|
||||
| `--cors` | فعالسازی CORS |
|
||||
| `--strictPort` | خروج در صورت استفاده از پورت مشخص شده (`boolean`) |
|
||||
| `--force` | اجبار به نادیده گرفتن حافظه پنهان و بازسازی (`boolean`) |
|
||||
|
||||
## `vitepress build` {#vitepress-build}
|
||||
|
||||
ساخت سایت ویتپرس برای تولید نهایی.
|
||||
|
||||
### استفاده {#usage-1}
|
||||
|
||||
```sh
|
||||
vitepress build [root]
|
||||
```
|
||||
|
||||
### گزینهها {#options-1}
|
||||
|
||||
| گزینه | توضیحات |
|
||||
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| `--mpa` (آزمایشی) | ساخت در حالت [MPA](../guide/mpa-mode) بدون هیدراسیون سمت مشتری (`boolean`) |
|
||||
| `--base <path>` | مسیر پایه عمومی (پیشفرض: `/`) (`string`) |
|
||||
| `--target <target>` | هدف ترنسپایل (پیشفرض: `"modules"`) (`string`) |
|
||||
| `--outDir <dir>` | دایرکتوری خروجی نسبت به **cwd** (پیشفرض: `<root>/.vitepress/dist`) (`string`) |
|
||||
| `--assetsInlineLimit <number>` | آستانه تبدیل پایه ۶۴ استاتیک به بایت (پیشفرض: `4096`) (`number`) |
|
||||
|
||||
## `vitepress preview` {#vitepress-preview}
|
||||
|
||||
پیشنمایش محلی برای ساخت تولیدی را نمایش دهید.
|
||||
|
||||
### استفاده {#usage-2}
|
||||
|
||||
```sh
|
||||
vitepress preview [root]
|
||||
```
|
||||
|
||||
### گزینهها {#options-2}
|
||||
|
||||
| گزینه | توضیحات |
|
||||
| --------------- | ---------------------------------------- |
|
||||
| `--base <path>` | مسیر پایه عمومی (پیشفرض: `/`) (`string`) |
|
||||
| `--port <port>` | تعیین پورت (`number`) |
|
||||
|
||||
## `vitepress init` {#vitepress-init}
|
||||
|
||||
شروع [جادوگر راهاندازی](../guide/getting-started#setup-wizard) در دایرکتوری فعلی.
|
||||
|
||||
### استفاده {#usage-3}
|
||||
|
||||
```sh
|
||||
vitepress init
|
||||
```
|
||||
@ -0,0 +1,72 @@
|
||||
# نشان {#badge}
|
||||
|
||||
برچسب به شما امکان میدهد وضعیتهای مختلفی را به سربرگهای خود اضافه کنید. به عنوان مثال، میتواند مفید باشد تا نوع بخش را مشخص کنید یا نسخههای پشتیبانی شده را نشان دهید.
|
||||
|
||||
## استفاده {#usage}
|
||||
|
||||
شما میتوانید از کامپوننت `Badge` که به صورت جهانی در دسترس است، استفاده کنید.
|
||||
|
||||
```html
|
||||
### عنوان <Badge type="info" text="پیشفرض" />
|
||||
### عنوان <Badge type="tip" text="^1.9.0" />
|
||||
### عنوان <Badge type="warning" text="بتا" />
|
||||
### عنوان <Badge type="danger" text="هشدار" />
|
||||
```
|
||||
|
||||
کد بالا به صورت زیر نمایش داده میشود:
|
||||
|
||||
### عنوان <Badge type="info" text="پیشفرض" /> {#title}
|
||||
|
||||
### عنوان <Badge type="tip" text="^1.9.0" /> {#title-1}
|
||||
|
||||
### عنوان <Badge type="warning" text="بتا" /> {#title-2}
|
||||
|
||||
### عنوان <Badge type="danger" text="هشدار" /> {#title-3}
|
||||
|
||||
## ارائه دادن محتوای دلخواه {#custom-children}
|
||||
|
||||
`<Badge>` میپذیرد `children` که در برچسب نمایش داده خواهد شد.
|
||||
|
||||
```html
|
||||
### عنوان <Badge type="info">عنصر سفارشی</Badge>
|
||||
```
|
||||
|
||||
### عنوان <Badge type="info">عنصر سفارشی</Badge>
|
||||
|
||||
## سفارشیسازی رنگ نوع {#customize-type-color}
|
||||
|
||||
شما میتوانید استایل برچسبها را با دوبارهنویسی متغیرهای css سفارشی کنید. مقادیر پیشفرض به شرح زیر هستند:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--vp-badge-info-border: transparent;
|
||||
--vp-badge-info-text: var(--vp-c-text-2);
|
||||
--vp-badge-info-bg: var(--vp-c-default-soft);
|
||||
|
||||
--vp-badge-tip-border: transparent;
|
||||
--vp-badge-tip-text: var(--vp-c-brand-1);
|
||||
--vp-badge-tip-bg: var(--vp-c-brand-soft);
|
||||
|
||||
--vp-badge-warning-border: transparent;
|
||||
--vp-badge-warning-text: var(--vp-c-warning-1);
|
||||
--vp-badge-warning-bg: var(--vp-c-warning-soft);
|
||||
|
||||
--vp-badge-danger-border: transparent;
|
||||
--vp-badge-danger-text: var(--vp-c-danger-1);
|
||||
--vp-badge-danger-bg: var(--vp-c-danger-soft);
|
||||
}
|
||||
```
|
||||
|
||||
## `<Badge>` {#badge-1}
|
||||
|
||||
کامپوننت `<Badge>` پراپهای زیر را میپذیرد:
|
||||
|
||||
```ts
|
||||
interface Props {
|
||||
// وقتی `<slot>` ارسال میشود، این مقدار نادیده گرفته میشود.
|
||||
text?: string
|
||||
|
||||
// پیشفرض به `tip`.
|
||||
type?: 'info' | 'tip' | 'warning' | 'danger'
|
||||
}
|
||||
```
|
||||
@ -0,0 +1,22 @@
|
||||
# تبلیغات Carbon {#carbon-ads}
|
||||
|
||||
ویتپرس پشتیبانی داخلی برای [Carbon Ads](https://www.carbonads.net/) را دارد. با تعریف مشخصات تبلیغات Carbon در تنظیمات، ویتپرس تبلیغات را در صفحه نمایش میدهد.
|
||||
|
||||
```js
|
||||
export default {
|
||||
themeConfig: {
|
||||
carbonAds: {
|
||||
code: 'your-carbon-code',
|
||||
placement: 'your-carbon-placement'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
این مقادیر برای فراخوانی اسکریپت CDN Carbon به شکل زیر استفاده میشوند.
|
||||
|
||||
```js
|
||||
`//cdn.carbonads.com/carbon.js?serve=${code}&placement=${placement}`
|
||||
```
|
||||
|
||||
برای یادگیری بیشتر درباره پیکربندی تبلیغات Carbon، لطفاً به [وبسایت Carbon Ads](https://www.carbonads.net/) مراجعه کنید.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue