chore: support eslint flat config
This commit is contained in:
parent
6e6c214e59
commit
db5220a015
@ -1,5 +1,5 @@
|
|||||||
const fs = require('fs')
|
const fs = require('node:fs')
|
||||||
const path = require('path')
|
const path = require('node:path')
|
||||||
|
|
||||||
const packages = fs.readdirSync(path.resolve(__dirname, 'plugins'))
|
const packages = fs.readdirSync(path.resolve(__dirname, 'plugins'))
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
.temp/
|
|
||||||
.cache/
|
|
||||||
lib/
|
|
||||||
dist/
|
|
||||||
!.vuepress/
|
|
||||||
!.*.js
|
|
||||||
scripts/
|
|
||||||
LICENSE
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
extends: 'vuepress',
|
|
||||||
globals: {
|
|
||||||
__VUEPRESS_VERSION__: 'readonly',
|
|
||||||
__VUEPRESS_BASE__: 'readonly',
|
|
||||||
__VUEPRESS_DEV__: 'readonly',
|
|
||||||
__VUEPRESS_SSR__: 'readonly',
|
|
||||||
__VUE_HMR_RUNTIME__: 'readonly',
|
|
||||||
__VUE_OPTIONS_API__: 'readonly',
|
|
||||||
__VUE_PROD_DEVTOOLS__: 'readonly',
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ['*.ts', '*.vue', '*.cts'],
|
|
||||||
extends: 'vuepress-typescript',
|
|
||||||
parserOptions: {
|
|
||||||
project: ['tsconfig.json'],
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
||||||
'vue/multi-word-component-names': 'off',
|
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
||||||
'vue/component-tags-order': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
order: ['script', 'template', 'style'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['**/client/config.ts'],
|
|
||||||
rules: {
|
|
||||||
'vue/match-component-file-name': 'off',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['**/__tests__/**/*.ts'],
|
|
||||||
env: {
|
|
||||||
jest: true,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
||||||
'vue/one-component-per-file': 'off',
|
|
||||||
'import/no-extraneous-dependencies': 'off',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['docs/**/*.ts'],
|
|
||||||
rules: {
|
|
||||||
'import/no-extraneous-dependencies': 'off',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@ -9,14 +9,6 @@
|
|||||||
"files.trimTrailingWhitespace": false
|
"files.trimTrailingWhitespace": false
|
||||||
},
|
},
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"eslint.experimental.useFlatConfig": false,
|
|
||||||
"eslint.validate": [
|
|
||||||
"javascript",
|
|
||||||
"javascriptreact",
|
|
||||||
"typescript",
|
|
||||||
"typescriptreact",
|
|
||||||
"vue"
|
|
||||||
],
|
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"bumpp",
|
"bumpp",
|
||||||
"caniuse",
|
"caniuse",
|
||||||
|
|||||||
17
eslint.config.js
Normal file
17
eslint.config.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import config from '@pengzhanbo/eslint-config-vue'
|
||||||
|
|
||||||
|
export default config({
|
||||||
|
formatters: {
|
||||||
|
css: false,
|
||||||
|
},
|
||||||
|
ignores: ['.temp', '.cache', 'lib', 'dist', 'docs', '**/*.html'],
|
||||||
|
globals: {
|
||||||
|
__VUEPRESS_VERSION__: 'readonly',
|
||||||
|
__VUEPRESS_BASE__: 'readonly',
|
||||||
|
__VUEPRESS_DEV__: 'readonly',
|
||||||
|
__VUEPRESS_SSR__: 'readonly',
|
||||||
|
__VUE_HMR_RUNTIME__: 'readonly',
|
||||||
|
__VUE_OPTIONS_API__: 'readonly',
|
||||||
|
__VUE_PROD_DEVTOOLS__: 'readonly',
|
||||||
|
},
|
||||||
|
})
|
||||||
43
package.json
43
package.json
@ -1,7 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "vuepress-theme-plume-monorepo",
|
"name": "vuepress-theme-plume-monorepo",
|
||||||
|
"type": "module",
|
||||||
"version": "1.0.0-rc.5",
|
"version": "1.0.0-rc.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"packageManager": "pnpm@8.12.1",
|
||||||
|
"author": "pengzhanbo",
|
||||||
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"vuepress",
|
"vuepress",
|
||||||
"vuepress-next",
|
"vuepress-next",
|
||||||
@ -9,9 +13,10 @@
|
|||||||
"vuepress theme",
|
"vuepress theme",
|
||||||
"vuepress-theme-plume"
|
"vuepress-theme-plume"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"engines": {
|
||||||
"author": "pengzhanbo",
|
"node": ">=16",
|
||||||
"type": "module",
|
"pnpm": ">=7"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"autoUpdate": "node scripts/autoInstall.js",
|
"autoUpdate": "node scripts/autoInstall.js",
|
||||||
"build": "pnpm run build:package",
|
"build": "pnpm run build:package",
|
||||||
@ -24,7 +29,7 @@
|
|||||||
"docs:build": "pnpm --filter=docs docs:build",
|
"docs:build": "pnpm --filter=docs docs:build",
|
||||||
"docs:clean": "pnpm --filter=docs docs:clean",
|
"docs:clean": "pnpm --filter=docs docs:clean",
|
||||||
"docs:serve": "pnpm --filter=docs docs:serve",
|
"docs:serve": "pnpm --filter=docs docs:serve",
|
||||||
"lint": "eslint --ext .js,.ts,.vue .",
|
"lint": "eslint .",
|
||||||
"pkg": "node scripts/create/index.js",
|
"pkg": "node scripts/create/index.js",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"release": "pnpm release:check && pnpm release:version && pnpm release:publish",
|
"release": "pnpm release:check && pnpm release:version && pnpm release:publish",
|
||||||
@ -34,20 +39,10 @@
|
|||||||
"release:version": "bumpp package.json plugins/*/package.json theme/package.json --execute=\"pnpm release:changelog\" --commit \"build: publish v%s\" --all --tag --push",
|
"release:version": "bumpp package.json plugins/*/package.json theme/package.json --execute=\"pnpm release:changelog\" --commit \"build: publish v%s\" --all --tag --push",
|
||||||
"up": "taze -r major"
|
"up": "taze -r major"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
|
||||||
"*.{js,ts,vue}": "eslint --fix",
|
|
||||||
"*.{json,yml,css,scss}": "prettier --write",
|
|
||||||
"package.json": "sort-package-json"
|
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"commitizen": {
|
|
||||||
"path": "./node_modules/cz-conventional-changelog"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"prettier": "prettier-config-vuepress",
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^18.4.3",
|
"@commitlint/cli": "^18.4.3",
|
||||||
"@commitlint/config-conventional": "^18.4.3",
|
"@commitlint/config-conventional": "^18.4.3",
|
||||||
|
"@pengzhanbo/eslint-config-vue": "^1.4.0",
|
||||||
"@types/minimist": "^1.2.5",
|
"@types/minimist": "^1.2.5",
|
||||||
"@types/node": "20.9.1",
|
"@types/node": "20.9.1",
|
||||||
"@types/webpack-env": "^1.18.4",
|
"@types/webpack-env": "^1.18.4",
|
||||||
@ -60,8 +55,7 @@
|
|||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cz-conventional-changelog": "^3.3.0",
|
"cz-conventional-changelog": "^3.3.0",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-vuepress": "^4.9.0",
|
"eslint-plugin-format": "^0.1.0",
|
||||||
"eslint-config-vuepress-typescript": "^4.9.0",
|
|
||||||
"execa": "^8.0.1",
|
"execa": "^8.0.1",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
@ -69,8 +63,6 @@
|
|||||||
"minimist": "^1.2.8",
|
"minimist": "^1.2.8",
|
||||||
"ora": "^8.0.1",
|
"ora": "^8.0.1",
|
||||||
"pnpm": "^8.12.1",
|
"pnpm": "^8.12.1",
|
||||||
"prettier": "^3.1.1",
|
|
||||||
"prettier-config-vuepress": "^4.4.0",
|
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"sort-package-json": "^2.6.0",
|
"sort-package-json": "^2.6.0",
|
||||||
"taze": "^0.13.0",
|
"taze": "^0.13.0",
|
||||||
@ -78,11 +70,6 @@
|
|||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"vite": "^5.0.10"
|
"vite": "^5.0.10"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@8.12.1",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16",
|
|
||||||
"pnpm": ">=7"
|
|
||||||
},
|
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"peerDependencyRules": {
|
"peerDependencyRules": {
|
||||||
"ignoreMissing": [
|
"ignoreMissing": [
|
||||||
@ -99,5 +86,13 @@
|
|||||||
"jest"
|
"jest"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*": "eslint --fix"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"commitizen": {
|
||||||
|
"path": "./node_modules/cz-conventional-changelog"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,8 @@ export default {
|
|||||||
autoFrontmatterPlugin({
|
autoFrontmatterPlugin({
|
||||||
formatter: {
|
formatter: {
|
||||||
createTime(formatTime, file, matter) {
|
createTime(formatTime, file, matter) {
|
||||||
if (formatTime) return formatTime
|
if (formatTime)
|
||||||
|
return formatTime
|
||||||
return file.createTime
|
return file.createTime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,14 +71,15 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* formatterObj 对象中的 key 即为 frontmatter 配置中的key
|
* formatterObj 对象中的 key 即为 frontmatter 配置中的key
|
||||||
* 其方法返回的值将作为 frontmatter[key] 的值
|
* 其方法返回的值将作为 frontmatter[key] 的值
|
||||||
* *.md
|
* .md
|
||||||
* ---
|
* ---
|
||||||
* createTime: 2022-03-26T11:46:50.000Z
|
* createTime: 2022-03-26T11:46:50.000Z
|
||||||
* ---
|
* ---
|
||||||
*/
|
*/
|
||||||
const formatterObj: Formatter = {
|
const formatterObj: Formatter = {
|
||||||
createTime(formatTime, file, matter) {
|
createTime(formatTime, file, matter) {
|
||||||
if (formatTime) return formatTime
|
if (formatTime)
|
||||||
|
return formatTime
|
||||||
return file.createTime
|
return file.createTime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,6 +94,7 @@ export default {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// 通配,如果文件没有被其他精细glob命中,
|
// 通配,如果文件没有被其他精细glob命中,
|
||||||
// 则使用 通配 formatter
|
// 则使用 通配 formatter
|
||||||
@ -103,9 +106,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Why ?
|
## Why ?
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "@vuepress-plume/plugin-auto-frontmatter",
|
"name": "@vuepress-plume/plugin-auto-frontmatter",
|
||||||
|
"type": "module",
|
||||||
"version": "1.0.0-rc.5",
|
"version": "1.0.0-rc.5",
|
||||||
"description": "The Plugin for VuePres 2",
|
"description": "The Plugin for VuePres 2",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"bugs": {
|
||||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
"type": "module",
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./lib/node/index.js",
|
".": "./lib/node/index.js",
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
|
|||||||
@ -13,11 +13,11 @@ import type {
|
|||||||
import { readMarkdown, readMarkdownList } from './readFiles.js'
|
import { readMarkdown, readMarkdownList } from './readFiles.js'
|
||||||
import { ensureArray, isEmptyObject } from './utils.js'
|
import { ensureArray, isEmptyObject } from './utils.js'
|
||||||
|
|
||||||
export const autoFrontmatterPlugin = ({
|
export function autoFrontmatterPlugin({
|
||||||
include = ['**/*.{md,MD}'],
|
include = ['**/*.{md,MD}'],
|
||||||
exclude = ['.vuepress/**/*', 'node_modules'],
|
exclude = ['.vuepress/**/*', 'node_modules'],
|
||||||
frontmatter = {},
|
frontmatter = {},
|
||||||
}: AutoFrontmatterOptions = {}): Plugin => {
|
}: AutoFrontmatterOptions = {}): Plugin {
|
||||||
include = ensureArray(include)
|
include = ensureArray(include)
|
||||||
exclude = ensureArray(exclude)
|
exclude = ensureArray(exclude)
|
||||||
|
|
||||||
@ -27,8 +27,8 @@ export const autoFrontmatterPlugin = ({
|
|||||||
? frontmatter
|
? frontmatter
|
||||||
: [{ include: '*', frontmatter }]
|
: [{ include: '*', frontmatter }]
|
||||||
|
|
||||||
const globFormatter: FrontmatterObject =
|
const globFormatter: FrontmatterObject
|
||||||
matterFrontmatter.find(({ include }) => include === '*')?.frontmatter || {}
|
= matterFrontmatter.find(({ include }) => include === '*')?.frontmatter || {}
|
||||||
|
|
||||||
const otherFormatters = matterFrontmatter
|
const otherFormatters = matterFrontmatter
|
||||||
.filter(({ include }) => include !== '*')
|
.filter(({ include }) => include !== '*')
|
||||||
@ -62,8 +62,9 @@ export const autoFrontmatterPlugin = ({
|
|||||||
const newContent = yaml ? `${yaml}---\n${content}` : content
|
const newContent = yaml ? `${yaml}---\n${content}` : content
|
||||||
|
|
||||||
fs.writeFileSync(filepath, newContent, 'utf-8')
|
fs.writeFileSync(filepath, newContent, 'utf-8')
|
||||||
} catch (e) {
|
}
|
||||||
console.log(e)
|
catch (e) {
|
||||||
|
console.error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,9 +72,8 @@ export const autoFrontmatterPlugin = ({
|
|||||||
name: '@vuepress-plume/plugin-auto-frontmatter',
|
name: '@vuepress-plume/plugin-auto-frontmatter',
|
||||||
onInitialized: async (app) => {
|
onInitialized: async (app) => {
|
||||||
const markdownList = await readMarkdownList(app.dir.source(), globFilter)
|
const markdownList = await readMarkdownList(app.dir.source(), globFilter)
|
||||||
for (const file of markdownList) {
|
for (const file of markdownList)
|
||||||
await formatMarkdown(file)
|
await formatMarkdown(file)
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onWatched: async (app, watchers) => {
|
onWatched: async (app, watchers) => {
|
||||||
const watcher = chokidar.watch('**/*.md', {
|
const watcher = chokidar.watch('**/*.md', {
|
||||||
@ -83,7 +83,8 @@ export const autoFrontmatterPlugin = ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
watcher.on('add', async (relativePath) => {
|
watcher.on('add', async (relativePath) => {
|
||||||
if (!globFilter(relativePath)) return
|
if (!globFilter(relativePath))
|
||||||
|
return
|
||||||
await formatMarkdown(readMarkdown(app.dir.source(), relativePath))
|
await formatMarkdown(readMarkdown(app.dir.source(), relativePath))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -5,24 +5,18 @@ import type { MarkdownFile } from '../shared/index.js'
|
|||||||
|
|
||||||
type MarkdownFileList = MarkdownFile[]
|
type MarkdownFileList = MarkdownFile[]
|
||||||
|
|
||||||
export const readMarkdownList = async (
|
export async function readMarkdownList(sourceDir: string, filter: (id: string) => boolean): Promise<MarkdownFileList> {
|
||||||
sourceDir: string,
|
|
||||||
filter: (id: string) => boolean
|
|
||||||
): Promise<MarkdownFileList> => {
|
|
||||||
const files: string[] = await fg(['**/*.md'], {
|
const files: string[] = await fg(['**/*.md'], {
|
||||||
cwd: sourceDir,
|
cwd: sourceDir,
|
||||||
ignore: ['node_modules', '.vuepress'],
|
ignore: ['node_modules', '.vuepress'],
|
||||||
})
|
})
|
||||||
|
|
||||||
return files
|
return files
|
||||||
.filter((file) => filter(file))
|
.filter(file => filter(file))
|
||||||
.map((file) => readMarkdown(sourceDir, file))
|
.map(file => readMarkdown(sourceDir, file))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const readMarkdown = (
|
export function readMarkdown(sourceDir: string, relativePath: string): MarkdownFile {
|
||||||
sourceDir: string,
|
|
||||||
relativePath: string
|
|
||||||
): MarkdownFile => {
|
|
||||||
const filepath = path.join(sourceDir, relativePath)
|
const filepath = path.join(sourceDir, relativePath)
|
||||||
const stats = fs.statSync(filepath)
|
const stats = fs.statSync(filepath)
|
||||||
return {
|
return {
|
||||||
@ -34,6 +28,6 @@ export const readMarkdown = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getFileCreateTime = (stats: fs.Stats): Date => {
|
export function getFileCreateTime(stats: fs.Stats): Date {
|
||||||
return stats.birthtime.getFullYear() !== 1970 ? stats.birthtime : stats.atime
|
return stats.birthtime.getFullYear() !== 1970 ? stats.birthtime : stats.atime
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
export function ensureArray<T>(thing: T | T[] | null | undefined): T[] {
|
export function ensureArray<T>(thing: T | T[] | null | undefined): T[] {
|
||||||
if (Array.isArray(thing)) return thing
|
if (Array.isArray(thing))
|
||||||
if (thing === null || thing === undefined) return []
|
return thing
|
||||||
|
if (thing === null || thing === undefined)
|
||||||
|
return []
|
||||||
return [thing]
|
return [thing]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type fs from 'node:fs'
|
import type fs from 'node:fs'
|
||||||
|
|
||||||
export interface MarkdownFile {
|
export interface MarkdownFile {
|
||||||
filepath: string
|
filepath: string
|
||||||
relativePath: string
|
relativePath: string
|
||||||
@ -13,7 +14,7 @@ export type FrontmatterFn<T = any, K = object> = (
|
|||||||
data: K
|
data: K
|
||||||
) => T | PromiseLike<T>
|
) => T | PromiseLike<T>
|
||||||
|
|
||||||
export type FrontmatterObject<K = object, T = any> = Record<string, FrontmatterFn<T, K>>;
|
export type FrontmatterObject<K = object, T = any> = Record<string, FrontmatterFn<T, K>>
|
||||||
|
|
||||||
export type FrontmatterArray = {
|
export type FrontmatterArray = {
|
||||||
include: string | string[]
|
include: string | string[]
|
||||||
|
|||||||
@ -4,6 +4,6 @@
|
|||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"outDir": "./lib"
|
"outDir": "./lib"
|
||||||
},
|
},
|
||||||
"include": ["./src"],
|
"files": [],
|
||||||
"files": []
|
"include": ["./src"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "@vuepress-plume/plugin-baidu-tongji",
|
"name": "@vuepress-plume/plugin-baidu-tongji",
|
||||||
|
"type": "module",
|
||||||
"version": "1.0.0-rc.5",
|
"version": "1.0.0-rc.5",
|
||||||
"description": "The Plugin for VuePres 2",
|
"description": "The Plugin for VuePres 2",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com> (https://github.com/pengzhanbo/)",
|
||||||
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"bugs": {
|
||||||
"author": "pengzhanbo <volodymyr@foxmail.com> (https://github.com/pengzhanbo/)",
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
"type": "module",
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./lib/node/index.js",
|
".": "./lib/node/index.js",
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
|
|||||||
@ -4,11 +4,11 @@ export interface BaiduTongjiOptions {
|
|||||||
key?: string
|
key?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const baiduTongjiPlugin = ({ key = '' }: BaiduTongjiOptions): Plugin => {
|
export function baiduTongjiPlugin({ key = '' }: BaiduTongjiOptions): Plugin {
|
||||||
return {
|
return {
|
||||||
name: '@vuepress-plume/plugin-baidu-tongji',
|
name: '@vuepress-plume/plugin-baidu-tongji',
|
||||||
extendsPage: (page) => {
|
extendsPage: (page) => {
|
||||||
page.frontmatter.head = page.frontmatter.head || [];
|
page.frontmatter.head = page.frontmatter.head || []
|
||||||
page.frontmatter.head?.push([
|
page.frontmatter.head?.push([
|
||||||
'script',
|
'script',
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
"extends": "../../tsconfig.base.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"outDir": "./lib",
|
"outDir": "./lib"
|
||||||
"composite": true
|
|
||||||
},
|
},
|
||||||
"include": ["./src"],
|
"files": [],
|
||||||
"files": []
|
"include": ["./src"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "@vuepress-plume/plugin-blog-data",
|
"name": "@vuepress-plume/plugin-blog-data",
|
||||||
|
"type": "module",
|
||||||
"version": "1.0.0-rc.5",
|
"version": "1.0.0-rc.5",
|
||||||
"description": "The Plugin for VuePres 2",
|
"description": "The Plugin for VuePres 2",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"bugs": {
|
||||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
"type": "module",
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./lib/node/index.js",
|
".": "./lib/node/index.js",
|
||||||
"./client": "./lib/client/index.js",
|
"./client": "./lib/client/index.js",
|
||||||
|
|||||||
@ -9,9 +9,11 @@ export type BlogDataRef<T extends BlogPostData = BlogPostData> = Ref<T>
|
|||||||
|
|
||||||
export const blogPostData: BlogDataRef = ref(blogPostDataRaw)
|
export const blogPostData: BlogDataRef = ref(blogPostDataRaw)
|
||||||
|
|
||||||
export const useBlogPostData = <
|
export function useBlogPostData<
|
||||||
T extends BlogPostData = BlogPostData
|
T extends BlogPostData = BlogPostData,
|
||||||
>(): BlogDataRef<T> => blogPostData as BlogDataRef<T>
|
>(): BlogDataRef<T> {
|
||||||
|
return blogPostData as BlogDataRef<T>
|
||||||
|
}
|
||||||
|
|
||||||
if (import.meta.webpackHot || import.meta.hot) {
|
if (import.meta.webpackHot || import.meta.hot) {
|
||||||
__VUE_HMR_RUNTIME__.updateBlogData = (data: BlogPostData) => {
|
__VUE_HMR_RUNTIME__.updateBlogData = (data: BlogPostData) => {
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export default defineClientConfig({
|
|||||||
value: blogPostData.value,
|
value: blogPostData.value,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { BlogPostData, BlogPostDataItem } from '../shared/index.js'
|
import type { BlogPostData, BlogPostDataItem } from '../shared/index.js'
|
||||||
|
|
||||||
export * from './composables/index.js'
|
export * from './composables/index.js'
|
||||||
|
|
||||||
export type { BlogPostData, BlogPostDataItem }
|
export type { BlogPostData, BlogPostDataItem }
|
||||||
|
|||||||
@ -9,11 +9,11 @@ const __dirname = getDirname(import.meta.url)
|
|||||||
|
|
||||||
export type PluginOption = Omit<BlogDataPluginOptions, 'include' | 'exclude'>
|
export type PluginOption = Omit<BlogDataPluginOptions, 'include' | 'exclude'>
|
||||||
|
|
||||||
export const blogDataPlugin = ({
|
export function blogDataPlugin({
|
||||||
include,
|
include,
|
||||||
exclude,
|
exclude,
|
||||||
...pluginOptions
|
...pluginOptions
|
||||||
}: BlogDataPluginOptions = {}): Plugin => {
|
}: BlogDataPluginOptions = {}): Plugin {
|
||||||
const pageFilter = createFilter(toArray(include), toArray(exclude), {
|
const pageFilter = createFilter(toArray(include), toArray(exclude), {
|
||||||
resolve: false,
|
resolve: false,
|
||||||
})
|
})
|
||||||
@ -26,7 +26,7 @@ export const blogDataPlugin = ({
|
|||||||
;(page.data as any).isBlogPost = true
|
;(page.data as any).isBlogPost = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPrepared: async (app) =>
|
onPrepared: async app =>
|
||||||
await preparedBlogData(app, pageFilter, pluginOptions),
|
await preparedBlogData(app, pageFilter, pluginOptions),
|
||||||
onWatched(app, watchers) {
|
onWatched(app, watchers) {
|
||||||
const watcher = chokidar.watch('pages/**/*', {
|
const watcher = chokidar.watch('pages/**/*', {
|
||||||
@ -36,15 +36,15 @@ export const blogDataPlugin = ({
|
|||||||
|
|
||||||
watcher.on(
|
watcher.on(
|
||||||
'add',
|
'add',
|
||||||
async () => await preparedBlogData(app, pageFilter, pluginOptions)
|
async () => await preparedBlogData(app, pageFilter, pluginOptions),
|
||||||
)
|
)
|
||||||
watcher.on(
|
watcher.on(
|
||||||
'change',
|
'change',
|
||||||
async () => await preparedBlogData(app, pageFilter, pluginOptions)
|
async () => await preparedBlogData(app, pageFilter, pluginOptions),
|
||||||
)
|
)
|
||||||
watcher.on(
|
watcher.on(
|
||||||
'unlink',
|
'unlink',
|
||||||
async () => await preparedBlogData(app, pageFilter, pluginOptions)
|
async () => await preparedBlogData(app, pageFilter, pluginOptions),
|
||||||
)
|
)
|
||||||
|
|
||||||
watchers.push(watcher)
|
watchers.push(watcher)
|
||||||
@ -53,6 +53,7 @@ export const blogDataPlugin = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toArray(likeArr: string | string[] | undefined): string[] {
|
function toArray(likeArr: string | string[] | undefined): string[] {
|
||||||
if (Array.isArray(likeArr)) return likeArr
|
if (Array.isArray(likeArr))
|
||||||
|
return likeArr
|
||||||
return likeArr ? [likeArr] : []
|
return likeArr ? [likeArr] : []
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,33 +17,30 @@ if (import.meta.hot) {
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const getTimestamp = (time: Date): number => {
|
function getTimestamp(time: Date): number {
|
||||||
return new Date(time).getTime()
|
return new Date(time).getTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
const EXCERPT_SPLIT = '<!-- more -->'
|
const EXCERPT_SPLIT = '<!-- more -->'
|
||||||
|
|
||||||
export const preparedBlogData = async (
|
export async function preparedBlogData(app: App, pageFilter: (id: string) => boolean, options: PluginOption): Promise<void> {
|
||||||
app: App,
|
|
||||||
pageFilter: (id: string) => boolean,
|
|
||||||
options: PluginOption
|
|
||||||
): Promise<void> => {
|
|
||||||
let pages = app.pages.filter((page) => {
|
let pages = app.pages.filter((page) => {
|
||||||
return page.filePathRelative && pageFilter(page.filePathRelative)
|
return page.filePathRelative && pageFilter(page.filePathRelative)
|
||||||
})
|
})
|
||||||
if (options.pageFilter) {
|
if (options.pageFilter)
|
||||||
pages = pages.filter(options.pageFilter)
|
pages = pages.filter(options.pageFilter)
|
||||||
}
|
|
||||||
if (options.sortBy) {
|
if (options.sortBy) {
|
||||||
pages = pages.sort((prev, next) => {
|
pages = pages.sort((prev, next) => {
|
||||||
if (options.sortBy === 'createTime') {
|
if (options.sortBy === 'createTime') {
|
||||||
return getTimestamp(prev.frontmatter.createTime as Date) <
|
return getTimestamp(prev.frontmatter.createTime as Date)
|
||||||
getTimestamp(next.frontmatter.createTime as Date)
|
< getTimestamp(next.frontmatter.createTime as Date)
|
||||||
? 1
|
? 1
|
||||||
: -1
|
: -1
|
||||||
} else {
|
}
|
||||||
return typeof options.sortBy === 'function' &&
|
else {
|
||||||
options.sortBy(prev, next)
|
return typeof options.sortBy === 'function'
|
||||||
|
&& options.sortBy(prev, next)
|
||||||
? 1
|
? 1
|
||||||
: -1
|
: -1
|
||||||
}
|
}
|
||||||
@ -52,9 +49,9 @@ export const preparedBlogData = async (
|
|||||||
|
|
||||||
const blogData: BlogPostData = pages.map((page: Page) => {
|
const blogData: BlogPostData = pages.map((page: Page) => {
|
||||||
let extended: Partial<BlogPostDataItem> = {}
|
let extended: Partial<BlogPostDataItem> = {}
|
||||||
if (typeof options.extendBlogData === 'function') {
|
if (typeof options.extendBlogData === 'function')
|
||||||
extended = options.extendBlogData(page)
|
extended = options.extendBlogData(page)
|
||||||
}
|
|
||||||
const data = {
|
const data = {
|
||||||
path: page.path,
|
path: page.path,
|
||||||
title: page.title,
|
title: page.title,
|
||||||
@ -71,14 +68,13 @@ export const preparedBlogData = async (
|
|||||||
|
|
||||||
let content = `\
|
let content = `\
|
||||||
export const blogPostData = JSON.parse(${JSON.stringify(
|
export const blogPostData = JSON.parse(${JSON.stringify(
|
||||||
JSON.stringify(blogData)
|
JSON.stringify(blogData),
|
||||||
)})
|
)})
|
||||||
`
|
`
|
||||||
|
|
||||||
// inject HMR code
|
// inject HMR code
|
||||||
if (app.env.isDev) {
|
if (app.env.isDev)
|
||||||
content += HMR_CODE
|
content += HMR_CODE
|
||||||
}
|
|
||||||
|
|
||||||
await app.writeTemp('internal/blogData.js', content)
|
await app.writeTemp('internal/blogData.js', content)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"extends": "../tsconfig.build.json",
|
"extends": "../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "./src",
|
|
||||||
"outDir": "./lib",
|
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"rootDir": "./src",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@internal/blogData": ["./src/client/blogPostData.d.ts"]
|
"@internal/blogData": ["./src/client/blogPostData.d.ts"]
|
||||||
},
|
},
|
||||||
"types": ["@vuepress/client/types", "vite/client", "webpack-env"]
|
"types": ["@vuepress/client/types", "vite/client", "webpack-env"],
|
||||||
|
"outDir": "./lib"
|
||||||
},
|
},
|
||||||
"include": ["./src"]
|
"include": ["./src"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "@vuepress-plume/plugin-caniuse",
|
"name": "@vuepress-plume/plugin-caniuse",
|
||||||
|
"type": "module",
|
||||||
"version": "1.0.0-rc.5",
|
"version": "1.0.0-rc.5",
|
||||||
"description": "The Plugin for VuePres 2, Support Can-I-Use feature",
|
"description": "The Plugin for VuePres 2, Support Can-I-Use feature",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"VuePress",
|
"VuePress",
|
||||||
"plugin",
|
"plugin",
|
||||||
@ -9,17 +20,6 @@
|
|||||||
"can-i-use",
|
"can-i-use",
|
||||||
"caniuse"
|
"caniuse"
|
||||||
],
|
],
|
||||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
|
||||||
"type": "module",
|
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./lib/node/index.js",
|
".": "./lib/node/index.js",
|
||||||
"./client": "./lib/client/index.js",
|
"./client": "./lib/client/index.js",
|
||||||
|
|||||||
@ -9,12 +9,12 @@ const mode = __CAN_I_USE_INJECT_MODE__
|
|||||||
|
|
||||||
export default defineClientConfig({
|
export default defineClientConfig({
|
||||||
enhance({ router }) {
|
enhance({ router }) {
|
||||||
if (__VUEPRESS_SSR__) return
|
if (__VUEPRESS_SSR__)
|
||||||
|
return
|
||||||
|
|
||||||
router.afterEach((to, from) => {
|
router.afterEach(() => {
|
||||||
if (mode === 'embed') {
|
if (mode === 'embed')
|
||||||
resolveCanIUse()
|
resolveCanIUse()
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,17 +1,18 @@
|
|||||||
let isBind = false
|
let isBind = false
|
||||||
export const resolveCanIUse = (): void => {
|
export function resolveCanIUse(): void {
|
||||||
if (isBind) return
|
if (isBind)
|
||||||
|
return
|
||||||
isBind = true
|
isBind = true
|
||||||
|
|
||||||
window.addEventListener('message', (message) => {
|
window.addEventListener('message', (message) => {
|
||||||
const data = message.data
|
const data = message.data
|
||||||
|
|
||||||
if (typeof data === 'string' && data.indexOf('ciu_embed') > -1) {
|
if (typeof data === 'string' && data.includes('ciu_embed')) {
|
||||||
const [, feature, height] = data.split(':')
|
const [, feature, height] = data.split(':')
|
||||||
const el = document.querySelector(`.ciu_embed[data-feature="${feature}"]`)
|
const el = document.querySelector(`.ciu_embed[data-feature="${feature}"]`)
|
||||||
if (el) {
|
if (el) {
|
||||||
const h = parseInt(height) + 30
|
const h = Number.parseInt(height) + 30
|
||||||
;(el.childNodes[0] as any).height = h + 'px'
|
;(el.childNodes[0] as any).height = `${h}px`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
declare module 'markdown-it-container' {
|
declare module 'markdown-it-container' {
|
||||||
import type { PluginWithParams } from 'markdown-it'
|
import type { PluginWithParams } from 'markdown-it'
|
||||||
|
|
||||||
const container: PluginWithParams
|
const container: PluginWithParams
|
||||||
export = container
|
export = container
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,9 +9,9 @@ const __dirname = getDirname(import.meta.url)
|
|||||||
const modeMap: CanIUseMode[] = ['image', 'embed']
|
const modeMap: CanIUseMode[] = ['image', 'embed']
|
||||||
const isMode = (mode: CanIUseMode): boolean => modeMap.includes(mode)
|
const isMode = (mode: CanIUseMode): boolean => modeMap.includes(mode)
|
||||||
|
|
||||||
export const caniusePlugin = ({
|
export function caniusePlugin({
|
||||||
mode = modeMap[0],
|
mode = modeMap[0],
|
||||||
}: CanIUsePluginOptions): Plugin => {
|
}: CanIUsePluginOptions): Plugin {
|
||||||
mode = isMode(mode) ? mode : modeMap[0]
|
mode = isMode(mode) ? mode : modeMap[0]
|
||||||
const type = 'caniuse'
|
const type = 'caniuse'
|
||||||
const validateReg = new RegExp(`^${type}\\s+(.*)$`)
|
const validateReg = new RegExp(`^${type}\\s+(.*)$`)
|
||||||
@ -32,7 +32,8 @@ export const caniusePlugin = ({
|
|||||||
if (token.nesting === 1) {
|
if (token.nesting === 1) {
|
||||||
const feature = token.info.trim().slice(type.length).trim() || ''
|
const feature = token.info.trim().slice(type.length).trim() || ''
|
||||||
return feature ? resolveCanIUse(feature, mode) : ''
|
return feature ? resolveCanIUse(feature, mode) : ''
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import type { CanIUseMode } from '../shared/index.js'
|
import type { CanIUseMode } from '../shared/index.js'
|
||||||
export const resolveCanIUse = (feature: string, mode: CanIUseMode): string => {
|
|
||||||
if (!feature) return ''
|
export function resolveCanIUse(feature: string, mode: CanIUseMode): string {
|
||||||
|
if (!feature)
|
||||||
|
return ''
|
||||||
|
|
||||||
if (mode === 'image') {
|
if (mode === 'image') {
|
||||||
return `<picture>
|
return `<picture>
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "@vuepress-plume/plugin-copy-code",
|
"name": "@vuepress-plume/plugin-copy-code",
|
||||||
|
"type": "module",
|
||||||
"version": "1.0.0-rc.5",
|
"version": "1.0.0-rc.5",
|
||||||
"description": "The Plugin for VuePres 2",
|
"description": "The Plugin for VuePres 2",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"bugs": {
|
||||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
"type": "module",
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./lib/node/index.js",
|
".": "./lib/node/index.js",
|
||||||
"./client": "./lib/client/index.js",
|
"./client": "./lib/client/index.js",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
export const copyToClipboard = (str: string): void => {
|
export function copyToClipboard(str: string): void {
|
||||||
const selection = document.getSelection()
|
const selection = document.getSelection()
|
||||||
const selectedRange =
|
const selectedRange
|
||||||
selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : false
|
= selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : false
|
||||||
|
|
||||||
const textEl = document.createElement('textarea')
|
const textEl = document.createElement('textarea')
|
||||||
|
|
||||||
|
|||||||
@ -2,37 +2,40 @@ import { onMounted, watch } from 'vue'
|
|||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import type { CopyCodeOptions } from '../../shared/index.js'
|
import type { CopyCodeOptions } from '../../shared/index.js'
|
||||||
import { copyToClipboard } from './copyToClipboard.js'
|
import { copyToClipboard } from './copyToClipboard.js'
|
||||||
|
|
||||||
declare const __COPY_CODE_OPTIONS__: CopyCodeOptions
|
declare const __COPY_CODE_OPTIONS__: CopyCodeOptions
|
||||||
|
|
||||||
const options = __COPY_CODE_OPTIONS__
|
const options = __COPY_CODE_OPTIONS__
|
||||||
|
|
||||||
const isMobile = (): boolean =>
|
function isMobile(): boolean {
|
||||||
navigator
|
return navigator
|
||||||
? /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/iu.test(
|
? /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/iu.test(
|
||||||
navigator.userAgent
|
navigator.userAgent,
|
||||||
)
|
)
|
||||||
: false
|
: false
|
||||||
|
}
|
||||||
|
|
||||||
export const setupCopyCode = (): void => {
|
export function setupCopyCode(): void {
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
const insertBtn = (codeBlockEl: HTMLElement): void => {
|
const insertBtn = (codeBlockEl: HTMLElement): void => {
|
||||||
if (codeBlockEl.hasAttribute('has-copy-code')) return
|
if (codeBlockEl.hasAttribute('has-copy-code'))
|
||||||
|
return
|
||||||
const button = document.createElement('button')
|
const button = document.createElement('button')
|
||||||
button.className = 'copy-code-button'
|
button.className = 'copy-code-button'
|
||||||
|
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
copyToClipboard(codeBlockEl.innerText)
|
copyToClipboard(codeBlockEl.textContent || '')
|
||||||
button.classList.add('copied')
|
button.classList.add('copied')
|
||||||
options.duration &&
|
options.duration
|
||||||
setTimeout(() => {
|
&& setTimeout(() => {
|
||||||
button.classList.remove('copied')
|
button.classList.remove('copied')
|
||||||
}, options.duration)
|
}, options.duration)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (codeBlockEl.parentElement) {
|
if (codeBlockEl.parentElement)
|
||||||
codeBlockEl.parentElement.insertBefore(button, codeBlockEl)
|
codeBlockEl.parentElement.insertBefore(button, codeBlockEl)
|
||||||
}
|
|
||||||
codeBlockEl.setAttribute('has-copy-code', '')
|
codeBlockEl.setAttribute('has-copy-code', '')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +44,8 @@ export const setupCopyCode = (): void => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (typeof selector === 'string') {
|
if (typeof selector === 'string') {
|
||||||
document.querySelectorAll<HTMLElement>(selector).forEach(insertBtn)
|
document.querySelectorAll<HTMLElement>(selector).forEach(insertBtn)
|
||||||
} else if (Array.isArray(selector)) {
|
}
|
||||||
|
else if (Array.isArray(selector)) {
|
||||||
selector.forEach((item) => {
|
selector.forEach((item) => {
|
||||||
document.querySelectorAll<HTMLElement>(item).forEach(insertBtn)
|
document.querySelectorAll<HTMLElement>(item).forEach(insertBtn)
|
||||||
})
|
})
|
||||||
@ -50,16 +54,14 @@ export const setupCopyCode = (): void => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!isMobile() || options.showInMobile) {
|
if (!isMobile() || options.showInMobile)
|
||||||
generateButton()
|
generateButton()
|
||||||
}
|
|
||||||
})
|
})
|
||||||
watch(
|
watch(
|
||||||
() => route.path,
|
() => route.path,
|
||||||
() => {
|
() => {
|
||||||
if (!isMobile() || options.showInMobile) {
|
if (!isMobile() || options.showInMobile)
|
||||||
generateButton()
|
generateButton()
|
||||||
}
|
},
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,9 +11,7 @@ const defaultOptions: CopyCodeOptions = {
|
|||||||
showInMobile: false,
|
showInMobile: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function copyCodePlugin(options: CopyCodeOptions): Plugin {
|
||||||
export const copyCodePlugin = (options: CopyCodeOptions): Plugin => {
|
|
||||||
|
|
||||||
options = Object.assign({}, defaultOptions, options)
|
options = Object.assign({}, defaultOptions, options)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -30,7 +30,6 @@ interface IconifyOptions {
|
|||||||
color?: string
|
color?: string
|
||||||
size?: string | number
|
size?: string | number
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Component
|
## Component
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "@vuepress-plume/plugin-iconify",
|
"name": "@vuepress-plume/plugin-iconify",
|
||||||
|
"type": "module",
|
||||||
"version": "1.0.0-rc.5",
|
"version": "1.0.0-rc.5",
|
||||||
"description": "The Plugin for VuePres 2",
|
"description": "The Plugin for VuePres 2",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"bugs": {
|
||||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
"type": "module",
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./lib/node/index.js",
|
".": "./lib/node/index.js",
|
||||||
"./client": "./lib/client/index.js",
|
"./client": "./lib/client/index.js",
|
||||||
|
|||||||
@ -15,7 +15,7 @@ const props = withDefaults(
|
|||||||
name: '',
|
name: '',
|
||||||
size: '',
|
size: '',
|
||||||
color: '',
|
color: '',
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const { name } = toRefs(props)
|
const { name } = toRefs(props)
|
||||||
@ -24,9 +24,9 @@ const { icon, loaded } = useIconify(name)
|
|||||||
|
|
||||||
const size = computed(() => {
|
const size = computed(() => {
|
||||||
const size = props.size || __VUEPRESS_PLUGIN_ICONIFY_DEFAULT_SIZE__
|
const size = props.size || __VUEPRESS_PLUGIN_ICONIFY_DEFAULT_SIZE__
|
||||||
if (String(Number(size)) === size) {
|
if (String(Number(size)) === size)
|
||||||
return `${size}px`
|
return `${size}px`
|
||||||
}
|
|
||||||
return size
|
return size
|
||||||
})
|
})
|
||||||
const iconStyle = computed(() => {
|
const iconStyle = computed(() => {
|
||||||
@ -46,7 +46,7 @@ declare const __VUEPRESS_PLUGIN_ICONIFY_DEFAULT_COLOR__: string
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<span v-if="!loaded" class="vp-iconify" :style="iconStyle"></span>
|
<span v-if="!loaded" class="vp-iconify" :style="iconStyle" />
|
||||||
<OfflineIcon
|
<OfflineIcon
|
||||||
v-else-if="icon"
|
v-else-if="icon"
|
||||||
:icon="icon"
|
:icon="icon"
|
||||||
@ -58,6 +58,7 @@ declare const __VUEPRESS_PLUGIN_ICONIFY_DEFAULT_COLOR__: string
|
|||||||
</span>
|
</span>
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.vp-iconify {
|
.vp-iconify {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
@ -5,22 +5,24 @@ import type { ComputedRef, Ref } from 'vue'
|
|||||||
|
|
||||||
const iconCache: Ref<Record<string, IconifyIcon>> = ref({})
|
const iconCache: Ref<Record<string, IconifyIcon>> = ref({})
|
||||||
|
|
||||||
export const useIconify = (name: ComputedRef<string> | Ref<string>) => {
|
export function useIconify(name: ComputedRef<string> | Ref<string>) {
|
||||||
const icon = computed(() => iconCache.value[name.value])
|
const icon = computed(() => iconCache.value[name.value])
|
||||||
const loaded = ref(true)
|
const loaded = ref(true)
|
||||||
|
|
||||||
async function loadIconComponent() {
|
async function loadIconComponent() {
|
||||||
if (icon.value) {
|
if (icon.value)
|
||||||
return
|
return
|
||||||
}
|
|
||||||
if (!__VUEPRESS_SSR__) {
|
if (!__VUEPRESS_SSR__) {
|
||||||
try {
|
try {
|
||||||
loaded.value = false
|
loaded.value = false
|
||||||
iconCache.value[name.value] = await loadIcon(name.value)
|
iconCache.value[name.value] = await loadIcon(name.value)
|
||||||
} finally {
|
}
|
||||||
|
finally {
|
||||||
loaded.value = true
|
loaded.value = true
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
loaded.value = true
|
loaded.value = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import type { App, Plugin } from '@vuepress/core'
|
import type { Plugin } from '@vuepress/core'
|
||||||
import { getDirname, path } from '@vuepress/utils'
|
import { getDirname, path } from '@vuepress/utils'
|
||||||
import type { IconifyOptions } from '../shared/index.js'
|
import type { IconifyOptions } from '../shared/index.js'
|
||||||
|
|
||||||
export const iconifyPlugin = ({
|
export function iconifyPlugin({
|
||||||
componentName = 'Iconify',
|
componentName = 'Iconify',
|
||||||
size = '1em',
|
size = '1em',
|
||||||
color = 'currentColor',
|
color = 'currentColor',
|
||||||
}: IconifyOptions = {}): Plugin => {
|
}: IconifyOptions = {}): Plugin {
|
||||||
return (app: App) => {
|
return () => {
|
||||||
return {
|
return {
|
||||||
name: '@vuepress-plume/plugin-iconify',
|
name: '@vuepress-plume/plugin-iconify',
|
||||||
define: {
|
define: {
|
||||||
@ -17,7 +17,7 @@ export const iconifyPlugin = ({
|
|||||||
},
|
},
|
||||||
clientConfigFile: path.resolve(
|
clientConfigFile: path.resolve(
|
||||||
getDirname(import.meta.url),
|
getDirname(import.meta.url),
|
||||||
'../client/clientConfig.js'
|
'../client/clientConfig.js',
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
# `vuepress-plugin-netlify-functions`
|
# `vuepress-plugin-netlify-functions`
|
||||||
|
|
||||||
|
|
||||||
If your vuepress site is deployed on `netlify` and you want to be able to use `netlify functions` for ` serverless`.
|
If your vuepress site is deployed on `netlify` and you want to be able to use `netlify functions` for ` serverless`.
|
||||||
|
|
||||||
You may need this plugin to provide support.
|
You may need this plugin to provide support.
|
||||||
@ -49,7 +48,7 @@ yarn add vuepress-plugin-netlify-functions
|
|||||||
``` js
|
``` js
|
||||||
import { useNetlifyFunctionsPlugin } from 'vuepress-plugin-netlify-functions'
|
import { useNetlifyFunctionsPlugin } from 'vuepress-plugin-netlify-functions'
|
||||||
|
|
||||||
const myPlugin = (): Plugin => {
|
function myPlugin(): Plugin {
|
||||||
return (app: App) => {
|
return (app: App) => {
|
||||||
const {
|
const {
|
||||||
// proxy prefix, default: /api
|
// proxy prefix, default: /api
|
||||||
|
|||||||
@ -1,7 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "vuepress-plugin-netlify-functions",
|
"name": "vuepress-plugin-netlify-functions",
|
||||||
|
"type": "module",
|
||||||
"version": "1.0.0-rc.5",
|
"version": "1.0.0-rc.5",
|
||||||
"description": "The Plugin for VuePres 2, Support Netlify Functions",
|
"description": "The Plugin for VuePres 2, Support Netlify Functions",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"VuePress",
|
"VuePress",
|
||||||
"vuepress plugin",
|
"vuepress plugin",
|
||||||
@ -10,17 +21,6 @@
|
|||||||
"netlifyFunctions",
|
"netlifyFunctions",
|
||||||
"vuepress-plugin-netlify-functions"
|
"vuepress-plugin-netlify-functions"
|
||||||
],
|
],
|
||||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
|
||||||
"type": "module",
|
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./lib/node/index.js",
|
".": "./lib/node/index.js",
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
import type { App } from '@vuepress/core'
|
import type { App } from '@vuepress/core'
|
||||||
import type { NetlifyFunctionsPluginOptions } from '../shared/index.js'
|
import type { NetlifyFunctionsPluginOptions } from '../shared/index.js'
|
||||||
export const extendsBundlerOptions = (
|
|
||||||
bundlerOption: any,
|
export function extendsBundlerOptions(bundlerOption: any, app: App, options: NetlifyFunctionsPluginOptions, server: string): void {
|
||||||
app: App,
|
|
||||||
options: NetlifyFunctionsPluginOptions,
|
|
||||||
server: string
|
|
||||||
): void => {
|
|
||||||
// 在 netlify-cli 的 function:serve 中,
|
// 在 netlify-cli 的 function:serve 中,
|
||||||
// 默认就是 指向 /.netlify/functions
|
// 默认就是 指向 /.netlify/functions
|
||||||
// 而配置的 --functions 仅作为源文件入口
|
// 而配置的 --functions 仅作为源文件入口
|
||||||
@ -27,7 +23,8 @@ export const extendsBundlerOptions = (
|
|||||||
const rewritePath = `^${options.proxyPrefix}`
|
const rewritePath = `^${options.proxyPrefix}`
|
||||||
bundlerOption.configureWebpack(
|
bundlerOption.configureWebpack(
|
||||||
(config: any, isServer: boolean, isBuild: boolean) => {
|
(config: any, isServer: boolean, isBuild: boolean) => {
|
||||||
if (isBuild) return
|
if (isBuild)
|
||||||
|
return
|
||||||
config.devServer = config.devServer || {}
|
config.devServer = config.devServer || {}
|
||||||
config.devServer.proxy = Object.assign(config.devServer.proxy || {}, {
|
config.devServer.proxy = Object.assign(config.devServer.proxy || {}, {
|
||||||
[options.proxyPrefix as string]: {
|
[options.proxyPrefix as string]: {
|
||||||
@ -36,7 +33,7 @@ export const extendsBundlerOptions = (
|
|||||||
pathRewrite: { [rewritePath]: targetPath },
|
pathRewrite: { [rewritePath]: targetPath },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,10 +5,7 @@ import esbuild from 'esbuild'
|
|||||||
import type { NetlifyFunctionsPluginOptions } from '../../shared/index.js'
|
import type { NetlifyFunctionsPluginOptions } from '../../shared/index.js'
|
||||||
import { readFileList } from '../utils/index.js'
|
import { readFileList } from '../utils/index.js'
|
||||||
|
|
||||||
export const generateFunctions = async (
|
export async function generateFunctions(app: App, options: NetlifyFunctionsPluginOptions): Promise<void> {
|
||||||
app: App,
|
|
||||||
options: NetlifyFunctionsPluginOptions
|
|
||||||
): Promise<void> => {
|
|
||||||
const { directory } = options
|
const { directory } = options
|
||||||
const { source, dest } = directory
|
const { source, dest } = directory
|
||||||
const userSource = source[0]
|
const userSource = source[0]
|
||||||
@ -25,11 +22,9 @@ export const generateFunctions = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialFunctions = async (
|
export async function initialFunctions(app: App, options: NetlifyFunctionsPluginOptions): Promise<void> {
|
||||||
app: App,
|
if (!app.env.isDev)
|
||||||
options: NetlifyFunctionsPluginOptions
|
return
|
||||||
): Promise<void> => {
|
|
||||||
if (!app.env.isDev) return
|
|
||||||
const { directory } = options
|
const { directory } = options
|
||||||
const { source, temp } = directory
|
const { source, temp } = directory
|
||||||
const userSource = source[0]
|
const userSource = source[0]
|
||||||
@ -47,10 +42,7 @@ export const initialFunctions = async (
|
|||||||
watchFunctions(app, options)
|
watchFunctions(app, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const watchFunctions = (
|
export function watchFunctions(app: App, { directory }: NetlifyFunctionsPluginOptions): void {
|
||||||
app: App,
|
|
||||||
{ directory }: NetlifyFunctionsPluginOptions
|
|
||||||
): void => {
|
|
||||||
const { source, temp } = directory
|
const { source, temp } = directory
|
||||||
const userSource = source[0]
|
const userSource = source[0]
|
||||||
const watcher = chokidar.watch('**/*.ts', {
|
const watcher = chokidar.watch('**/*.ts', {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import process from 'node:process'
|
||||||
import type { JsonMap } from '@iarna/toml'
|
import type { JsonMap } from '@iarna/toml'
|
||||||
import { parse, stringify } from '@iarna/toml'
|
import { parse, stringify } from '@iarna/toml'
|
||||||
import type { App } from '@vuepress/core'
|
import type { App } from '@vuepress/core'
|
||||||
@ -11,39 +12,32 @@ export interface NetlifyConfig {
|
|||||||
|
|
||||||
const configName = 'netlify.toml'
|
const configName = 'netlify.toml'
|
||||||
|
|
||||||
const readConfig = (filepath: string): NetlifyConfig => {
|
function readConfig(filepath: string): NetlifyConfig {
|
||||||
let netlifyConfig = ''
|
let netlifyConfig = ''
|
||||||
if (fs.existsSync(filepath)) {
|
if (fs.existsSync(filepath))
|
||||||
netlifyConfig = fs.readFileSync(filepath, 'utf-8') || ''
|
netlifyConfig = fs.readFileSync(filepath, 'utf-8') || ''
|
||||||
}
|
|
||||||
return (parse(netlifyConfig) as unknown as NetlifyConfig) || {}
|
return (parse(netlifyConfig) as unknown as NetlifyConfig) || {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const writeConfig = (filepath: string, netlifyConfig: NetlifyConfig): void => {
|
function writeConfig(filepath: string, netlifyConfig: NetlifyConfig): void {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
filepath,
|
filepath,
|
||||||
stringify(netlifyConfig as unknown as JsonMap),
|
stringify(netlifyConfig as unknown as JsonMap),
|
||||||
'utf-8'
|
'utf-8',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolveFunctions = (
|
function resolveFunctions(config: NetlifyConfig, { directory }: NetlifyFunctionsPluginOptions, app: App): void {
|
||||||
config: NetlifyConfig,
|
|
||||||
{ directory }: NetlifyFunctionsPluginOptions,
|
|
||||||
app: App
|
|
||||||
): void => {
|
|
||||||
const functions = (config.functions = config.functions || {})
|
const functions = (config.functions = config.functions || {})
|
||||||
functions.directory =
|
functions.directory
|
||||||
functions.directory || path.relative(app.dir.dest('../'), directory.dest)
|
= functions.directory || path.relative(app.dir.dest('../'), directory.dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolveRedirects = (
|
function resolveRedirects(config: NetlifyConfig, { proxyPrefix }: NetlifyFunctionsPluginOptions): void {
|
||||||
config: NetlifyConfig,
|
const funcDir = `/${(config.functions.directory || '').replace(/^\//, '')}`
|
||||||
{ proxyPrefix }: NetlifyFunctionsPluginOptions
|
|
||||||
): void => {
|
|
||||||
const funcDir = '/' + (config.functions.directory || '').replace(/^\//, '')
|
|
||||||
const redirects = (config.redirects = config.redirects || [])
|
const redirects = (config.redirects = config.redirects || [])
|
||||||
if (!redirects.some((redirect) => redirect?.to?.startsWith(funcDir))) {
|
if (!redirects.some(redirect => redirect?.to?.startsWith(funcDir))) {
|
||||||
redirects.push({
|
redirects.push({
|
||||||
from: path.join('/', proxyPrefix, '*'),
|
from: path.join('/', proxyPrefix, '*'),
|
||||||
to: path.join(funcDir, ':splat'),
|
to: path.join(funcDir, ':splat'),
|
||||||
@ -56,10 +50,7 @@ const resolveRedirects = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateNetlifyConfig = (
|
export function generateNetlifyConfig(app: App, options: NetlifyFunctionsPluginOptions): NetlifyConfig {
|
||||||
app: App,
|
|
||||||
options: NetlifyFunctionsPluginOptions
|
|
||||||
): NetlifyConfig => {
|
|
||||||
const configPath = path.join(process.cwd(), configName)
|
const configPath = path.join(process.cwd(), configName)
|
||||||
const config = readConfig(configPath)
|
const config = readConfig(configPath)
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { Buffer } from 'node:buffer'
|
||||||
|
import process from 'node:process'
|
||||||
import { fs, getDirname, path } from '@vuepress/utils'
|
import { fs, getDirname, path } from '@vuepress/utils'
|
||||||
import dotenv from 'dotenv'
|
import dotenv from 'dotenv'
|
||||||
import { execa } from 'execa'
|
import { execa } from 'execa'
|
||||||
@ -6,15 +8,16 @@ import type { NetlifyFunctionsPluginOptions } from '../../shared/index.js'
|
|||||||
|
|
||||||
const __dirname = getDirname(import.meta.url)
|
const __dirname = getDirname(import.meta.url)
|
||||||
|
|
||||||
const loadEnvConfig = (): Record<string, string | undefined> => {
|
function loadEnvConfig(): Record<string, string | undefined> {
|
||||||
const configPath = path.resolve(process.cwd(), '.env')
|
const configPath = path.resolve(process.cwd(), '.env')
|
||||||
if (!fs.existsSync(configPath)) {
|
if (!fs.existsSync(configPath))
|
||||||
return {}
|
return {}
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const content = fs.readFileSync(configPath, 'utf-8')
|
const content = fs.readFileSync(configPath, 'utf-8')
|
||||||
return dotenv.parse(Buffer.from(content))
|
return dotenv.parse(Buffer.from(content))
|
||||||
} catch {
|
}
|
||||||
|
catch {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -23,15 +26,15 @@ export interface NetlifyServe {
|
|||||||
host: string
|
host: string
|
||||||
close: () => void
|
close: () => void
|
||||||
}
|
}
|
||||||
export const netlifyServe = async ({
|
export async function netlifyServe({
|
||||||
directory,
|
directory,
|
||||||
}: NetlifyFunctionsPluginOptions): Promise<NetlifyServe> => {
|
}: NetlifyFunctionsPluginOptions): Promise<NetlifyServe> {
|
||||||
const port = await portFinder.getPortPromise({ port: 9000 })
|
const port = await portFinder.getPortPromise({ port: 9000 })
|
||||||
|
|
||||||
const argv = [
|
const argv = [
|
||||||
'functions:serve',
|
'functions:serve',
|
||||||
'--port',
|
'--port',
|
||||||
port + '',
|
`${port}`,
|
||||||
'--functions',
|
'--functions',
|
||||||
path.join('./', path.relative(process.cwd(), directory.temp)),
|
path.join('./', path.relative(process.cwd(), directory.temp)),
|
||||||
// '--debug',
|
// '--debug',
|
||||||
@ -45,12 +48,12 @@ export const netlifyServe = async ({
|
|||||||
env: {
|
env: {
|
||||||
...loadEnvConfig(),
|
...loadEnvConfig(),
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
stdout?.pipe(process.stdout)
|
stdout?.pipe(process.stdout)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
host: 'http://localhost:' + port,
|
host: `http://localhost:${port}`,
|
||||||
close: () => cancel(),
|
close: () => cancel(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,27 +39,26 @@ import {
|
|||||||
netlifyServe,
|
netlifyServe,
|
||||||
} from './netlify/index.js'
|
} from './netlify/index.js'
|
||||||
|
|
||||||
const initOptions = (
|
function initOptions(app: App, {
|
||||||
app: App,
|
|
||||||
{
|
|
||||||
sourceDirectory,
|
sourceDirectory,
|
||||||
destDirectory,
|
destDirectory,
|
||||||
proxyPrefix = '/api',
|
proxyPrefix = '/api',
|
||||||
}: NetlifyFunctionsOptions
|
}: NetlifyFunctionsOptions): NetlifyFunctionsPluginOptions {
|
||||||
): NetlifyFunctionsPluginOptions => ({
|
return {
|
||||||
directory: {
|
directory: {
|
||||||
source: [sourceDirectory || app.dir.source('.vuepress/functions')],
|
source: [sourceDirectory || app.dir.source('.vuepress/functions')],
|
||||||
dest: destDirectory || app.dir.dest('functions'),
|
dest: destDirectory || app.dir.dest('functions'),
|
||||||
temp: app.dir.temp('functions'),
|
temp: app.dir.temp('functions'),
|
||||||
},
|
},
|
||||||
proxyPrefix,
|
proxyPrefix,
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const cache = {
|
const cache = {
|
||||||
options: {},
|
options: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getOptions = (): NetlifyFunctionsPluginOptions => {
|
export function getOptions(): NetlifyFunctionsPluginOptions {
|
||||||
return cache.options as NetlifyFunctionsPluginOptions
|
return cache.options as NetlifyFunctionsPluginOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,11 +67,8 @@ export const getOptions = (): NetlifyFunctionsPluginOptions => {
|
|||||||
* netlify function 插件,提供 netlify functions 支持
|
* netlify function 插件,提供 netlify functions 支持
|
||||||
*
|
*
|
||||||
* @param options
|
* @param options
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
export const netlifyFunctionsPlugin = (
|
export function netlifyFunctionsPlugin(options: NetlifyFunctionsOptions = {}): Plugin {
|
||||||
options: NetlifyFunctionsOptions = {}
|
|
||||||
): Plugin => {
|
|
||||||
return (app: App) => {
|
return (app: App) => {
|
||||||
const opts = initOptions(app, options)
|
const opts = initOptions(app, options)
|
||||||
let server: NetlifyServe
|
let server: NetlifyServe
|
||||||
|
|||||||
@ -19,27 +19,24 @@ interface UseNetlifyFunctionResult {
|
|||||||
*/
|
*/
|
||||||
generatePluginFunctions: () => void
|
generatePluginFunctions: () => void
|
||||||
}
|
}
|
||||||
export const useNetlifyFunctionsPlugin = (
|
export function useNetlifyFunctionsPlugin(app: App, options: UseNetlifyFunctionPluginsOptions): UseNetlifyFunctionResult {
|
||||||
app: App,
|
if (typeof options === 'undefined')
|
||||||
options: UseNetlifyFunctionPluginsOptions
|
|
||||||
): UseNetlifyFunctionResult => {
|
|
||||||
if (typeof options === 'undefined') {
|
|
||||||
throw new Error('useNetlifyFunctionsPlugin [options] argument not found.')
|
throw new Error('useNetlifyFunctionsPlugin [options] argument not found.')
|
||||||
}
|
|
||||||
if (typeof options.directory !== 'string' || !options.directory) {
|
if (typeof options.directory !== 'string' || !options.directory) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`useNetlifyFunctionsPlugin [options.directory] must be a string\n exp: path.join(__dirname, 'functions')`
|
`useNetlifyFunctionsPlugin [options.directory] must be a string\n exp: path.join(__dirname, 'functions')`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const plugins = app.pluginApi.plugins
|
const plugins = app.pluginApi.plugins
|
||||||
if (
|
if (
|
||||||
!plugins.some(
|
!plugins.some(
|
||||||
(plugin: PluginObject) =>
|
(plugin: PluginObject) =>
|
||||||
plugin.name === 'vuepress-plugin-netlify-functions'
|
plugin.name === 'vuepress-plugin-netlify-functions',
|
||||||
|
)
|
||||||
)
|
)
|
||||||
) {
|
|
||||||
app.use(netlifyFunctionsPlugin())
|
app.use(netlifyFunctionsPlugin())
|
||||||
}
|
|
||||||
const { proxyPrefix, directory } = getOptions()
|
const { proxyPrefix, directory } = getOptions()
|
||||||
const source = path.join(options.directory, '**/*.js')
|
const source = path.join(options.directory, '**/*.js')
|
||||||
|
|
||||||
|
|||||||
@ -1,19 +1,17 @@
|
|||||||
import { fs, path } from '@vuepress/utils'
|
import { fs, path } from '@vuepress/utils'
|
||||||
|
|
||||||
export const readFileList = (
|
export function readFileList(source: string, fileList: string[] = []): string[] {
|
||||||
source: string,
|
if (!fs.existsSync(source))
|
||||||
fileList: string[] = []
|
return []
|
||||||
): string[] => {
|
|
||||||
if (!fs.existsSync(source)) return []
|
|
||||||
const files = fs.readdirSync(source)
|
const files = fs.readdirSync(source)
|
||||||
files.forEach((file: string) => {
|
files.forEach((file: string) => {
|
||||||
const filepath = path.join(source, file)
|
const filepath = path.join(source, file)
|
||||||
const stat = fs.statSync(filepath)
|
const stat = fs.statSync(filepath)
|
||||||
if (stat.isDirectory()) {
|
if (stat.isDirectory()) {
|
||||||
if (file !== 'node_modules') {
|
if (file !== 'node_modules')
|
||||||
readFileList(filepath, fileList)
|
readFileList(filepath, fileList)
|
||||||
}
|
}
|
||||||
} else {
|
else {
|
||||||
fileList.push(filepath)
|
fileList.push(filepath)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -20,7 +20,7 @@ module.exports = {
|
|||||||
## Options
|
## Options
|
||||||
|
|
||||||
``` ts
|
``` ts
|
||||||
type NotesDataOptions = {
|
interface NotesDataOptions {
|
||||||
dir: string
|
dir: string
|
||||||
link: string
|
link: string
|
||||||
include?: string | string[]
|
include?: string | string[]
|
||||||
@ -28,7 +28,7 @@ type NotesDataOptions = {
|
|||||||
notes: NotesItem[]
|
notes: NotesItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotesItem = {
|
interface NotesItem {
|
||||||
dir: string
|
dir: string
|
||||||
link: string
|
link: string
|
||||||
text: string
|
text: string
|
||||||
@ -37,7 +37,7 @@ type NotesItem = {
|
|||||||
|
|
||||||
type NotesSidebar = (NotesSidebarItem | string)[]
|
type NotesSidebar = (NotesSidebarItem | string)[]
|
||||||
|
|
||||||
type NotesSidebarItem = {
|
interface NotesSidebarItem {
|
||||||
text?: string
|
text?: string
|
||||||
link?: string
|
link?: string
|
||||||
dir?: string
|
dir?: string
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "@vuepress-plume/plugin-notes-data",
|
"name": "@vuepress-plume/plugin-notes-data",
|
||||||
|
"type": "module",
|
||||||
"version": "1.0.0-rc.5",
|
"version": "1.0.0-rc.5",
|
||||||
"description": "The Plugin for VuePres 2",
|
"description": "The Plugin for VuePres 2",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"bugs": {
|
||||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
"type": "module",
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./lib/node/index.js",
|
".": "./lib/node/index.js",
|
||||||
"./client": "./lib/client/index.js",
|
"./client": "./lib/client/index.js",
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export default defineClientConfig({
|
|||||||
value: notesData.value,
|
value: notesData.value,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -9,9 +9,11 @@ export type NotesDataRef<T extends NotesData = NotesData> = Ref<T>
|
|||||||
|
|
||||||
export const notesData: NotesDataRef = ref(notesDataRaw)
|
export const notesData: NotesDataRef = ref(notesDataRaw)
|
||||||
|
|
||||||
export const useNotesData = <
|
export function useNotesData<
|
||||||
T extends NotesData = NotesData
|
T extends NotesData = NotesData,
|
||||||
>(): NotesDataRef<T> => notesData as NotesDataRef<T>
|
>(): NotesDataRef<T> {
|
||||||
|
return notesData as NotesDataRef<T>
|
||||||
|
}
|
||||||
|
|
||||||
if (import.meta.webpackHot || import.meta.hot) {
|
if (import.meta.webpackHot || import.meta.hot) {
|
||||||
__VUE_HMR_RUNTIME__.updateNotesData = (data: NotesData) => {
|
__VUE_HMR_RUNTIME__.updateNotesData = (data: NotesData) => {
|
||||||
|
|||||||
@ -3,13 +3,13 @@ import { getDirname, path } from '@vuepress/utils'
|
|||||||
import type { NotesDataOptions } from '../shared/index.js'
|
import type { NotesDataOptions } from '../shared/index.js'
|
||||||
import { prepareNotesData, watchNotesData } from './prepareNotesData.js'
|
import { prepareNotesData, watchNotesData } from './prepareNotesData.js'
|
||||||
|
|
||||||
export const notesDataPlugin = (options: NotesDataOptions): Plugin => {
|
export function notesDataPlugin(options: NotesDataOptions): Plugin {
|
||||||
return (app: App) => {
|
return (app: App) => {
|
||||||
return {
|
return {
|
||||||
name: '@vuepress-plume/plugin-notes-data',
|
name: '@vuepress-plume/plugin-notes-data',
|
||||||
clientConfigFile: path.resolve(
|
clientConfigFile: path.resolve(
|
||||||
getDirname(import.meta.url),
|
getDirname(import.meta.url),
|
||||||
'../client/clientConfig.js'
|
'../client/clientConfig.js',
|
||||||
),
|
),
|
||||||
onPrepared: () => prepareNotesData(app, options),
|
onPrepared: () => prepareNotesData(app, options),
|
||||||
onWatched: (app, watchers) => watchNotesData(app, watchers, options),
|
onWatched: (app, watchers) => watchNotesData(app, watchers, options),
|
||||||
|
|||||||
@ -32,11 +32,9 @@ interface NotePage {
|
|||||||
link: string
|
link: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const prepareNotesData = async (
|
export async function prepareNotesData(app: App, { include, exclude, notes, dir, link }: NotesDataOptions) {
|
||||||
app: App,
|
if (!notes || notes.length === 0)
|
||||||
{ include, exclude, notes, dir, link }: NotesDataOptions
|
return
|
||||||
) => {
|
|
||||||
if (!notes || notes.length === 0) return
|
|
||||||
dir = normalizePath(dir)
|
dir = normalizePath(dir)
|
||||||
const filter = createFilter(ensureArray(include), ensureArray(exclude), {
|
const filter = createFilter(ensureArray(include), ensureArray(exclude), {
|
||||||
resolve: false,
|
resolve: false,
|
||||||
@ -44,10 +42,10 @@ export const prepareNotesData = async (
|
|||||||
const DIR_PATTERN = new RegExp(`^${normalizePath(path.join(dir, '/'))}`)
|
const DIR_PATTERN = new RegExp(`^${normalizePath(path.join(dir, '/'))}`)
|
||||||
const notesPageList: NotePage[] = app.pages
|
const notesPageList: NotePage[] = app.pages
|
||||||
.filter(
|
.filter(
|
||||||
(page) =>
|
page =>
|
||||||
page.filePathRelative &&
|
page.filePathRelative
|
||||||
page.filePathRelative.startsWith(dir) &&
|
&& page.filePathRelative.startsWith(dir)
|
||||||
filter(page.filePathRelative)
|
&& filter(page.filePathRelative),
|
||||||
)
|
)
|
||||||
.map((page) => {
|
.map((page) => {
|
||||||
return {
|
return {
|
||||||
@ -61,27 +59,23 @@ export const prepareNotesData = async (
|
|||||||
notes.forEach((note) => {
|
notes.forEach((note) => {
|
||||||
notesData[normalizePath(path.join('/', link, note.link))] = initSidebar(
|
notesData[normalizePath(path.join('/', link, note.link))] = initSidebar(
|
||||||
note,
|
note,
|
||||||
notesPageList.filter((page) =>
|
notesPageList.filter(page =>
|
||||||
page.relativePath.startsWith(note.dir.trim().replace(/^\/|\/$/g, ''))
|
page.relativePath.startsWith(note.dir.trim().replace(/^\/|\/$/g, '')),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
let content = `
|
let content = `
|
||||||
export const notesData = ${JSON.stringify(notesData, null, 2)}
|
export const notesData = ${JSON.stringify(notesData, null, 2)}
|
||||||
`
|
`
|
||||||
if (app.env.isDev) {
|
if (app.env.isDev)
|
||||||
content += HMR_CODE
|
content += HMR_CODE
|
||||||
}
|
|
||||||
|
|
||||||
await app.writeTemp('internal/notesData.js', content)
|
await app.writeTemp('internal/notesData.js', content)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const watchNotesData = (
|
export function watchNotesData(app: App, watchers: any[], options: NotesDataOptions): void {
|
||||||
app: App,
|
if (!options.notes || options.notes.length === 0 || !options.dir)
|
||||||
watchers: any[],
|
return
|
||||||
options: NotesDataOptions
|
|
||||||
): void => {
|
|
||||||
if (!options.notes || options.notes.length === 0 || !options.dir) return
|
|
||||||
const dir = path.join('pages', options.dir, '**/*')
|
const dir = path.join('pages', options.dir, '**/*')
|
||||||
const watcher = chokidar.watch(dir, {
|
const watcher = chokidar.watch(dir, {
|
||||||
cwd: app.dir.temp(),
|
cwd: app.dir.temp(),
|
||||||
@ -95,14 +89,16 @@ export const watchNotesData = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
function initSidebar(note: NotesItem, pages: NotePage[]): NotesSidebarItem[] {
|
function initSidebar(note: NotesItem, pages: NotePage[]): NotesSidebarItem[] {
|
||||||
if (!note.sidebar) return []
|
if (!note.sidebar)
|
||||||
if (note.sidebar === 'auto') return initSidebarByAuto(note, pages)
|
return []
|
||||||
|
if (note.sidebar === 'auto')
|
||||||
|
return initSidebarByAuto(note, pages)
|
||||||
return initSidebarByConfig(note, pages)
|
return initSidebarByConfig(note, pages)
|
||||||
}
|
}
|
||||||
|
|
||||||
function initSidebarByAuto(
|
function initSidebarByAuto(
|
||||||
note: NotesItem,
|
note: NotesItem,
|
||||||
pages: NotePage[]
|
pages: NotePage[],
|
||||||
): NotesSidebarItem[] {
|
): NotesSidebarItem[] {
|
||||||
pages = pages.sort((prev, next) => {
|
pages = pages.sort((prev, next) => {
|
||||||
const pi = prev.relativePath.match(/\//g)?.length || 0
|
const pi = prev.relativePath.match(/\//g)?.length || 0
|
||||||
@ -119,9 +115,10 @@ function initSidebarByAuto(
|
|||||||
let index = 0
|
let index = 0
|
||||||
let dir: string
|
let dir: string
|
||||||
let items = result
|
let items = result
|
||||||
|
// eslint-disable-next-line no-cond-assign
|
||||||
while ((dir = paths[index])) {
|
while ((dir = paths[index])) {
|
||||||
const text = dir.replace(/\.md$/, '')
|
const text = dir.replace(/\.md$/, '')
|
||||||
let current = items.find((item) => item.text === text)
|
let current = items.find(item => item.text === text)
|
||||||
if (!current) {
|
if (!current) {
|
||||||
current = { text, link: undefined, items: [] }
|
current = { text, link: undefined, items: [] }
|
||||||
!RE_INDEX.includes(dir) ? items.push(current) : items.unshift(current)
|
!RE_INDEX.includes(dir) ? items.push(current) : items.unshift(current)
|
||||||
@ -139,7 +136,7 @@ function initSidebarByAuto(
|
|||||||
|
|
||||||
function initSidebarByConfig(
|
function initSidebarByConfig(
|
||||||
{ text, dir, sidebar }: NotesItem,
|
{ text, dir, sidebar }: NotesItem,
|
||||||
pages: NotePage[]
|
pages: NotePage[],
|
||||||
): NotesSidebarItem[] {
|
): NotesSidebarItem[] {
|
||||||
return (sidebar as NotesSidebar).map((item) => {
|
return (sidebar as NotesSidebar).map((item) => {
|
||||||
if (typeof item === 'string') {
|
if (typeof item === 'string') {
|
||||||
@ -149,7 +146,8 @@ function initSidebarByConfig(
|
|||||||
link: current?.link,
|
link: current?.link,
|
||||||
items: [],
|
items: [],
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
const current = findNotePage(item.link || '', dir, pages)
|
const current = findNotePage(item.link || '', dir, pages)
|
||||||
return {
|
return {
|
||||||
text: item.text || item.dir || current?.title,
|
text: item.text || item.dir || current?.title,
|
||||||
@ -162,7 +160,7 @@ function initSidebarByConfig(
|
|||||||
sidebar: item.items,
|
sidebar: item.items,
|
||||||
dir: normalizePath(path.join(dir, item.dir || '')),
|
dir: normalizePath(path.join(dir, item.dir || '')),
|
||||||
},
|
},
|
||||||
pages
|
pages,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,23 +170,24 @@ function initSidebarByConfig(
|
|||||||
function findNotePage(
|
function findNotePage(
|
||||||
sidebar: string,
|
sidebar: string,
|
||||||
dir: string,
|
dir: string,
|
||||||
notePageList: NotePage[]
|
notePageList: NotePage[],
|
||||||
): NotePage | undefined {
|
): NotePage | undefined {
|
||||||
if (sidebar === '' || sidebar === 'README.md' || sidebar === 'index.md') {
|
if (sidebar === '' || sidebar === 'README.md' || sidebar === 'index.md') {
|
||||||
return notePageList.find((page) => {
|
return notePageList.find((page) => {
|
||||||
const relative = page.relativePath
|
const relative = page.relativePath
|
||||||
return (
|
return (
|
||||||
relative === normalizePath(path.join(dir, 'README.md')) ||
|
relative === normalizePath(path.join(dir, 'README.md'))
|
||||||
relative === normalizePath(path.join(dir, 'index.md'))
|
|| relative === normalizePath(path.join(dir, 'index.md'))
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return notePageList.find((page) => {
|
return notePageList.find((page) => {
|
||||||
const relative = page.relativePath
|
const relative = page.relativePath
|
||||||
return (
|
return (
|
||||||
relative === normalizePath(path.join(dir, sidebar)) ||
|
relative === normalizePath(path.join(dir, sidebar))
|
||||||
relative === normalizePath(path.join(dir, sidebar + '.md')) ||
|
|| relative === normalizePath(path.join(dir, `${sidebar}.md`))
|
||||||
page.link === sidebar
|
|| page.link === sidebar
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
export function ensureArray<T>(thing: T | T[] | null | undefined): T[] {
|
export function ensureArray<T>(thing: T | T[] | null | undefined): T[] {
|
||||||
if (Array.isArray(thing)) return thing
|
if (Array.isArray(thing))
|
||||||
if (thing === null || thing === undefined) return []
|
return thing
|
||||||
|
if (thing === null || thing === undefined)
|
||||||
|
return []
|
||||||
return [thing]
|
return [thing]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,11 +2,11 @@
|
|||||||
"extends": "../tsconfig.build.json",
|
"extends": "../tsconfig.build.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"outDir": "./lib",
|
|
||||||
"paths": {
|
"paths": {
|
||||||
"@internal/notesData": ["./src/client/notesData.d.ts"]
|
"@internal/notesData": ["./src/client/notesData.d.ts"]
|
||||||
},
|
},
|
||||||
"types": ["@vuepress/client/types", "vite/client", "webpack-env"]
|
"types": ["@vuepress/client/types", "vite/client", "webpack-env"],
|
||||||
|
"outDir": "./lib"
|
||||||
},
|
},
|
||||||
"include": ["./src"]
|
"include": ["./src"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "plugin-page-collection",
|
"name": "plugin-page-collection",
|
||||||
|
"type": "module",
|
||||||
"version": "1.0.0-rc.5",
|
"version": "1.0.0-rc.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "The Plugin for VuePres 2",
|
"description": "The Plugin for VuePres 2",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"bugs": {
|
||||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
"type": "module",
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./lib/node/index.js",
|
".": "./lib/node/index.js",
|
||||||
"./client": "./lib/client/index.js",
|
"./client": "./lib/client/index.js",
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import Collection from './components/PageCollection.js'
|
|||||||
|
|
||||||
export default defineClientConfig({
|
export default defineClientConfig({
|
||||||
enhance({ app }) {
|
enhance({ app }) {
|
||||||
// eslint-disable-next-line vue/match-component-file-name
|
|
||||||
app.component('PageCollection', Collection)
|
app.component('PageCollection', Collection)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export default defineComponent({
|
|||||||
{
|
{
|
||||||
class: 'page-collection',
|
class: 'page-collection',
|
||||||
},
|
},
|
||||||
`阅读数:${collection.visitCount}`
|
`阅读数:${collection.visitCount}`,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -14,7 +14,7 @@ interface ResponseData {
|
|||||||
result: Record<string, any>
|
result: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchCollection = async (url: string): Promise<PageCollection> => {
|
async function fetchCollection(url: string): Promise<PageCollection> {
|
||||||
// 发起 netlify functions 请求
|
// 发起 netlify functions 请求
|
||||||
// 你已经注意到,接口名就是在 node/functions 目录下的 文件名
|
// 你已经注意到,接口名就是在 node/functions 目录下的 文件名
|
||||||
const response = await fetch(`${prefix}/page_collection`, {
|
const response = await fetch(`${prefix}/page_collection`, {
|
||||||
@ -24,14 +24,15 @@ const fetchCollection = async (url: string): Promise<PageCollection> => {
|
|||||||
const result = (await response.json()) as unknown as ResponseData
|
const result = (await response.json()) as unknown as ResponseData
|
||||||
if (result.code === 200) {
|
if (result.code === 200) {
|
||||||
return (result.result || {}) as PageCollection
|
return (result.result || {}) as PageCollection
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return {
|
return {
|
||||||
visitCount: 0,
|
visitCount: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const usePageCollection = (): PageCollection => {
|
export function usePageCollection(): PageCollection {
|
||||||
const collection = reactive({
|
const collection = reactive({
|
||||||
visitCount: 0,
|
visitCount: 0,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import Collection from './components/PageCollection.js'
|
import Collection from './components/PageCollection.js'
|
||||||
|
|
||||||
export * from '../shared/index.js'
|
export * from '../shared/index.js'
|
||||||
|
|
||||||
export { Collection }
|
export { Collection }
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
* 那么客户端发起的请求接口应该是 {host}:{port}/{proxyPrefix}/page_collection
|
* 那么客户端发起的请求接口应该是 {host}:{port}/{proxyPrefix}/page_collection
|
||||||
*/
|
*/
|
||||||
// 引入这个包来提供类型支持
|
// 引入这个包来提供类型支持
|
||||||
|
import process from 'node:process'
|
||||||
import type { Handler } from '@netlify/functions'
|
import type { Handler } from '@netlify/functions'
|
||||||
import * as lean from 'leancloud-storage'
|
import * as lean from 'leancloud-storage'
|
||||||
|
|
||||||
@ -24,11 +25,7 @@ interface ResponseRes {
|
|||||||
message?: string
|
message?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = (
|
function response(code: number, message: string, data?: Record<string, any>): ResponseRes {
|
||||||
code: number,
|
|
||||||
message: string,
|
|
||||||
data?: Record<string, any>
|
|
||||||
): ResponseRes => {
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@ -39,11 +36,11 @@ const response = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const successRes = (data: Record<string, any>): ResponseRes => {
|
function successRes(data: Record<string, any>): ResponseRes {
|
||||||
return response(200, 'success', data)
|
return response(200, 'success', data)
|
||||||
}
|
}
|
||||||
|
|
||||||
const errorRes = (message: string, code = 500): ResponseRes => {
|
function errorRes(message: string, code = 500): ResponseRes {
|
||||||
return response(code, message)
|
return response(code, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,12 +52,11 @@ const useMasterKey = Boolean(process.env.LEAN_CLOUD_MASTER_KEY)
|
|||||||
// netlify functions 的 functions 格式规范
|
// netlify functions 的 functions 格式规范
|
||||||
// 通过导出一个 handler 函数作为 钩子
|
// 通过导出一个 handler 函数作为 钩子
|
||||||
// event 中包含了 请求相关的信息,你可以通过 console.log(event) 查看具体信息
|
// event 中包含了 请求相关的信息,你可以通过 console.log(event) 查看具体信息
|
||||||
export const handler: Handler = async (event, context) => {
|
export const handler: Handler = async (event) => {
|
||||||
// body 即为 请求体
|
// body 即为 请求体
|
||||||
const { url } = JSON.parse(event.body || '') || {}
|
const { url } = JSON.parse(event.body || '') || {}
|
||||||
if (!url) {
|
if (!url)
|
||||||
return errorRes('params [url] not found')
|
return errorRes('params [url] not found')
|
||||||
}
|
|
||||||
|
|
||||||
// 出于安全考虑,你可能还需要在这里做 域名请求白名单的校验
|
// 出于安全考虑,你可能还需要在这里做 域名请求白名单的校验
|
||||||
// 可以在 event.headers 中拿到相关 域名信息
|
// 可以在 event.headers 中拿到相关 域名信息
|
||||||
@ -72,11 +68,12 @@ export const handler: Handler = async (event, context) => {
|
|||||||
query.equalTo('url', url)
|
query.equalTo('url', url)
|
||||||
const current = await query.first({ useMasterKey })
|
const current = await query.first({ useMasterKey })
|
||||||
let page: lean.Object
|
let page: lean.Object
|
||||||
if (current) {
|
if (current)
|
||||||
page = lean.Object.createWithoutData('Page', current.get('objectId'))
|
page = lean.Object.createWithoutData('Page', current.get('objectId'))
|
||||||
} else {
|
|
||||||
|
else
|
||||||
page = new Page()
|
page = new Page()
|
||||||
}
|
|
||||||
page.increment('visitCount', 1)
|
page.increment('visitCount', 1)
|
||||||
page = await page.save(null, {
|
page = await page.save(null, {
|
||||||
useMasterKey,
|
useMasterKey,
|
||||||
@ -85,7 +82,8 @@ export const handler: Handler = async (event, context) => {
|
|||||||
return successRes({
|
return successRes({
|
||||||
visitCount: page.get('visitCount'),
|
visitCount: page.get('visitCount'),
|
||||||
})
|
})
|
||||||
} catch (e: any) {
|
}
|
||||||
|
catch (e: any) {
|
||||||
return errorRes(e.message, e.code || e.status || e.statusCode)
|
return errorRes(e.message, e.code || e.status || e.statusCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,9 +13,7 @@ import type { PageCollectionOptions } from '../shared/index.js'
|
|||||||
|
|
||||||
const __dirname = getDirname(import.meta.url)
|
const __dirname = getDirname(import.meta.url)
|
||||||
|
|
||||||
export const pageCollectionPlugin = (
|
export function pageCollectionPlugin(_options: PageCollectionOptions = {}): Plugin {
|
||||||
options: PageCollectionOptions = {}
|
|
||||||
): Plugin => {
|
|
||||||
return (app: App) => {
|
return (app: App) => {
|
||||||
const {
|
const {
|
||||||
// 客户端发起 functions 请求时的代理前缀
|
// 客户端发起 functions 请求时的代理前缀
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export type PageCollectionOptions = Record<string, unknown>;
|
export type PageCollectionOptions = Record<string, unknown>
|
||||||
|
|||||||
@ -63,5 +63,4 @@ interface ShikijiOptions {
|
|||||||
*/
|
*/
|
||||||
codeTransformers?: ShikijiTransformer[]
|
codeTransformers?: ShikijiTransformer[]
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "@vuepress-plume/plugin-shikiji",
|
"name": "@vuepress-plume/plugin-shikiji",
|
||||||
|
"type": "module",
|
||||||
"version": "1.0.0-rc.5",
|
"version": "1.0.0-rc.5",
|
||||||
"description": "The Plugin for VuePres 2",
|
"description": "The Plugin for VuePres 2",
|
||||||
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"bugs": {
|
||||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
"type": "module",
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./lib/node/index.js",
|
".": "./lib/node/index.js",
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
|
|||||||
@ -7,13 +7,13 @@ import {
|
|||||||
bundledLanguages,
|
bundledLanguages,
|
||||||
getHighlighter,
|
getHighlighter,
|
||||||
isPlaintext as isPlainLang,
|
isPlaintext as isPlainLang,
|
||||||
isSpecialLang
|
isSpecialLang,
|
||||||
} from 'shikiji'
|
} from 'shikiji'
|
||||||
import {
|
import {
|
||||||
transformerNotationDiff,
|
transformerNotationDiff,
|
||||||
transformerNotationErrorLevel,
|
transformerNotationErrorLevel,
|
||||||
transformerNotationFocus,
|
transformerNotationFocus,
|
||||||
transformerNotationHighlight
|
transformerNotationHighlight,
|
||||||
} from 'shikiji-transformers'
|
} from 'shikiji-transformers'
|
||||||
import type { HighlighterOptions, ThemeOptions } from './types.js'
|
import type { HighlighterOptions, ThemeOptions } from './types.js'
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ export async function highlight(
|
|||||||
): Promise<(str: string, lang: string, attrs: string) => string> {
|
): Promise<(str: string, lang: string, attrs: string) => string> {
|
||||||
const {
|
const {
|
||||||
defaultHighlightLang: defaultLang = '',
|
defaultHighlightLang: defaultLang = '',
|
||||||
codeTransformers: userTransformers = []
|
codeTransformers: userTransformers = [],
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
const highlighter = await getHighlighter({
|
const highlighter = await getHighlighter({
|
||||||
@ -34,7 +34,7 @@ export async function highlight(
|
|||||||
? [theme]
|
? [theme]
|
||||||
: [theme.light, theme.dark],
|
: [theme.light, theme.dark],
|
||||||
langs: [...Object.keys(bundledLanguages), ...(options.languages || [])],
|
langs: [...Object.keys(bundledLanguages), ...(options.languages || [])],
|
||||||
langAlias: options.languageAlias
|
langAlias: options.languageAlias,
|
||||||
})
|
})
|
||||||
|
|
||||||
await options?.shikijiSetup?.(highlighter)
|
await options?.shikijiSetup?.(highlighter)
|
||||||
@ -43,7 +43,7 @@ export async function highlight(
|
|||||||
transformerNotationDiff(),
|
transformerNotationDiff(),
|
||||||
transformerNotationFocus({
|
transformerNotationFocus({
|
||||||
classActiveLine: 'has-focus',
|
classActiveLine: 'has-focus',
|
||||||
classActivePre: 'has-focused-lines'
|
classActivePre: 'has-focused-lines',
|
||||||
}),
|
}),
|
||||||
transformerNotationHighlight(),
|
transformerNotationHighlight(),
|
||||||
transformerNotationErrorLevel(),
|
transformerNotationErrorLevel(),
|
||||||
@ -51,15 +51,15 @@ export async function highlight(
|
|||||||
name: 'vuepress:add-class',
|
name: 'vuepress:add-class',
|
||||||
pre(node) {
|
pre(node) {
|
||||||
addClassToHast(node, 'vp-code')
|
addClassToHast(node, 'vp-code')
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'vuepress:clean-up',
|
name: 'vuepress:clean-up',
|
||||||
pre(node) {
|
pre(node) {
|
||||||
delete node.properties.tabindex
|
delete node.properties.tabindex
|
||||||
delete node.properties.style
|
delete node.properties.style
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const vueRE = /-vue$/
|
const vueRE = /-vue$/
|
||||||
@ -69,8 +69,8 @@ export async function highlight(
|
|||||||
|
|
||||||
return (str: string, lang: string, attrs: string) => {
|
return (str: string, lang: string, attrs: string) => {
|
||||||
const vPre = vueRE.test(lang) ? '' : 'v-pre'
|
const vPre = vueRE.test(lang) ? '' : 'v-pre'
|
||||||
lang =
|
|
||||||
lang
|
lang
|
||||||
|
= lang
|
||||||
.replace(lineNoStartRE, '')
|
.replace(lineNoStartRE, '')
|
||||||
.replace(lineNoRE, '')
|
.replace(lineNoRE, '')
|
||||||
.replace(vueRE, '')
|
.replace(vueRE, '')
|
||||||
@ -82,8 +82,8 @@ export async function highlight(
|
|||||||
logger.warn(
|
logger.warn(
|
||||||
c.yellow(
|
c.yellow(
|
||||||
`\nThe language '${lang}' is not loaded, falling back to '${defaultLang || 'txt'
|
`\nThe language '${lang}' is not loaded, falling back to '${defaultLang || 'txt'
|
||||||
}' for syntax highlighting.`
|
}' for syntax highlighting.`,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
lang = defaultLang
|
lang = defaultLang
|
||||||
}
|
}
|
||||||
@ -94,7 +94,8 @@ export async function highlight(
|
|||||||
const mustaches = new Map<string, string>()
|
const mustaches = new Map<string, string>()
|
||||||
|
|
||||||
const removeMustache = (s: string) => {
|
const removeMustache = (s: string) => {
|
||||||
if (vPre) return s
|
if (vPre)
|
||||||
|
return s
|
||||||
return s.replace(mustacheRE, (match) => {
|
return s.replace(mustacheRE, (match) => {
|
||||||
let marker = mustaches.get(match)
|
let marker = mustaches.get(match)
|
||||||
if (!marker) {
|
if (!marker) {
|
||||||
@ -113,10 +114,10 @@ export async function highlight(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fillEmptyHighlightedLine = (s: string) => {
|
const fillEmptyHighlightedLine = (s: string) => {
|
||||||
return s.replace(
|
return `${s.replace(
|
||||||
/(<span class="line highlighted">)(<\/span>)/g,
|
/(<span class="line highlighted">)(<\/span>)/g,
|
||||||
'$1<wbr>$2'
|
'$1<wbr>$2',
|
||||||
).replace(/(\/\/\s*?\[)\\(!code.*?\])/g, '$1$2') + '\n'
|
).replace(/(\/\/\s*?\[)\\(!code.*?\])/g, '$1$2')}\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
str = removeMustache(str).trimEnd()
|
str = removeMustache(str).trimEnd()
|
||||||
@ -125,17 +126,17 @@ export async function highlight(
|
|||||||
lang,
|
lang,
|
||||||
transformers: [
|
transformers: [
|
||||||
...transformers,
|
...transformers,
|
||||||
...userTransformers
|
...userTransformers,
|
||||||
],
|
],
|
||||||
meta: {
|
meta: {
|
||||||
__raw: attrs
|
__raw: attrs,
|
||||||
},
|
},
|
||||||
...(typeof theme === 'string' || 'name' in theme
|
...(typeof theme === 'string' || 'name' in theme
|
||||||
? { theme }
|
? { theme }
|
||||||
: {
|
: {
|
||||||
themes: theme,
|
themes: theme,
|
||||||
defaultColor: false
|
defaultColor: false,
|
||||||
})
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
return fillEmptyHighlightedLine(restoreMustache(highlighted))
|
return fillEmptyHighlightedLine(restoreMustache(highlighted))
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import type { Plugin } from '@vuepress/core'
|
import type { Plugin } from '@vuepress/core'
|
||||||
import { highlight } from './highlight.js'
|
import { highlight } from './highlight.js'
|
||||||
import type { HighlighterOptions } from './types'
|
import type { HighlighterOptions } from './types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options of @vuepress/plugin-shiki
|
* Options of @vuepress/plugin-shiki
|
||||||
*/
|
*/
|
||||||
export type ShikijiPluginOptions = HighlighterOptions
|
export type ShikijiPluginOptions = HighlighterOptions
|
||||||
|
|
||||||
export const shikijiPlugin = (
|
export function shikijiPlugin(options: ShikijiPluginOptions = {}): Plugin {
|
||||||
options: ShikijiPluginOptions = {}): Plugin => ({
|
return {
|
||||||
name: '@vuepress-plume/plugin-shikiji',
|
name: '@vuepress-plume/plugin-shikiji',
|
||||||
|
|
||||||
extendsMarkdown: async (md) => {
|
extendsMarkdown: async (md) => {
|
||||||
@ -16,4 +17,5 @@ export const shikijiPlugin = (
|
|||||||
|
|
||||||
md.options.highlight = highlighter
|
md.options.highlight = highlighter
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import type {
|
|||||||
ShikijiTransformer,
|
ShikijiTransformer,
|
||||||
ThemeRegistration,
|
ThemeRegistration,
|
||||||
} from 'shikiji'
|
} from 'shikiji'
|
||||||
|
|
||||||
export type ThemeOptions =
|
export type ThemeOptions =
|
||||||
| ThemeRegistration
|
| ThemeRegistration
|
||||||
| BuiltinTheme
|
| BuiltinTheme
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": true
|
"composite": true
|
||||||
},
|
},
|
||||||
"files": [],
|
|
||||||
"references": [
|
"references": [
|
||||||
{ "path": "./plugin-auto-frontmatter/tsconfig.build.json" },
|
{ "path": "./plugin-auto-frontmatter/tsconfig.build.json" },
|
||||||
{ "path": "./plugin-baidu-tongji/tsconfig.build.json" },
|
{ "path": "./plugin-baidu-tongji/tsconfig.build.json" },
|
||||||
@ -15,5 +14,6 @@
|
|||||||
{ "path": "./plugin-notes-data/tsconfig.build.json" },
|
{ "path": "./plugin-notes-data/tsconfig.build.json" },
|
||||||
{ "path": "./plugin-page-collection/tsconfig.build.json" },
|
{ "path": "./plugin-page-collection/tsconfig.build.json" },
|
||||||
{ "path": "./plugin-shikiji/tsconfig.build.json" }
|
{ "path": "./plugin-shikiji/tsconfig.build.json" }
|
||||||
]
|
],
|
||||||
|
"files": []
|
||||||
}
|
}
|
||||||
|
|||||||
1441
pnpm-lock.yaml
generated
1441
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,6 @@
|
|||||||
[](https://www.npmjs.com/package/vuepress-theme-plume)
|
[](https://www.npmjs.com/package/vuepress-theme-plume)
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
### [查看文档](https://pengzhanbo.cn/note/vuepress-theme-plume)
|
### [查看文档](https://pengzhanbo.cn/note/vuepress-theme-plume)
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
@ -55,7 +54,6 @@ __options__ : `PlumeThemeOptions`
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## 内置插件
|
## 内置插件
|
||||||
|
|
||||||
- [plugin-shikiji](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/plugins/plugin-shikiji) 使用 [`shikiji`](https://shikiji.netlify.app/) 来为 Markdown 代码块启用代码高亮。
|
- [plugin-shikiji](https://github.com/pengzhanbo/vuepress-theme-plume/tree/main/plugins/plugin-shikiji) 使用 [`shikiji`](https://shikiji.netlify.app/) 来为 Markdown 代码块启用代码高亮。
|
||||||
|
|||||||
@ -1,27 +1,28 @@
|
|||||||
import fs from 'fs'
|
import fs from 'node:fs'
|
||||||
import path from 'path'
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import process from 'node:process'
|
||||||
import { execa } from 'execa'
|
import { execa } from 'execa'
|
||||||
import ora from 'ora'
|
import ora from 'ora'
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import { fileURLToPath } from 'url'
|
|
||||||
|
|
||||||
const _dirname =
|
const _dirname
|
||||||
typeof __dirname !== 'undefined'
|
= typeof __dirname !== 'undefined'
|
||||||
? __dirname
|
? __dirname
|
||||||
: path.dirname(fileURLToPath(import.meta.url))
|
: path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
const packages = [
|
const packages = [
|
||||||
...fs
|
...fs
|
||||||
.readdirSync(path.join(_dirname, '../packages'))
|
.readdirSync(path.join(_dirname, '../packages'))
|
||||||
.filter((file) => file !== '.DS_Store' && file !== 'tsconfig.build.json')
|
.filter(file => file !== '.DS_Store' && file !== 'tsconfig.build.json')
|
||||||
.map((dir) => path.join('../packages', dir)),
|
.map(dir => path.join('../packages', dir)),
|
||||||
'../docs',
|
'../docs',
|
||||||
]
|
]
|
||||||
|
|
||||||
const dependencies = packages.map((dir) => {
|
const dependencies = packages.map((dir) => {
|
||||||
const pkg = fs.readFileSync(
|
const pkg = fs.readFileSync(
|
||||||
path.join(_dirname, dir, 'package.json'),
|
path.join(_dirname, dir, 'package.json'),
|
||||||
'utf-8'
|
'utf-8',
|
||||||
)
|
)
|
||||||
const { dependencies, devDependencies } = JSON.parse(pkg)
|
const { dependencies, devDependencies } = JSON.parse(pkg)
|
||||||
return {
|
return {
|
||||||
@ -34,29 +35,29 @@ const dependencies = packages.map((dir) => {
|
|||||||
function filterVuePress(dependencies) {
|
function filterVuePress(dependencies) {
|
||||||
const vuepress = dependencies
|
const vuepress = dependencies
|
||||||
.filter(
|
.filter(
|
||||||
(dependence) =>
|
dependence =>
|
||||||
dependence.startsWith('@vuepress/')
|
dependence.startsWith('@vuepress/'),
|
||||||
)
|
)
|
||||||
.map((dependence) => dependence + '@next')
|
.map(dependence => `${dependence}@next`)
|
||||||
const includes = ['vue', 'vue-router']
|
const includes = ['vue', 'vue-router']
|
||||||
const vue = dependencies
|
const vue = dependencies
|
||||||
.filter((dependence) => includes.includes(dependence))
|
.filter(dependence => includes.includes(dependence))
|
||||||
.map((dependence) => dependence + '@latest')
|
.map(dependence => `${dependence}@latest`)
|
||||||
return [...vue, ...vuepress]
|
return [...vue, ...vuepress]
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = []
|
const options = []
|
||||||
dependencies.forEach(({ dirname, dependencies, devDependencies }) => {
|
dependencies.forEach(({ dirname, dependencies, devDependencies }) => {
|
||||||
if (dependencies.length) {
|
if (dependencies.length)
|
||||||
options.push(['pnpm', ['add', ...dependencies], { cwd: dirname }])
|
options.push(['pnpm', ['add', ...dependencies], { cwd: dirname }])
|
||||||
}
|
|
||||||
if (devDependencies.length) {
|
if (devDependencies.length)
|
||||||
options.push(['pnpm', ['add', '-D', ...devDependencies], { cwd: dirname }])
|
options.push(['pnpm', ['add', '-D', ...devDependencies], { cwd: dirname }])
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
async function install(index = 0) {
|
async function install(index = 0) {
|
||||||
if (index >= options.length) return
|
if (index >= options.length)
|
||||||
|
return
|
||||||
const spinner = ora()
|
const spinner = ora()
|
||||||
const opt = options[index]
|
const opt = options[index]
|
||||||
const dir = opt[2].cwd.split('/').slice(-2).join('/')
|
const dir = opt[2].cwd.split('/').slice(-2).join('/')
|
||||||
@ -69,7 +70,8 @@ async function install(index = 0) {
|
|||||||
await current
|
await current
|
||||||
spinner.succeed('Installed.')
|
spinner.succeed('Installed.')
|
||||||
await install(index + 1)
|
await install(index + 1)
|
||||||
} catch (e) {
|
}
|
||||||
|
catch (e) {
|
||||||
spinner.fail('Install Fail.')
|
spinner.fail('Install Fail.')
|
||||||
console.log(e)
|
console.log(e)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
import { readTemplateList } from './readTpl.js'
|
import path from 'node:path'
|
||||||
import path from 'path'
|
import fs from 'node:fs'
|
||||||
import fs from 'fs'
|
import { fileURLToPath } from 'node:url'
|
||||||
import { upperCase, lowerCase, packageName } from './utils.js'
|
|
||||||
import handlebars from 'handlebars'
|
import handlebars from 'handlebars'
|
||||||
import { writeFile } from './writeFile.js'
|
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import ora from 'ora'
|
import ora from 'ora'
|
||||||
import { execa } from 'execa'
|
import { execa } from 'execa'
|
||||||
import { fileURLToPath } from 'url'
|
import { readTemplateList } from './readTpl.js'
|
||||||
|
import { lowerCase, packageName, upperCase } from './utils.js'
|
||||||
|
import { writeFile } from './writeFile.js'
|
||||||
|
|
||||||
const compile = handlebars.compile
|
const compile = handlebars.compile
|
||||||
|
|
||||||
const _dirname =
|
const _dirname
|
||||||
typeof __dirname !== 'undefined'
|
= typeof __dirname !== 'undefined'
|
||||||
? __dirname
|
? __dirname
|
||||||
: path.dirname(fileURLToPath(import.meta.url))
|
: path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
@ -20,10 +20,10 @@ const packagesRoot = path.join(_dirname, '../../packages')
|
|||||||
const spinner = ora()
|
const spinner = ora()
|
||||||
|
|
||||||
const pkg = JSON.parse(
|
const pkg = JSON.parse(
|
||||||
fs.readFileSync(path.join(_dirname, '../../package.json'), 'utf-8')
|
fs.readFileSync(path.join(_dirname, '../../package.json'), 'utf-8'),
|
||||||
)
|
)
|
||||||
|
|
||||||
const generatorFile = async (config) => {
|
async function generatorFile(config) {
|
||||||
const templateList = readTemplateList(path.join(_dirname, './template'))
|
const templateList = readTemplateList(path.join(_dirname, './template'))
|
||||||
|
|
||||||
const { name, client, shared } = config
|
const { name, client, shared } = config
|
||||||
@ -40,7 +40,7 @@ const generatorFile = async (config) => {
|
|||||||
const include = [
|
const include = [
|
||||||
!client && 'client',
|
!client && 'client',
|
||||||
!shared && 'shared',
|
!shared && 'shared',
|
||||||
!shared && 'client/index.js'
|
!shared && 'client/index.js',
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join('|')
|
.join('|')
|
||||||
@ -60,7 +60,8 @@ const generatorFile = async (config) => {
|
|||||||
try {
|
try {
|
||||||
const filepath = path.join(targetDir, file)
|
const filepath = path.join(targetDir, file)
|
||||||
await writeFile(filepath, template(data))
|
await writeFile(filepath, template(data))
|
||||||
} catch (e) {
|
}
|
||||||
|
catch (e) {
|
||||||
spinner.fail(`Failed to generate ${chalk.cyan(pkgName)}`)
|
spinner.fail(`Failed to generate ${chalk.cyan(pkgName)}`)
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
@ -68,7 +69,7 @@ const generatorFile = async (config) => {
|
|||||||
spinner.succeed(`${chalk.cyan(pkgName)} generated !`)
|
spinner.succeed(`${chalk.cyan(pkgName)} generated !`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const initPackage = async (config) => {
|
async function initPackage(config) {
|
||||||
const { name, client } = config
|
const { name, client } = config
|
||||||
const pkgName = packageName(name)
|
const pkgName = packageName(name)
|
||||||
const targetDir = path.join(packagesRoot, pkgName)
|
const targetDir = path.join(packagesRoot, pkgName)
|
||||||
@ -83,13 +84,14 @@ const initPackage = async (config) => {
|
|||||||
try {
|
try {
|
||||||
await execa('pnpm', ['add', ...dependencies], { cwd: targetDir })
|
await execa('pnpm', ['add', ...dependencies], { cwd: targetDir })
|
||||||
spinner.succeed('Installed.')
|
spinner.succeed('Installed.')
|
||||||
} catch (e) {
|
}
|
||||||
|
catch (e) {
|
||||||
spinner.fail('Failed to Installed')
|
spinner.fail('Failed to Installed')
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generator = async (config) => {
|
export async function generator(config) {
|
||||||
await generatorFile(config)
|
await generatorFile(config)
|
||||||
await initPackage(config)
|
await initPackage(config)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import process from 'node:process'
|
||||||
import minimist from 'minimist'
|
import minimist from 'minimist'
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
@ -6,23 +7,23 @@ const defaultOptions = {
|
|||||||
c: false,
|
c: false,
|
||||||
client: false,
|
client: false,
|
||||||
h: false,
|
h: false,
|
||||||
help: false
|
help: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizeArgv = (argv) => {
|
function normalizeArgv(argv) {
|
||||||
return {
|
return {
|
||||||
name: argv._[0] || '',
|
name: argv._[0] || '',
|
||||||
client: argv.client || argv.c,
|
client: argv.client || argv.c,
|
||||||
shared: argv.shared || argv.s,
|
shared: argv.shared || argv.s,
|
||||||
help: argv.h || argv.help
|
help: argv.h || argv.help,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getConfig = () => {
|
export function getConfig() {
|
||||||
const argv = Object.assign(
|
const argv = Object.assign(
|
||||||
{},
|
{},
|
||||||
defaultOptions,
|
defaultOptions,
|
||||||
minimist(process.argv.slice(2))
|
minimist(process.argv.slice(2)),
|
||||||
)
|
)
|
||||||
return normalizeArgv(argv)
|
return normalizeArgv(argv)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
|
|
||||||
export const getHelp = () => {
|
export function getHelp() {
|
||||||
console.log(` this command will generator a package to ${chalk.cyan('packages')}.
|
console.log(` this command will generator a package to ${chalk.cyan('packages')}.
|
||||||
command: ${chalk.green('pnpm pkg <package-name> [--options]')}
|
command: ${chalk.green('pnpm pkg <package-name> [--options]')}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import process from 'node:process'
|
||||||
import { getConfig } from './getConfig.js'
|
import { getConfig } from './getConfig.js'
|
||||||
import { getHelp } from './getHelp.js'
|
import { getHelp } from './getHelp.js'
|
||||||
import { generator } from './generator.js'
|
import { generator } from './generator.js'
|
||||||
@ -7,7 +8,8 @@ const config = getConfig()
|
|||||||
if (config.help) {
|
if (config.help) {
|
||||||
getHelp()
|
getHelp()
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
generator(config).catch((err) => {
|
generator(config).catch((err) => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
|
|||||||
@ -1,32 +1,32 @@
|
|||||||
import fs from 'fs'
|
import fs from 'node:fs'
|
||||||
import path from 'path'
|
import path from 'node:path'
|
||||||
|
|
||||||
const tplRE = /\.tpl$/
|
const tplRE = /\.tpl$/
|
||||||
const readFileList = (dir, fileList = {}) => {
|
function readFileList(dir, fileList = {}) {
|
||||||
const files = fs.readdirSync(dir)
|
const files = fs.readdirSync(dir)
|
||||||
files.forEach((file) => {
|
files.forEach((file) => {
|
||||||
const filepath = path.join(dir, file)
|
const filepath = path.join(dir, file)
|
||||||
const stat = fs.statSync(filepath)
|
const stat = fs.statSync(filepath)
|
||||||
if (stat.isDirectory()) {
|
if (stat.isDirectory()) {
|
||||||
readFileList(filepath, fileList)
|
readFileList(filepath, fileList)
|
||||||
} else {
|
|
||||||
const extname = path.extname(filepath)
|
|
||||||
if (tplRE.test(extname)) {
|
|
||||||
fileList[filepath.replace(tplRE, '')] = fs.readFileSync(filepath, 'utf-8')
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
const extname = path.extname(filepath)
|
||||||
|
if (tplRE.test(extname))
|
||||||
|
fileList[filepath.replace(tplRE, '')] = fs.readFileSync(filepath, 'utf-8')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return fileList
|
return fileList
|
||||||
}
|
}
|
||||||
|
|
||||||
export const readTemplateList = (dir) => {
|
export function readTemplateList(dir) {
|
||||||
const templateMap = readFileList(dir)
|
const templateMap = readFileList(dir)
|
||||||
const result = []
|
const result = []
|
||||||
Object.keys(templateMap).forEach((key) => {
|
Object.keys(templateMap).forEach((key) => {
|
||||||
const file = path.relative(dir, key)
|
const file = path.relative(dir, key)
|
||||||
result.push({
|
result.push({
|
||||||
file,
|
file,
|
||||||
content: templateMap[key]
|
content: templateMap[key],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
export const upperCase = (str) => {
|
export function upperCase(str) {
|
||||||
return str.split(/-|\s+/).filter(Boolean).map((s) => {
|
return str.split(/-|\s+/).filter(Boolean).map((s) => {
|
||||||
return s[0].toUpperCase() + s.slice(1)
|
return s[0].toUpperCase() + s.slice(1)
|
||||||
}).join('')
|
}).join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const lowerCase = (str) => {
|
export function lowerCase(str) {
|
||||||
str = upperCase(str)
|
str = upperCase(str)
|
||||||
return str[0].toLowerCase() + str.slice(1)
|
return str[0].toLowerCase() + str.slice(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const packageName = (name) => {
|
export function packageName(name) {
|
||||||
return 'plugin-' + name.trim().split(/-|\s+/).filter(Boolean).join('-')
|
return `plugin-${name.trim().split(/-|\s+/).filter(Boolean).join('-')}`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,17 @@
|
|||||||
import path from 'path'
|
import path from 'node:path'
|
||||||
import fs from 'fs'
|
import fs from 'node:fs'
|
||||||
|
|
||||||
export const writeFile = async (filepath, content) => {
|
export async function writeFile(filepath, content) {
|
||||||
const dirname = path.dirname(filepath)
|
const dirname = path.dirname(filepath)
|
||||||
if (!fs.existsSync(dirname)) {
|
if (!fs.existsSync(dirname))
|
||||||
fs.mkdirSync(dirname, { recursive: true })
|
fs.mkdirSync(dirname, { recursive: true })
|
||||||
}
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.writeFile(filepath, content, 'utf-8', (err) => {
|
fs.writeFile(filepath, content, 'utf-8', (err) => {
|
||||||
if (err) {
|
if (err)
|
||||||
reject(err)
|
reject(err)
|
||||||
} else {
|
else
|
||||||
resolve()
|
resolve()
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "vuepress-theme-plume",
|
"name": "vuepress-theme-plume",
|
||||||
|
"type": "module",
|
||||||
"version": "1.0.0-rc.5",
|
"version": "1.0.0-rc.5",
|
||||||
"description": "A Blog Theme for VuePress 2.0",
|
"description": "A Blog Theme for VuePress 2.0",
|
||||||
|
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://pengzhanbo.cn/note/vuepress-theme-plume",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||||
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"VuePress",
|
"VuePress",
|
||||||
"Theme",
|
"Theme",
|
||||||
@ -10,17 +21,6 @@
|
|||||||
"vuepress-theme-plume",
|
"vuepress-theme-plume",
|
||||||
"theme-plume"
|
"theme-plume"
|
||||||
],
|
],
|
||||||
"homepage": "https://pengzhanbo.cn/note/vuepress-theme-plume",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
|
||||||
"type": "module",
|
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./lib/node/index.js",
|
".": "./lib/node/index.js",
|
||||||
"./client": "./lib/client/index.js",
|
"./client": "./lib/client/index.js",
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useArchives, useBlogExtract } from '../composables/index.js'
|
import { useArchives, useBlogExtract } from '../composables/index.js'
|
||||||
import IconArchive from './icons/IconArchive.vue'
|
import IconArchive from './icons/IconArchive.vue'
|
||||||
import ShortPostList from './ShortPostList.vue';
|
import ShortPostList from './ShortPostList.vue'
|
||||||
|
|
||||||
const { archives: archivesLink } = useBlogExtract()
|
const { archives: archivesLink } = useBlogExtract()
|
||||||
const { archives } = useArchives()
|
const { archives } = useArchives()
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="archives-wrapper">
|
<div class="archives-wrapper">
|
||||||
<h2 class="archives-title">
|
<h2 class="archives-title">
|
||||||
@ -16,7 +16,9 @@ const { archives } = useArchives()
|
|||||||
<div v-if="archives.length" class="archives">
|
<div v-if="archives.length" class="archives">
|
||||||
<template v-for="archive in archives" :key="archive.label">
|
<template v-for="archive in archives" :key="archive.label">
|
||||||
<div class="archive">
|
<div class="archive">
|
||||||
<h3 class="archive-title">{{ archive.label }}</h3>
|
<h3 class="archive-title">
|
||||||
|
{{ archive.label }}
|
||||||
|
</h3>
|
||||||
<ShortPostList :post-list="archive.list" />
|
<ShortPostList :post-list="archive.list" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -16,17 +16,16 @@ const router = useRouter()
|
|||||||
|
|
||||||
const tag = computed(() => props.tag ?? (props.href ? 'a' : 'span'))
|
const tag = computed(() => props.tag ?? (props.href ? 'a' : 'span'))
|
||||||
const isExternal = computed(
|
const isExternal = computed(
|
||||||
() => props.href && EXTERNAL_URL_RE.test(props.href)
|
() => props.href && EXTERNAL_URL_RE.test(props.href),
|
||||||
)
|
)
|
||||||
|
|
||||||
const linkTo = (e: Event) => {
|
function linkTo(e: Event) {
|
||||||
if (!isExternal.value) {
|
if (!isExternal.value) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (props.href) {
|
if (props.href)
|
||||||
router.push({ path: props.href })
|
router.push({ path: props.href })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import PostList from './PostList.vue'
|
|||||||
import Tags from './Tags.vue'
|
import Tags from './Tags.vue'
|
||||||
|
|
||||||
const page = usePageData<PlumeThemePageData>()
|
const page = usePageData<PlumeThemePageData>()
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="blog-wrapper">
|
<div class="blog-wrapper">
|
||||||
<PostList v-if="page.type === 'blog'" />
|
<PostList v-if="page.type === 'blog'" />
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import AutoLink from './AutoLink.vue'
|
|||||||
import IconArchive from './icons/IconArchive.vue'
|
import IconArchive from './icons/IconArchive.vue'
|
||||||
import IconTag from './icons/IconTag.vue'
|
import IconTag from './icons/IconTag.vue'
|
||||||
|
|
||||||
|
|
||||||
const theme = useThemeLocaleData()
|
const theme = useThemeLocaleData()
|
||||||
|
|
||||||
const avatar = computed(() => theme.value.avatar)
|
const avatar = computed(() => theme.value.avatar)
|
||||||
@ -16,7 +15,7 @@ const { hasBlogExtract, tags, archives } = useBlogExtract()
|
|||||||
<div v-if="avatar" class="blog-aside-wrapper">
|
<div v-if="avatar" class="blog-aside-wrapper">
|
||||||
<div class="avatar-profile">
|
<div class="avatar-profile">
|
||||||
<p v-if="avatar.url">
|
<p v-if="avatar.url">
|
||||||
<img :src="avatar.url" :alt="avatar.name" />
|
<img :src="avatar.url" :alt="avatar.name">
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<h3>{{ avatar.name }}</h3>
|
<h3>{{ avatar.name }}</h3>
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import IconArchive from './icons/IconArchive.vue'
|
|||||||
import IconBlogExt from './icons/IconBlogExt.vue'
|
import IconBlogExt from './icons/IconBlogExt.vue'
|
||||||
import IconTag from './icons/IconTag.vue'
|
import IconTag from './icons/IconTag.vue'
|
||||||
|
|
||||||
|
|
||||||
const theme = useThemeLocaleData()
|
const theme = useThemeLocaleData()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@ -26,17 +25,19 @@ watch(() => route.path, () => {
|
|||||||
watch(
|
watch(
|
||||||
[() => open.value],
|
[() => open.value],
|
||||||
() => {
|
() => {
|
||||||
if (open.value) {
|
if (open.value)
|
||||||
isLocked.value = true
|
isLocked.value = true
|
||||||
} else isLocked.value = false
|
|
||||||
|
else isLocked.value = false
|
||||||
},
|
},
|
||||||
{ immediate: true, flush: 'post' }
|
{ immediate: true, flush: 'post' },
|
||||||
)
|
)
|
||||||
|
|
||||||
const showBlogExtract = computed(() => {
|
const showBlogExtract = computed(() => {
|
||||||
return avatar.value || hasBlogExtract.value
|
return avatar.value || hasBlogExtract.value
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="showBlogExtract" class="blog-extract" @click="open = !open">
|
<div v-if="showBlogExtract" class="blog-extract" @click="open = !open">
|
||||||
<IconBlogExt class="icon" />
|
<IconBlogExt class="icon" />
|
||||||
@ -45,11 +46,13 @@ const showBlogExtract = computed(() => {
|
|||||||
<div class="blog-modal-container">
|
<div class="blog-modal-container">
|
||||||
<div v-if="avatar" class="avatar-profile">
|
<div v-if="avatar" class="avatar-profile">
|
||||||
<p v-if="avatar.url" class="avatar">
|
<p v-if="avatar.url" class="avatar">
|
||||||
<img :src="avatar.url" :alt="avatar.name" />
|
<img :src="avatar.url" :alt="avatar.name">
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<h3>{{ avatar.name }}</h3>
|
<h3>{{ avatar.name }}</h3>
|
||||||
<p class="desc">{{ avatar.description }}</p>
|
<p class="desc">
|
||||||
|
{{ avatar.description }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasBlogExtract" class="blog-nav">
|
<div v-if="hasBlogExtract" class="blog-nav">
|
||||||
|
|||||||
@ -12,7 +12,7 @@ defineProps<{
|
|||||||
<div class="menu-group">
|
<div class="menu-group">
|
||||||
<p v-if="text" class="title">
|
<p v-if="text" class="title">
|
||||||
<Icon v-if="icon" :name="icon" />
|
<Icon v-if="icon" :name="icon" />
|
||||||
<span v-text="text"></span>
|
<span v-text="text" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<template v-for="item in items">
|
<template v-for="item in items">
|
||||||
|
|||||||
@ -17,13 +17,13 @@ const page = usePageData()
|
|||||||
active: isActive(
|
active: isActive(
|
||||||
page.path,
|
page.path,
|
||||||
item.activeMatch || item.link,
|
item.activeMatch || item.link,
|
||||||
!!item.activeMatch
|
!!item.activeMatch,
|
||||||
),
|
),
|
||||||
}"
|
}"
|
||||||
:href="item.link"
|
:href="item.link"
|
||||||
>
|
>
|
||||||
<Icon v-if="item.icon" :name="item.icon" />
|
<Icon v-if="item.icon" :name="item.icon" />
|
||||||
<i v-text="item.text"></i>
|
<i v-text="item.text" />
|
||||||
</AutoLink>
|
</AutoLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -25,7 +25,7 @@ function onBlur() {
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
// eslint-disable-next-line vue/match-component-file-name
|
|
||||||
name: 'Flyout',
|
name: 'Flyout',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -7,18 +7,20 @@ import AutoLink from './AutoLink.vue'
|
|||||||
import FriendsItem from './FriendsItem.vue'
|
import FriendsItem from './FriendsItem.vue'
|
||||||
import IconEdit from './icons/IconEdit.vue'
|
import IconEdit from './icons/IconEdit.vue'
|
||||||
|
|
||||||
|
|
||||||
const matter = usePageFrontmatter<PlumeThemeFriendsFrontmatter>()
|
const matter = usePageFrontmatter<PlumeThemeFriendsFrontmatter>()
|
||||||
const editNavLink = useEditNavLink()
|
const editNavLink = useEditNavLink()
|
||||||
|
|
||||||
const list = computed(() => matter.value.list || [])
|
const list = computed(() => matter.value.list || [])
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="friends-wrapper">
|
<div class="friends-wrapper">
|
||||||
<h2 class="title">{{ matter.title || 'My Friends' }}</h2>
|
<h2 class="title">
|
||||||
<p v-if="matter.description" class="description">{{ matter.description }}</p>
|
{{ matter.title || 'My Friends' }}
|
||||||
|
</h2>
|
||||||
|
<p v-if="matter.description" class="description">
|
||||||
|
{{ matter.description }}
|
||||||
|
</p>
|
||||||
<section v-if="list.length" class="friends-list">
|
<section v-if="list.length" class="friends-list">
|
||||||
<FriendsItem v-for="(friend, index) in list" :key="friend.name + index" :friend="friend" />
|
<FriendsItem v-for="(friend, index) in list" :key="friend.name + index" :friend="friend" />
|
||||||
</section>
|
</section>
|
||||||
@ -31,6 +33,7 @@ const list = computed(() => matter.value.list || [])
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.friends-wrapper {
|
.friends-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type {FriendsItem} from '../../shared/index';
|
import type { FriendsItem } from '../../shared/index'
|
||||||
import AutoLink from './AutoLink.vue'
|
import AutoLink from './AutoLink.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@ -10,11 +10,15 @@ defineProps<{
|
|||||||
<template>
|
<template>
|
||||||
<div class="friend">
|
<div class="friend">
|
||||||
<AutoLink class="avatar-link" :href="friend.link" no-icon>
|
<AutoLink class="avatar-link" :href="friend.link" no-icon>
|
||||||
<div class="avatar" :style="{ backgroundImage: `url(${friend.avatar})` }"></div>
|
<div class="avatar" :style="{ backgroundImage: `url(${friend.avatar})` }" />
|
||||||
</AutoLink>
|
</AutoLink>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<AutoLink class="title" :href="friend.link" no-icon>{{ friend.name }}</AutoLink>
|
<AutoLink class="title" :href="friend.link" no-icon>
|
||||||
<p v-if="friend.desc">{{ friend.desc }}</p>
|
{{ friend.name }}
|
||||||
|
</AutoLink>
|
||||||
|
<p v-if="friend.desc">
|
||||||
|
{{ friend.desc }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
import { usePageFrontmatter, withBase } from '@vuepress/client'
|
import { usePageFrontmatter, withBase } from '@vuepress/client'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import type { PlumeThemeHomeFrontmatter } from '../../shared/index.js'
|
import type { PlumeThemeHomeFrontmatter } from '../../shared/index.js'
|
||||||
|
|
||||||
// import { useThemeLocaleData } from '../composables/index.js'
|
// import { useThemeLocaleData } from '../composables/index.js'
|
||||||
import { useDarkMode } from '../composables/darkMode.js'
|
import { useDarkMode } from '../composables/darkMode.js'
|
||||||
import VButton from './VButton.vue'
|
import VButton from './VButton.vue'
|
||||||
@ -10,9 +11,9 @@ const matter = usePageFrontmatter<PlumeThemeHomeFrontmatter>()
|
|||||||
const isDark = useDarkMode()
|
const isDark = useDarkMode()
|
||||||
|
|
||||||
const mask = computed(() => {
|
const mask = computed(() => {
|
||||||
if (typeof matter.value.bannerMask !== 'object') {
|
if (typeof matter.value.bannerMask !== 'object')
|
||||||
return matter.value.bannerMask || 0
|
return matter.value.bannerMask || 0
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
(isDark.value
|
(isDark.value
|
||||||
? matter.value.bannerMask.dark
|
? matter.value.bannerMask.dark
|
||||||
@ -41,15 +42,20 @@ const actions = computed(() => {
|
|||||||
return matter.value.hero?.actions ?? []
|
return matter.value.hero?.actions ?? []
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="plume-home" :style="homeStyle">
|
<div class="plume-home" :style="homeStyle">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div v-if="matter.hero" class="content">
|
<div v-if="matter.hero" class="content">
|
||||||
<h2 v-if="name" class="hero-name">{{ name }}</h2>
|
<h2 v-if="name" class="hero-name">
|
||||||
|
{{ name }}
|
||||||
|
</h2>
|
||||||
<p v-if="tagline" class="hero-tagline">
|
<p v-if="tagline" class="hero-tagline">
|
||||||
<span class="line"></span> <span>{{ tagline }}</span>
|
<span class="line" /> <span>{{ tagline }}</span>
|
||||||
|
</p>
|
||||||
|
<p v-if="text" class="hero-text">
|
||||||
|
{{ text }}
|
||||||
</p>
|
</p>
|
||||||
<p v-if="text" class="hero-text">{{ text }}</p>
|
|
||||||
<div v-if="actions" class="actions">
|
<div v-if="actions" class="actions">
|
||||||
<div v-for="action in actions" :key="action.link" class="action">
|
<div v-for="action in actions" :key="action.link" class="action">
|
||||||
<VButton
|
<VButton
|
||||||
|
|||||||
@ -7,6 +7,7 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const { hasSidebar } = useSidebar()
|
const { hasSidebar } = useSidebar()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
id="LayoutContent"
|
id="LayoutContent"
|
||||||
|
|||||||
@ -13,7 +13,7 @@ defineProps<{
|
|||||||
open: boolean
|
open: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
defineEmits<(e: 'open-menu') => void>()
|
defineEmits<(e: 'openMenu') => void>()
|
||||||
|
|
||||||
const page = usePageData<PlumeThemePageData>()
|
const page = usePageData<PlumeThemePageData>()
|
||||||
const themeData = useThemeLocaleData()
|
const themeData = useThemeLocaleData()
|
||||||
@ -29,36 +29,35 @@ const empty = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
navHeight.value = parseInt(
|
navHeight.value = Number.parseInt(
|
||||||
getComputedStyle(document.documentElement).getPropertyValue(
|
getComputedStyle(document.documentElement).getPropertyValue(
|
||||||
'--vp-nav-height'
|
'--vp-nav-height',
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const classes = computed(() => {
|
const classes = computed(() => {
|
||||||
return {
|
return {
|
||||||
'local-nav': true,
|
'local-nav': true,
|
||||||
fixed: empty.value,
|
'fixed': empty.value,
|
||||||
'reached-top': y.value >= navHeight.value
|
'reached-top': y.value >= navHeight.value,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const showLocalNav = computed(() => {
|
const showLocalNav = computed(() => {
|
||||||
return (hasSidebar.value || page.value.isBlogPost) && (!empty.value || y.value >= navHeight.value)
|
return (hasSidebar.value || page.value.isBlogPost) && (!empty.value || y.value >= navHeight.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="showLocalNav" :class="classes">
|
<div v-if="showLocalNav" :class="classes">
|
||||||
<button
|
<button
|
||||||
class="menu"
|
class="menu"
|
||||||
:class="{ 'hidden': page.isBlogPost }"
|
:class="{ hidden: page.isBlogPost }"
|
||||||
:disabled="page.isBlogPost"
|
:disabled="page.isBlogPost"
|
||||||
:aria-expanded="open"
|
:aria-expanded="open"
|
||||||
aria-controls="SidebarNav"
|
aria-controls="SidebarNav"
|
||||||
@click="$emit('open-menu')"
|
@click="$emit('openMenu')"
|
||||||
>
|
>
|
||||||
<IconAlignLeft class="menu-icon" />
|
<IconAlignLeft class="menu-icon" />
|
||||||
<span class="menu-text"> {{ themeData.sidebarMenuLabel || 'Menu' }} </span>
|
<span class="menu-text"> {{ themeData.sidebarMenuLabel || 'Menu' }} </span>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type {PageHeader} from '@vuepress/client';
|
import type { PageHeader } from '@vuepress/client'
|
||||||
import { onClickOutside } from '@vueuse/core'
|
import { onClickOutside } from '@vueuse/core'
|
||||||
import { nextTick, ref, watch } from 'vue'
|
import { nextTick, ref, watch } from 'vue'
|
||||||
import { useThemeLocaleData } from '../composables/index.js'
|
import { useThemeLocaleData } from '../composables/index.js'
|
||||||
@ -33,9 +33,9 @@ function toggle() {
|
|||||||
function onItemClick(e: Event) {
|
function onItemClick(e: Event) {
|
||||||
if ((e.target as HTMLElement).classList.contains('outline-link')) {
|
if ((e.target as HTMLElement).classList.contains('outline-link')) {
|
||||||
// disable animation on hash navigation when page jumps
|
// disable animation on hash navigation when page jumps
|
||||||
if (items.value) {
|
if (items.value)
|
||||||
items.value.style.transition = 'none'
|
items.value.style.transition = 'none'
|
||||||
}
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
open.value = false
|
open.value = false
|
||||||
})
|
})
|
||||||
@ -49,7 +49,7 @@ function scrollToTop() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="local-nav-outline-dropdown" :style="{ '--vp-vh': vh + 'px' }">
|
<div class="local-nav-outline-dropdown" :style="{ '--vp-vh': `${vh}px` }">
|
||||||
<button v-if="headers.length > 0" ref="btn" :class="{ open }" @click="toggle">
|
<button v-if="headers.length > 0" ref="btn" :class="{ open }" @click="toggle">
|
||||||
{{ theme.outlineLabel || 'On this page' }}
|
{{ theme.outlineLabel || 'On this page' }}
|
||||||
<IconChevronRight class="icon" />
|
<IconChevronRight class="icon" />
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { usePageFrontmatter, } from '@vuepress/client'
|
import { usePageFrontmatter } from '@vuepress/client'
|
||||||
import { useWindowScroll } from '@vueuse/core'
|
import { useWindowScroll } from '@vueuse/core'
|
||||||
import { ref, watchPostEffect } from 'vue'
|
import { ref, watchPostEffect } from 'vue'
|
||||||
import { useSidebar } from '../../composables/sidebar.js'
|
import { useSidebar } from '../../composables/sidebar.js'
|
||||||
@ -15,7 +15,7 @@ import NavBarTranslations from './NavBarTranslations.vue'
|
|||||||
defineProps<{
|
defineProps<{
|
||||||
isScreenOpen: boolean
|
isScreenOpen: boolean
|
||||||
}>()
|
}>()
|
||||||
defineEmits<(e: 'toggle-screen') => void>()
|
defineEmits<(e: 'toggleScreen') => void>()
|
||||||
|
|
||||||
const matter = usePageFrontmatter()
|
const matter = usePageFrontmatter()
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ const classes = ref<Record<string, boolean>>({})
|
|||||||
watchPostEffect(() => {
|
watchPostEffect(() => {
|
||||||
classes.value = {
|
classes.value = {
|
||||||
'has-sidebar': hasSidebar.value,
|
'has-sidebar': hasSidebar.value,
|
||||||
top: !!matter.value.home && y.value === 0,
|
'top': !!matter.value.home && y.value === 0,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@ -39,7 +39,7 @@ watchPostEffect(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="curtain"></div>
|
<div class="curtain" />
|
||||||
<div class="content-body">
|
<div class="content-body">
|
||||||
<NavBarSearch class="search" />
|
<NavBarSearch class="search" />
|
||||||
<NavBarMenu class="menu" />
|
<NavBarMenu class="menu" />
|
||||||
@ -50,7 +50,7 @@ watchPostEffect(() => {
|
|||||||
<NavBarHamburger
|
<NavBarHamburger
|
||||||
class="hamburger"
|
class="hamburger"
|
||||||
:active="isScreenOpen"
|
:active="isScreenOpen"
|
||||||
@click="$emit('toggle-screen')"
|
@click="$emit('toggleScreen')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -68,7 +68,6 @@ watchPostEffect(() => {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.navbar-wrapper {
|
.navbar-wrapper {
|
||||||
padding: 0 32px;
|
padding: 0 32px;
|
||||||
|
|||||||
@ -11,7 +11,7 @@ const theme = useThemeLocaleData()
|
|||||||
const { localeLinks, currentLang } = useLangs({ correspondingLink: true })
|
const { localeLinks, currentLang } = useLangs({ correspondingLink: true })
|
||||||
|
|
||||||
const hasExtraContent = computed(
|
const hasExtraContent = computed(
|
||||||
() => theme.value.appearance || theme.value.social
|
() => theme.value.appearance || theme.value.social,
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -21,7 +21,9 @@ const hasExtraContent = computed(
|
|||||||
v-if="localeLinks.length && currentLang.label"
|
v-if="localeLinks.length && currentLang.label"
|
||||||
class="group translations"
|
class="group translations"
|
||||||
>
|
>
|
||||||
<p class="trans-title">{{ currentLang.label }}</p>
|
<p class="trans-title">
|
||||||
|
{{ currentLang.label }}
|
||||||
|
</p>
|
||||||
|
|
||||||
<template v-for="locale in localeLinks" :key="locale.link">
|
<template v-for="locale in localeLinks" :key="locale.link">
|
||||||
<MenuLink :item="locale" />
|
<MenuLink :item="locale" />
|
||||||
@ -30,7 +32,9 @@ const hasExtraContent = computed(
|
|||||||
|
|
||||||
<div v-if="theme.appearance" class="group">
|
<div v-if="theme.appearance" class="group">
|
||||||
<div class="item appearance">
|
<div class="item appearance">
|
||||||
<p class="label">Appearance</p>
|
<p class="label">
|
||||||
|
Appearance
|
||||||
|
</p>
|
||||||
<div class="appearance-action">
|
<div class="appearance-action">
|
||||||
<SwitchAppearance />
|
<SwitchAppearance />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,9 +12,7 @@ const theme = useThemeLocaleData()
|
|||||||
aria-labelledby="main-nav-aria-label"
|
aria-labelledby="main-nav-aria-label"
|
||||||
class="navbar-menu"
|
class="navbar-menu"
|
||||||
>
|
>
|
||||||
<span id="main-nav-aria-label" class="visually-hidden"
|
<span id="main-nav-aria-label" class="visually-hidden">Main Navigation</span>
|
||||||
>Main Navigation</span
|
|
||||||
>
|
|
||||||
<template v-for="item in theme.navbar" :key="item.text">
|
<template v-for="item in theme.navbar" :key="item.text">
|
||||||
<NavBarMenuLink v-if="'link' in item" :item="item" />
|
<NavBarMenuLink v-if="'link' in item" :item="item" />
|
||||||
<NavBarMenuGroup v-else :item="item" />
|
<NavBarMenuGroup v-else :item="item" />
|
||||||
|
|||||||
@ -11,14 +11,15 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const page = usePageData()
|
const page = usePageData()
|
||||||
|
|
||||||
const isChildActive = (navItem: NavItem) => {
|
function isChildActive(navItem: NavItem) {
|
||||||
if ('link' in navItem) {
|
if ('link' in navItem) {
|
||||||
return isActive(
|
return isActive(
|
||||||
page.value.path,
|
page.value.path,
|
||||||
navItem.link,
|
navItem.link,
|
||||||
!!props.item.activeMatch
|
!!props.item.activeMatch,
|
||||||
)
|
)
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return navItem.items.some(isChildActive)
|
return navItem.items.some(isChildActive)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -27,9 +28,8 @@ const childrenActive = computed(() => isChildActive(props.item))
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Flyout
|
<Flyout
|
||||||
:class="{
|
class="navbar-menu-group" :class="{
|
||||||
'navbar-menu-group': true,
|
active: isActive(page.path, item.activeMatch, !!item.activeMatch) || childrenActive,
|
||||||
'active': isActive(page.path, item.activeMatch, !!item.activeMatch) || childrenActive,
|
|
||||||
}"
|
}"
|
||||||
:button="item.text"
|
:button="item.text"
|
||||||
:items="item.items"
|
:items="item.items"
|
||||||
|
|||||||
@ -13,19 +13,18 @@ const page = usePageData()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AutoLink
|
<AutoLink
|
||||||
:class="{
|
class="navbar-menu-link" :class="{
|
||||||
'navbar-menu-link': true,
|
active: isActive(
|
||||||
'active': isActive(
|
|
||||||
page.path,
|
page.path,
|
||||||
item.activeMatch || item.link,
|
item.activeMatch || item.link,
|
||||||
!!item.activeMatch
|
!!item.activeMatch,
|
||||||
),
|
),
|
||||||
}"
|
}"
|
||||||
:href="item.link"
|
:href="item.link"
|
||||||
:no-icon="true"
|
:no-icon="true"
|
||||||
>
|
>
|
||||||
<Icon v-if="item.icon" :name="item.icon" />
|
<Icon v-if="item.icon" :name="item.icon" />
|
||||||
<i v-text="item.text"></i>
|
<i v-text="item.text" />
|
||||||
</AutoLink>
|
</AutoLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,9 @@ const { currentLang, localeLinks } = useLangs()
|
|||||||
:label="theme.selectLanguageText || 'change language'"
|
:label="theme.selectLanguageText || 'change language'"
|
||||||
>
|
>
|
||||||
<div class="items">
|
<div class="items">
|
||||||
<p class="title">{{ currentLang.label }}</p>
|
<p class="title">
|
||||||
|
{{ currentLang.label }}
|
||||||
|
</p>
|
||||||
|
|
||||||
<template v-for="locale in localeLinks" :key="locale.link">
|
<template v-for="locale in localeLinks" :key="locale.link">
|
||||||
<MenuLink :item="locale" />
|
<MenuLink :item="locale" />
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user