feat: add create-vuepress-theme-plume package (#153)
* feat: add `create-vuepress-theme-plume` package * feat(cli): add support deploy
This commit is contained in:
parent
a4dc03f736
commit
74079390f6
21
cli/LICENSE
Normal file
21
cli/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 2021 - PRESENT by pengzhanbo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
14
cli/README.md
Normal file
14
cli/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# create-vuepress-theme-plume
|
||||
|
||||
The cli for create vuepress-theme-plume's project
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
# npm
|
||||
npm init vuepress-theme-plume@latest
|
||||
# pnpm
|
||||
pnpm create vuepress-theme-plume@latest
|
||||
# yarn
|
||||
yarn create vuepress-theme-plume@latest
|
||||
```
|
||||
2
cli/bin/index.js
Executable file
2
cli/bin/index.js
Executable file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
import '../lib/index.js'
|
||||
43
cli/package.json
Normal file
43
cli/package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "create-vuepress-theme-plume",
|
||||
"type": "module",
|
||||
"version": "1.0.0-rc.90",
|
||||
"description": "The cli for create vuepress-theme-plume's project",
|
||||
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
||||
"license": "MIT",
|
||||
"homepage": "https://theme-plume.vuejs.press/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git",
|
||||
"directory": "cli"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"VuePress",
|
||||
"theme",
|
||||
"plume",
|
||||
"cli"
|
||||
],
|
||||
"bin": "./bin/index.js",
|
||||
"files": [
|
||||
"bin",
|
||||
"lib",
|
||||
"templates"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup"
|
||||
},
|
||||
"dependencies": {
|
||||
"@clack/prompts": "^0.7.0",
|
||||
"@pengzhanbo/utils": "^1.1.2",
|
||||
"cac": "^6.7.14",
|
||||
"execa": "^9.3.1",
|
||||
"handlebars": "^4.7.8",
|
||||
"picocolors": "^1.0.1"
|
||||
},
|
||||
"theme-plume": {
|
||||
"vuepress": "2.0.0-rc.14"
|
||||
}
|
||||
}
|
||||
30
cli/src/constants.ts
Normal file
30
cli/src/constants.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import type { Bundler, Langs, Options } from './types.js'
|
||||
|
||||
export const languageOptions: Options<Langs> = [
|
||||
{ label: 'English', value: 'en-US' },
|
||||
{ label: '简体中文', value: 'zh-CN' },
|
||||
]
|
||||
|
||||
export const bundlerOptions: Options<Bundler> = [
|
||||
{ label: 'Vite', value: 'vite' },
|
||||
{ label: 'Webpack', value: 'webpack' },
|
||||
]
|
||||
|
||||
export enum Mode {
|
||||
init,
|
||||
create,
|
||||
}
|
||||
|
||||
export enum DeployType {
|
||||
github = 'github',
|
||||
vercel = 'vercel',
|
||||
netlify = 'netlify',
|
||||
custom = 'custom',
|
||||
}
|
||||
|
||||
export const deployOptions: Options<DeployType> = [
|
||||
{ label: 'Custom', value: DeployType.custom },
|
||||
{ label: 'GitHub Pages', value: DeployType.github },
|
||||
{ label: 'Vercel', value: DeployType.vercel },
|
||||
{ label: 'Netlify', value: DeployType.netlify },
|
||||
]
|
||||
123
cli/src/generate.ts
Normal file
123
cli/src/generate.ts
Normal file
@ -0,0 +1,123 @@
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import fs from 'node:fs'
|
||||
import { execaCommand } from 'execa'
|
||||
import { createPackageJson } from './packageJson.js'
|
||||
import { createRender } from './render.js'
|
||||
import { getTemplate, readFiles, readJsonFile, writeFiles } from './utils/index.js'
|
||||
import type { File, ResolvedData } from './types.js'
|
||||
import { DeployType, Mode } from './constants.js'
|
||||
|
||||
export async function generate(mode: Mode, data: ResolvedData): Promise<void> {
|
||||
const cwd = process.cwd()
|
||||
|
||||
let userPkg: Record<string, any> = {}
|
||||
if (mode === Mode.init) {
|
||||
const pkgPath = path.join(cwd, 'package.json')
|
||||
if (fs.existsSync(pkgPath)) {
|
||||
userPkg = (await readJsonFile(pkgPath)) || {}
|
||||
}
|
||||
}
|
||||
|
||||
const fileList: File[] = [
|
||||
// add package.json
|
||||
await createPackageJson(mode, userPkg, data),
|
||||
// add docs files
|
||||
...await createDocsFiles(data),
|
||||
// add vuepress and theme-plume configs
|
||||
...updateFileListTarget(await readFiles(getTemplate('.vuepress')), `${data.docsDir}/.vuepress`),
|
||||
]
|
||||
|
||||
// add repo root files
|
||||
if (mode === Mode.create) {
|
||||
fileList.push(...await readFiles(getTemplate('common')))
|
||||
if (data.packageManager === 'pnpm') {
|
||||
fileList.push({
|
||||
filepath: '.npmrc',
|
||||
content: 'shamefully-hoist=true\nshell-emulator=true',
|
||||
})
|
||||
}
|
||||
if (data.packageManager === 'yarn') {
|
||||
const { stdout: yarnVersion } = await execaCommand('yarn --version')
|
||||
if (yarnVersion.startsWith('2')) {
|
||||
fileList.push({
|
||||
filepath: '.yarnrc.yml',
|
||||
content: 'nodeLinker: \'node-modules\'\n',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rewrite git files begin ==================================
|
||||
if (data.git)
|
||||
fileList.push(...await readFiles(getTemplate('git')))
|
||||
|
||||
if (mode === Mode.init) {
|
||||
const gitignorePath = path.join(cwd, '.gitignore')
|
||||
const docs = data.docsDir
|
||||
if (fs.existsSync(gitignorePath)) {
|
||||
const content = await fs.promises.readFile(gitignorePath, 'utf-8')
|
||||
fileList.push({
|
||||
filepath: '.gitignore',
|
||||
content: `${content}\n${docs}/.vuepress/.cache\n${docs}/.vuepress/.temp\n${docs}/.vuepress/dist\n`,
|
||||
})
|
||||
}
|
||||
}
|
||||
// rewrite git files end ====================================
|
||||
|
||||
if (data.deploy !== DeployType.custom) {
|
||||
fileList.push(...await readFiles(getTemplate(`deploy/${data.deploy}`)))
|
||||
}
|
||||
|
||||
const render = createRender(data)
|
||||
|
||||
const renderedFiles = fileList.map((file) => {
|
||||
if (file.filepath.endsWith('.handlebars'))
|
||||
file.content = render(file.content)
|
||||
|
||||
return file
|
||||
})
|
||||
|
||||
const ext = data.useTs ? '' : userPkg.type !== 'module' ? '.mjs' : '.js'
|
||||
const REG_EXT = /\.ts$/
|
||||
const output = mode === Mode.create ? path.join(cwd, data.root) : cwd
|
||||
await writeFiles(renderedFiles, output, (filepath) => {
|
||||
if (filepath.endsWith('.d.ts'))
|
||||
return filepath
|
||||
if (ext)
|
||||
return filepath.replace(REG_EXT, ext)
|
||||
return filepath
|
||||
})
|
||||
}
|
||||
|
||||
async function createDocsFiles(data: ResolvedData): Promise<File[]> {
|
||||
const fileList: File[] = []
|
||||
if (data.multiLanguage) {
|
||||
const enDocs = await readFiles(getTemplate('docs/en'))
|
||||
const zhDocs = await readFiles(getTemplate('docs/zh'))
|
||||
|
||||
if (data.defaultLanguage === 'en-US') {
|
||||
fileList.push(...enDocs)
|
||||
fileList.push(...updateFileListTarget(zhDocs, 'zh'))
|
||||
}
|
||||
else {
|
||||
fileList.push(...zhDocs)
|
||||
fileList.push(...updateFileListTarget(enDocs, 'en'))
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (data.defaultLanguage === 'en-US')
|
||||
fileList.push(...await readFiles(getTemplate('docs/en')))
|
||||
else
|
||||
fileList.push(...await readFiles(getTemplate('docs/zh')))
|
||||
}
|
||||
|
||||
return updateFileListTarget(fileList, data.docsDir)
|
||||
}
|
||||
|
||||
function updateFileListTarget(fileList: File[], target: string): File[] {
|
||||
return fileList.map(({ filepath, content }) => ({
|
||||
filepath: path.join(target, filepath),
|
||||
content,
|
||||
}))
|
||||
}
|
||||
21
cli/src/index.ts
Normal file
21
cli/src/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import cac from 'cac'
|
||||
import { run } from './run.js'
|
||||
import { Mode } from './constants.js'
|
||||
|
||||
declare const __CLI_VERSION__: string
|
||||
|
||||
const cli = cac('create-vuepress-theme-plume')
|
||||
|
||||
cli
|
||||
.command('[root]', 'create a new vuepress-theme-plume project / 创建新的 vuepress-theme-plume 项目')
|
||||
.action((root: string) => run(Mode.create, root))
|
||||
|
||||
cli
|
||||
.command('init [root]', 'Initial vuepress-theme-plume in the existing project / 在现有项目中初始化 vuepress-theme-plume')
|
||||
.action((root: string) => run(Mode.init, root))
|
||||
|
||||
cli.help()
|
||||
|
||||
cli.version(__CLI_VERSION__)
|
||||
|
||||
cli.parse()
|
||||
25
cli/src/locales/en.ts
Normal file
25
cli/src/locales/en.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import type { Locale } from '../types.js'
|
||||
|
||||
export const en: Locale = {
|
||||
'question.root': 'Where would you want to initialize VuePress?',
|
||||
'question.site.name': 'Site Name:',
|
||||
'question.site.description': 'Site Description:',
|
||||
'question.bundler': 'Select a bundler',
|
||||
'question.multiLanguage': 'Do you want to use multiple languages?',
|
||||
'question.defaultLanguage': 'Select the default language of the site',
|
||||
'question.useTs': 'Use TypeScript?',
|
||||
'question.injectNpmScripts': 'Inject npm scripts?',
|
||||
'question.deploy': 'Deploy type:',
|
||||
'question.git': 'Initialize a git repository?',
|
||||
'question.installDeps': 'Install dependencies?',
|
||||
|
||||
'spinner.start': '🚀 Creating...',
|
||||
'spinner.stop': '🎉 Create success!',
|
||||
'spinner.git': '📄 Initializing git repository...',
|
||||
'spinner.install': '📦 Installing dependencies...',
|
||||
'spinner.command': '🔨 Execute the following command to start:',
|
||||
|
||||
'hint.cancel': 'Operation cancelled.',
|
||||
'hint.root': 'The path cannot be an absolute path, and cannot contain the parent path.',
|
||||
'hint.root.illegal': 'Project names cannot contain special characters.',
|
||||
}
|
||||
8
cli/src/locales/index.ts
Normal file
8
cli/src/locales/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import type { Langs, Locale } from '../types.js'
|
||||
import { en } from './en.js'
|
||||
import { zh } from './zh.js'
|
||||
|
||||
export const locales: Record<Langs, Locale> = {
|
||||
'zh-CN': zh,
|
||||
'en-US': en,
|
||||
}
|
||||
25
cli/src/locales/zh.ts
Normal file
25
cli/src/locales/zh.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import type { Locale } from '../types.js'
|
||||
|
||||
export const zh: Locale = {
|
||||
'question.root': '您想在哪里初始化 VuePress?',
|
||||
'question.site.name': '站点名称:',
|
||||
'question.site.description': '站点描述信息:',
|
||||
'question.bundler': '请选择打包工具',
|
||||
'question.multiLanguage': '是否使用多语言?',
|
||||
'question.defaultLanguage': '请选择站点默认语言',
|
||||
'question.useTs': '是否使用 TypeScript?',
|
||||
'question.injectNpmScripts': '是否注入 npm 脚本?',
|
||||
'question.deploy': '部署方式:',
|
||||
'question.git': '是否初始化 git 仓库?',
|
||||
'question.installDeps': '是否安装依赖?',
|
||||
|
||||
'spinner.start': '🚀 正在创建...',
|
||||
'spinner.stop': '🎉 创建成功!',
|
||||
'spinner.git': '📄 初始化 git 仓库...',
|
||||
'spinner.install': '📦 安装依赖...',
|
||||
'spinner.command': '🔨 执行以下命令即可启动:',
|
||||
|
||||
'hint.cancel': '操作已取消。',
|
||||
'hint.root': '文件路径不能是绝对路径,不能包含父路径。',
|
||||
'hint.root.illegal': '文件夹不能包含特殊字符。',
|
||||
}
|
||||
79
cli/src/packageJson.ts
Normal file
79
cli/src/packageJson.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { execaCommand } from 'execa'
|
||||
import { kebabCase } from '@pengzhanbo/utils'
|
||||
import { getDependenciesVersion, readJsonFile, resolve } from './utils/index.js'
|
||||
import type { File, ResolvedData } from './types.js'
|
||||
import { Mode } from './constants.js'
|
||||
|
||||
export async function createPackageJson(
|
||||
mode: Mode,
|
||||
pkg: Record<string, any>,
|
||||
{
|
||||
docsDir,
|
||||
siteName,
|
||||
siteDescription,
|
||||
bundler,
|
||||
injectNpmScripts,
|
||||
}: ResolvedData,
|
||||
): Promise<File> {
|
||||
if (mode === Mode.create) {
|
||||
pkg.name = kebabCase(siteName)
|
||||
pkg.type = 'module'
|
||||
pkg.version = '1.0.0'
|
||||
pkg.description = siteDescription
|
||||
const userInfo = await getUserInfo()
|
||||
if (userInfo) {
|
||||
pkg.author = userInfo.username + (userInfo.email ? ` <${userInfo.email}>` : '')
|
||||
}
|
||||
pkg.license = 'MIT'
|
||||
}
|
||||
|
||||
if (injectNpmScripts) {
|
||||
pkg.scripts ??= {}
|
||||
pkg.scripts = {
|
||||
...pkg.scripts,
|
||||
'docs:dev': `vuepress dev ${docsDir}`,
|
||||
'docs:dev-clean': `vuepress dev ${docsDir} --clean-cache --clean-temp`,
|
||||
'docs:build': `vuepress build ${docsDir} --clean-cache --clean-temp`,
|
||||
'docs:preview': `http-server ${docsDir}/.vuepress/dist`,
|
||||
}
|
||||
if (mode === Mode.create) {
|
||||
pkg.scripts['vp-update'] = 'vp-update'
|
||||
}
|
||||
}
|
||||
|
||||
pkg.devDependencies ??= {}
|
||||
|
||||
const context = (await readJsonFile(resolve('package.json')))!
|
||||
const meta = context['theme-plume']
|
||||
pkg.devDependencies.vuepress = `${meta.vuepress}`
|
||||
pkg.devDependencies['vuepress-theme-plume'] = `${context.version}`
|
||||
pkg.devDependencies[`@vuepress/bundler-${bundler}`] = `${meta.vuepress}`
|
||||
pkg.devDependencies['http-server'] = '^14.1.1'
|
||||
|
||||
const deps: string[] = []
|
||||
if (!pkg.dependencies?.vue && !pkg.devDependencies.vue)
|
||||
deps.push('vue')
|
||||
if (bundler === 'webpack' && !pkg.dependencies?.['sass-loader'] && !pkg.devDependencies['sass-loader'])
|
||||
deps.push('sass-loader')
|
||||
|
||||
const dv = await getDependenciesVersion(deps)
|
||||
|
||||
for (const [d, v] of Object.entries(dv))
|
||||
pkg.devDependencies[d] = `^${v}`
|
||||
|
||||
return {
|
||||
filepath: 'package.json',
|
||||
content: JSON.stringify(pkg, null, 2),
|
||||
}
|
||||
}
|
||||
|
||||
async function getUserInfo() {
|
||||
try {
|
||||
const { stdout: username } = await execaCommand('git config --global user.name')
|
||||
const { stdout: email } = await execaCommand('git config --global user.email')
|
||||
return { username, email }
|
||||
}
|
||||
catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
165
cli/src/prompt.ts
Normal file
165
cli/src/prompt.ts
Normal file
@ -0,0 +1,165 @@
|
||||
import process from 'node:process'
|
||||
import path from 'node:path'
|
||||
import { createRequire } from 'node:module'
|
||||
import { cancel, confirm, group, select, text } from '@clack/prompts'
|
||||
import { setLang, t } from './translate.js'
|
||||
import type { Bundler, Langs, Options, PromptResult } from './types.js'
|
||||
import { DeployType, Mode, bundlerOptions, deployOptions, languageOptions } from './constants.js'
|
||||
|
||||
const require = createRequire(process.cwd())
|
||||
|
||||
const REG_DIR_CHAR = /[<>:"\\|?*[\]]/
|
||||
|
||||
export async function prompt(mode: Mode, root?: string): Promise<PromptResult> {
|
||||
let hasTs = false
|
||||
if (mode === Mode.init) {
|
||||
try {
|
||||
hasTs = !!require.resolve('typescript')
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
|
||||
const result: PromptResult = await group({
|
||||
displayLang: async () => {
|
||||
const lang = await select<Options<Langs>, Langs>({
|
||||
message: 'Select a language to display / 选择显示语言',
|
||||
options: languageOptions,
|
||||
})
|
||||
|
||||
if (typeof lang === 'string')
|
||||
setLang(lang)
|
||||
|
||||
return lang
|
||||
},
|
||||
|
||||
root: async () => {
|
||||
if (root)
|
||||
return root
|
||||
const DEFAULT_ROOT = mode === Mode.init ? './docs' : './my-project'
|
||||
return await text({
|
||||
message: t('question.root'),
|
||||
placeholder: DEFAULT_ROOT,
|
||||
validate(value) {
|
||||
// not absolute path or parent path
|
||||
if (value?.startsWith('/') || value?.startsWith('..'))
|
||||
return t('hint.root')
|
||||
|
||||
// not contains illegal characters
|
||||
if (value && REG_DIR_CHAR.test(value))
|
||||
return t('hint.root.illegal')
|
||||
|
||||
return undefined
|
||||
},
|
||||
defaultValue: DEFAULT_ROOT,
|
||||
})
|
||||
},
|
||||
|
||||
siteName: () => text({
|
||||
message: t('question.site.name'),
|
||||
placeholder: 'My Vuepress Site',
|
||||
defaultValue: 'My Vuepress Site',
|
||||
}),
|
||||
|
||||
siteDescription: () => text({
|
||||
message: t('question.site.description'),
|
||||
}),
|
||||
|
||||
multiLanguage: () => confirm({
|
||||
message: t('question.multiLanguage'),
|
||||
initialValue: false,
|
||||
}),
|
||||
|
||||
defaultLanguage: () => select<Options<Langs>, Langs>({
|
||||
message: t('question.defaultLanguage'),
|
||||
options: languageOptions,
|
||||
}),
|
||||
|
||||
useTs: async () => {
|
||||
if (mode === Mode.init)
|
||||
return hasTs
|
||||
if (hasTs)
|
||||
return true
|
||||
return await confirm({
|
||||
message: t('question.useTs'),
|
||||
initialValue: true,
|
||||
})
|
||||
},
|
||||
|
||||
injectNpmScripts: async () => {
|
||||
if (mode === Mode.create)
|
||||
return true
|
||||
return await confirm({
|
||||
message: t('question.injectNpmScripts'),
|
||||
initialValue: true,
|
||||
})
|
||||
},
|
||||
|
||||
bundler: () => select<Options<Bundler>, Bundler>({
|
||||
message: t('question.bundler'),
|
||||
options: bundlerOptions,
|
||||
}),
|
||||
|
||||
deploy: async () => {
|
||||
if (mode === Mode.init) {
|
||||
return DeployType.custom
|
||||
}
|
||||
return await select<Options<DeployType>, DeployType>({
|
||||
message: t('question.deploy'),
|
||||
options: deployOptions,
|
||||
initialValue: DeployType.custom,
|
||||
})
|
||||
},
|
||||
|
||||
git: async () => {
|
||||
if (mode === Mode.init)
|
||||
return false
|
||||
return confirm({
|
||||
message: t('question.git'),
|
||||
initialValue: true,
|
||||
})
|
||||
},
|
||||
|
||||
install: () => confirm({
|
||||
message: t('question.installDeps'),
|
||||
initialValue: true,
|
||||
}),
|
||||
}, {
|
||||
onCancel: () => {
|
||||
cancel(t('hint.cancel'))
|
||||
process.exit(0)
|
||||
},
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export async function getTargetDir(cwd: string, dir?: string) {
|
||||
if (dir === '.')
|
||||
return cwd
|
||||
|
||||
if (typeof dir === 'string' && dir) {
|
||||
return path.resolve(cwd, dir)
|
||||
}
|
||||
|
||||
const DEFAULT_DIR = 'my-project'
|
||||
|
||||
const dirPath = await text({
|
||||
message: t('question.root'),
|
||||
placeholder: DEFAULT_DIR,
|
||||
validate(value) {
|
||||
if (value && REG_DIR_CHAR.test(value))
|
||||
return t('hint.root.illegal')
|
||||
|
||||
return undefined
|
||||
},
|
||||
defaultValue: DEFAULT_DIR,
|
||||
})
|
||||
|
||||
if (typeof dirPath === 'string') {
|
||||
if (dirPath === '.')
|
||||
return cwd
|
||||
|
||||
return path.join(cwd, dirPath || DEFAULT_DIR)
|
||||
}
|
||||
return dirPath
|
||||
}
|
||||
40
cli/src/render.ts
Normal file
40
cli/src/render.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import handlebars from 'handlebars'
|
||||
import { kebabCase } from '@pengzhanbo/utils'
|
||||
import type { ResolvedData } from './types.js'
|
||||
|
||||
export interface RenderData extends ResolvedData {
|
||||
name: string
|
||||
siteName: string
|
||||
locales: { path: string, lang: string, isEn: boolean, prefix: string }[]
|
||||
isEN: boolean
|
||||
}
|
||||
|
||||
handlebars.registerHelper('removeLeadingSlash', (path: string) => path.replace(/^\//, ''))
|
||||
handlebars.registerHelper('equal', (a: string, b: string) => a === b)
|
||||
|
||||
export function createRender(result: ResolvedData) {
|
||||
const data: RenderData = {
|
||||
...result,
|
||||
name: kebabCase(result.siteName),
|
||||
isEN: result.defaultLanguage === 'en-US',
|
||||
locales: result.defaultLanguage === 'en-US'
|
||||
? [
|
||||
{ path: '/', lang: 'en-US', isEn: true, prefix: 'en' },
|
||||
{ path: '/zh/', lang: 'zh-CN', isEn: false, prefix: 'zh' },
|
||||
]
|
||||
: [
|
||||
{ path: '/', lang: 'zh-CN', isEn: false, prefix: 'zh' },
|
||||
{ path: '/en/', lang: 'en-US', isEn: true, prefix: 'en' },
|
||||
],
|
||||
}
|
||||
return function render(source: string): string {
|
||||
try {
|
||||
const template = handlebars.compile(source)
|
||||
return template(data)
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e)
|
||||
return source
|
||||
}
|
||||
}
|
||||
}
|
||||
54
cli/src/run.ts
Normal file
54
cli/src/run.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { intro, outro, spinner } from '@clack/prompts'
|
||||
import { execaCommand } from 'execa'
|
||||
import colors from 'picocolors'
|
||||
import { prompt } from './prompt.js'
|
||||
import { generate } from './generate.js'
|
||||
import { t } from './translate.js'
|
||||
import { Mode } from './constants.js'
|
||||
import type { PromptResult, ResolvedData } from './types.js'
|
||||
import { getPackageManager } from './utils/index.js'
|
||||
|
||||
export async function run(mode: Mode, root?: string) {
|
||||
intro(colors.cyan('Welcome to VuePress and vuepress-theme-plume !'))
|
||||
|
||||
const result = await prompt(mode, root)
|
||||
const data = resolveData(result, mode)
|
||||
|
||||
const progress = spinner()
|
||||
progress.start(t('spinner.start'))
|
||||
|
||||
await generate(mode, data)
|
||||
|
||||
if (data.git) {
|
||||
progress.message(t('spinner.git'))
|
||||
await execaCommand('git init')
|
||||
}
|
||||
|
||||
const pm = data.packageManager
|
||||
|
||||
if (data.install) {
|
||||
progress.message(t('spinner.install'))
|
||||
await execaCommand(pm === 'yarn' ? 'yarn' : `${pm} install`)
|
||||
}
|
||||
|
||||
const cdCommand = mode === Mode.create ? colors.green(`cd ${data.root}`) : ''
|
||||
const runCommand = colors.green(pm === 'yarn' ? 'yarn dev' : `${pm} run dev`)
|
||||
const installCommand = colors.green(pm === 'yarn' ? 'yarn' : `${pm} install`)
|
||||
|
||||
progress.stop(t('spinner.stop'))
|
||||
|
||||
if (mode === Mode.create) {
|
||||
outro(`${t('spinner.command')}
|
||||
${cdCommand}
|
||||
${data.install ? '' : `${installCommand} && `}${runCommand}`)
|
||||
}
|
||||
}
|
||||
|
||||
function resolveData(result: PromptResult, mode: Mode): ResolvedData {
|
||||
return {
|
||||
...result,
|
||||
packageManager: getPackageManager(),
|
||||
docsDir: mode === Mode.create ? 'docs' : result.root.replace(/^\.\//, '').replace(/\/$/, ''),
|
||||
siteDescription: result.siteDescription || '',
|
||||
}
|
||||
}
|
||||
15
cli/src/translate.ts
Normal file
15
cli/src/translate.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { Langs, Locale } from './types.js'
|
||||
import { locales } from './locales/index.js'
|
||||
|
||||
function createTranslate(lang?: Langs) {
|
||||
let current: Langs = lang || 'en-US'
|
||||
|
||||
return {
|
||||
setLang: (lang: Langs) => {
|
||||
current = lang
|
||||
},
|
||||
t: (key: keyof Locale) => locales[current][key],
|
||||
}
|
||||
}
|
||||
|
||||
export const { t, setLang } = createTranslate()
|
||||
57
cli/src/types.ts
Normal file
57
cli/src/types.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import type { DeployType } from './constants.js'
|
||||
|
||||
export type Langs = 'zh-CN' | 'en-US'
|
||||
|
||||
export interface Locale {
|
||||
'question.root': string
|
||||
'question.site.name': string
|
||||
'question.site.description': string
|
||||
'question.multiLanguage': string
|
||||
'question.defaultLanguage': string
|
||||
'question.bundler': string
|
||||
'question.useTs': string
|
||||
'question.injectNpmScripts': string
|
||||
'question.git': string
|
||||
'question.deploy': string
|
||||
'question.installDeps': string
|
||||
|
||||
'spinner.start': string
|
||||
'spinner.stop': string
|
||||
'spinner.git': string
|
||||
'spinner.install': string
|
||||
'spinner.command': string
|
||||
|
||||
'hint.cancel': string
|
||||
'hint.root': string
|
||||
'hint.root.illegal': string
|
||||
}
|
||||
|
||||
export type PackageManager = 'npm' | 'yarn' | 'pnpm'
|
||||
export type Bundler = 'vite' | 'webpack'
|
||||
|
||||
export type Options<Value = string, Label = string> = { label: Label, value: Value }[]
|
||||
|
||||
export interface File {
|
||||
filepath: string
|
||||
content: string
|
||||
}
|
||||
|
||||
export interface PromptResult {
|
||||
displayLang: string // cli display language
|
||||
root: string
|
||||
siteName: string
|
||||
siteDescription: string
|
||||
bundler: Bundler
|
||||
multiLanguage: boolean
|
||||
defaultLanguage: Langs
|
||||
useTs: boolean
|
||||
injectNpmScripts: boolean
|
||||
deploy: DeployType
|
||||
git: boolean
|
||||
install: boolean
|
||||
}
|
||||
|
||||
export interface ResolvedData extends PromptResult {
|
||||
packageManager: PackageManager
|
||||
docsDir: string
|
||||
}
|
||||
16
cli/src/utils/depsVersion.ts
Normal file
16
cli/src/utils/depsVersion.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export type DependencyVersion = 'latest' | 'next' | 'pre' | string
|
||||
|
||||
const api = 'https://api.pengzhanbo.cn/npm/dependencies/version'
|
||||
|
||||
export async function getDependenciesVersion(
|
||||
dependencies: string[],
|
||||
version: DependencyVersion = 'latest',
|
||||
): Promise<Record<string, string>> {
|
||||
const result = await fetch(api, {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ dependencies, version }),
|
||||
}).then(res => res.json())
|
||||
|
||||
return result
|
||||
}
|
||||
43
cli/src/utils/fs.ts
Normal file
43
cli/src/utils/fs.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import path from 'node:path'
|
||||
import fs from 'node:fs/promises'
|
||||
import type { File } from '../types.js'
|
||||
|
||||
export async function readFiles(root: string): Promise<File[]> {
|
||||
const filepaths = await fs.readdir(root, { recursive: true })
|
||||
const files: File[] = []
|
||||
for (const file of filepaths) {
|
||||
const filepath = path.join(root, file)
|
||||
if ((await fs.stat(filepath)).isFile()) {
|
||||
files.push({
|
||||
filepath: file,
|
||||
content: await fs.readFile(filepath, 'utf-8'),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
export async function writeFiles(
|
||||
files: File[],
|
||||
target: string,
|
||||
rewrite?: (path: string) => string,
|
||||
) {
|
||||
for (const { filepath, content } of files) {
|
||||
let root = path.join(target, filepath).replace(/\.handlebars$/, '')
|
||||
if (rewrite)
|
||||
root = rewrite(root)
|
||||
await fs.mkdir(path.dirname(root), { recursive: true })
|
||||
await fs.writeFile(root, content)
|
||||
}
|
||||
}
|
||||
|
||||
export async function readJsonFile<T extends Record<string, any> = Record<string, any>>(filepath: string): Promise<T | null> {
|
||||
try {
|
||||
const content = await fs.readFile(filepath, 'utf-8')
|
||||
return JSON.parse(content)
|
||||
}
|
||||
catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
7
cli/src/utils/getPackageManager.ts
Normal file
7
cli/src/utils/getPackageManager.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import process from 'node:process'
|
||||
import type { PackageManager } from '../types.js'
|
||||
|
||||
export function getPackageManager(): PackageManager {
|
||||
const name = process.env?.npm_config_user_agent || 'npm'
|
||||
return name.split('/')[0] as PackageManager
|
||||
}
|
||||
12
cli/src/utils/index.ts
Normal file
12
cli/src/utils/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'node:path'
|
||||
|
||||
export const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
export const resolve = (...args: string[]) => path.resolve(__dirname, '../', ...args)
|
||||
|
||||
export const getTemplate = (dir: string) => resolve('templates', dir)
|
||||
|
||||
export * from './fs.js'
|
||||
export * from './depsVersion.js'
|
||||
export * from './getPackageManager.js'
|
||||
12
cli/templates/.vuepress/client.ts.handlebars
Normal file
12
cli/templates/.vuepress/client.ts.handlebars
Normal file
@ -0,0 +1,12 @@
|
||||
import { defineClientConfig } from 'vuepress/client'
|
||||
import RepoCard from 'vuepress-theme-plume/features/RepoCard.vue'
|
||||
import CustomComponent from './theme/components/Custom.vue'
|
||||
|
||||
import './theme/styles/custom.css'
|
||||
|
||||
export default defineClientConfig({
|
||||
enhance({ app }) {
|
||||
app.component('RepoCard', RepoCard)
|
||||
app.component('CustomComponent', CustomComponent)
|
||||
},
|
||||
})
|
||||
90
cli/templates/.vuepress/config.ts.handlebars
Normal file
90
cli/templates/.vuepress/config.ts.handlebars
Normal file
@ -0,0 +1,90 @@
|
||||
import { defineUserConfig } from 'vuepress'
|
||||
import { {{ bundler }}Bundler } from '@vuepress/bundler-{{ bundler }}'
|
||||
import { plumeTheme } from 'vuepress-theme-plume'
|
||||
|
||||
export default defineUserConfig({
|
||||
base: '/',
|
||||
lang: '{{ defaultLanguage }}',
|
||||
{{#if multiLanguage}}
|
||||
locales: {
|
||||
{{#each locales}}
|
||||
'{{ path }}': {
|
||||
title: '{{ ../siteName }}',
|
||||
lang: '{{ lang }}',
|
||||
description: '{{ ../siteDescription }}',
|
||||
},
|
||||
{{/each}}
|
||||
},
|
||||
{{else}}
|
||||
title: '{{ siteName }}',
|
||||
description: '{{ siteDescription }}',
|
||||
{{/if}}
|
||||
|
||||
bundler: {{ bundler }}Bundler(),
|
||||
|
||||
theme: plumeTheme({
|
||||
// 添加您的部署域名
|
||||
// hostname: 'https://your_site_url',
|
||||
|
||||
plugins: {
|
||||
/**
|
||||
* Shiki 代码高亮
|
||||
* @see https://theme-plume.vuejs.press/config/plugins/code-highlight/
|
||||
*/
|
||||
// shiki: {
|
||||
// languages: ['shell', 'bash', 'typescript', 'javascript'],
|
||||
// twoslash: true,
|
||||
// },
|
||||
|
||||
/**
|
||||
* markdown enhance
|
||||
* @see https://theme-plume.vuejs.press/config/plugins/markdown-enhance/
|
||||
*/
|
||||
markdownEnhance: {
|
||||
demo: true,
|
||||
// include: true,
|
||||
// chart: true,
|
||||
// echarts: true,
|
||||
// mermaid: true,
|
||||
// flowchart: true,
|
||||
},
|
||||
|
||||
/**
|
||||
* markdown power
|
||||
* @see https://theme-plume.vuejs.press/config/plugin/markdown-power/
|
||||
*/
|
||||
// markdownPower: {
|
||||
// pdf: true,
|
||||
// caniuse: true,
|
||||
// plot: true,
|
||||
// bilibili: true,
|
||||
// youtube: true,
|
||||
// icons: true,
|
||||
// codepen: true,
|
||||
// replit: true,
|
||||
// codeSandbox: true,
|
||||
// jsfiddle: true,
|
||||
// repl: {
|
||||
// go: true,
|
||||
// rust: true,
|
||||
// kotlin: true,
|
||||
// },
|
||||
// },
|
||||
|
||||
/**
|
||||
* comments
|
||||
* @see https://theme-plume.vuejs.press/guide/features/comments/
|
||||
*/
|
||||
// comment: {
|
||||
// provider: '', // "Artalk" | "Giscus" | "Twikoo" | "Waline"
|
||||
// comment: true,
|
||||
// repo: '',
|
||||
// repoId: '',
|
||||
// categoryId: '',
|
||||
// mapping: 'pathname',
|
||||
// reactionsEnabled: true,
|
||||
// inputPosition: 'top',
|
||||
// },
|
||||
},
|
||||
}),
|
||||
})
|
||||
28
cli/templates/.vuepress/navbar.ts.handlebars
Normal file
28
cli/templates/.vuepress/navbar.ts.handlebars
Normal file
@ -0,0 +1,28 @@
|
||||
import { defineNavbarConfig } from 'vuepress-theme-plume'
|
||||
|
||||
{{#if multiLanguage}}
|
||||
{{#each locales}}
|
||||
export const {{prefix}}Navbar = defineNavbarConfig([
|
||||
{ text: '{{#if isEn}}Home{{else}}首页{{/if}}', link: '{{ path }}' },
|
||||
{ text: '{{#if isEn}}Blog{{else}}博客{{/if}}', link: '{{ path }}blog/' },
|
||||
{ text: '{{#if isEn}}Tags{{else}}标签{{/if}}', link: '{{ path }}blog/tags/' },
|
||||
{ text: '{{#if isEn}}Archives{{else}}归档{{/if}}', link: '{{ path }}blog/archives/' },
|
||||
{
|
||||
text: '{{#if isEn}}Notes{{else}}笔记{{/if}}',
|
||||
items: [{ text: '{{#if isEn}}Demo{{else}}示例{{/if}}', link: '{{ path }}notes/demo/README.md' }]
|
||||
},
|
||||
])
|
||||
|
||||
{{/each}}
|
||||
{{else}}
|
||||
export const navbar = defineNavbarConfig([
|
||||
{ text: '{{#if isEn}}Home{{else}}首页{{/if}}', link: '/' },
|
||||
{ text: '{{#if isEn}}Blog{{else}}博客{{/if}}', link: '/blog/' },
|
||||
{ text: '{{#if isEn}}Tags{{else}}标签{{/if}}', link: '/blog/tags/' },
|
||||
{ text: '{{#if isEn}}Archives{{else}}归档{{/if}}', link: '/blog/archives/' },
|
||||
{
|
||||
text: '{{#if isEn}}Notes{{else}}笔记{{/if}}',
|
||||
items: [{ text: '{{#if isEn}}Demo{{else}}示例{{/if}}', link: '/notes/demo/README.md' }]
|
||||
},
|
||||
])
|
||||
{{/if}}
|
||||
32
cli/templates/.vuepress/notes.ts.handlebars
Normal file
32
cli/templates/.vuepress/notes.ts.handlebars
Normal file
@ -0,0 +1,32 @@
|
||||
import { defineNoteConfig, defineNotesConfig } from 'vuepress-theme-plume'
|
||||
|
||||
{{#if multiLanguage}}
|
||||
{{#each locales}}
|
||||
/* =================== locale: {{ lang }} ======================= */
|
||||
|
||||
const {{ prefix }}DemoNote = defineNoteConfig({
|
||||
dir: 'demo',
|
||||
link: '/demo',
|
||||
sidebar: ['', 'foo', 'bar'],
|
||||
})
|
||||
|
||||
export const {{ prefix }}Notes = defineNotesConfig({
|
||||
dir: '{{ removeLeadingSlash path }}notes',
|
||||
link: '{{ path }}',
|
||||
notes: [{{ prefix }}DemoNote],
|
||||
})
|
||||
|
||||
{{/each}}
|
||||
{{else}}
|
||||
const demoNote = defineNoteConfig({
|
||||
dir: 'demo',
|
||||
link: '/demo',
|
||||
sidebar: ['', 'foo', 'bar'],
|
||||
})
|
||||
|
||||
export const notes = defineNotesConfig({
|
||||
dir: 'notes',
|
||||
link: '/',
|
||||
notes: [demoNote],
|
||||
})
|
||||
{{/if}}
|
||||
57
cli/templates/.vuepress/plume.config.ts.handlebars
Normal file
57
cli/templates/.vuepress/plume.config.ts.handlebars
Normal file
@ -0,0 +1,57 @@
|
||||
import { defineThemeConfig } from 'vuepress-theme-plume'
|
||||
{{#if multiLanguage}}
|
||||
import { enNavbar, zhNavbar } from './navbar'
|
||||
import { enNotes, zhNotes } from './notes'
|
||||
{{else}}
|
||||
import { navbar } from './navbar'
|
||||
import { notes } from './notes'
|
||||
{{/if}}
|
||||
|
||||
/**
|
||||
* @see https://theme-plume.vuejs.press/config/basic/
|
||||
*/
|
||||
export default defineThemeConfig({
|
||||
logo: 'https://theme-plume.vuejs.press/plume.png',
|
||||
// your git repo url
|
||||
docsRepo: '',
|
||||
docsDir: '{{ docsDir }}',
|
||||
|
||||
appearance: true,
|
||||
|
||||
{{#unless multiLanguage}}
|
||||
profile: {
|
||||
avatar: 'https://theme-plume.vuejs.press/plume.png',
|
||||
name: '{{ siteName }}',
|
||||
description: '{{ siteDescription }}',
|
||||
// circle: true,
|
||||
// location: '',
|
||||
// organization: '',
|
||||
},
|
||||
|
||||
navbar,
|
||||
notes,
|
||||
{{/unless}}
|
||||
social: [
|
||||
{ icon: 'github', link: '/' },
|
||||
],
|
||||
|
||||
{{#if multiLanguage}}
|
||||
locales: {
|
||||
{{#each locales}}
|
||||
'{{ path }}': {
|
||||
profile: {
|
||||
avatar: 'https://theme-plume.vuejs.press/plume.png',
|
||||
name: '{{ ../siteName }}',
|
||||
description: '{{ ../siteDescription }}',
|
||||
// circle: true,
|
||||
// location: '',
|
||||
// organization: '',
|
||||
},
|
||||
|
||||
navbar: {{ prefix }}Navbar,
|
||||
notes: {{ prefix }}Notes,
|
||||
},
|
||||
{{/each}}
|
||||
},
|
||||
{{/if}}
|
||||
})
|
||||
8
cli/templates/.vuepress/public/plume.svg
Normal file
8
cli/templates/.vuepress/public/plume.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 72 72">
|
||||
<path fill="#5086a1" d="M42.334 49.147a29.945 29.945 0 0 1-19.338-8.151c-8.014-7.365-8.378-18.076-8.533-22.649l-.022-.627a2.904 2.904 0 0 1 3.457-2.951c17.005 3.355 21.695 16.324 22.056 17.4a49.543 49.543 0 0 1 3.574 15.922a1 1 0 0 1-.967 1.052c-.029.001-.106.004-.227.004" />
|
||||
<path fill="#8cccd5" d="M44.436 55.316c-11.646 0-17.376-6.974-17.653-7.354a1 1 0 0 1 .262-1.424a11.103 11.103 0 0 1 12.774-1.574c-1.465-9.078 1.877-13.568 2.031-13.77a.998.998 0 0 1 .75-.39a.97.97 0 0 1 .78.325c8.944 9.771 8.793 16.532 7.908 19.691c-.034.14-1.062 4.092-4.772 4.406c-.711.062-1.405.09-2.08.09" />
|
||||
<g fill="none" stroke="#333" stroke-linecap="round" stroke-linejoin="round" stroke-width="1">
|
||||
<path d="M55.184 57.69S34.96 45.877 23.097 24.206m22.131 30.096c-11.93.46-17.628-6.88-17.628-6.88" />
|
||||
<path d="M40.528 42.483c-.56-7.195 2.116-10.679 2.116-10.679c8.834 9.654 8.406 16.162 7.681 18.747m-13.311-3.129a30.15 30.15 0 0 1-13.341-7.162c-8.072-7.419-8.067-18.241-8.232-22.577a1.903 1.903 0 0 1 2.264-1.932C34.694 19.103 39.02 32.528 39.02 32.528" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
11
cli/templates/.vuepress/theme/components/Custom.vue
Normal file
11
cli/templates/.vuepress/theme/components/Custom.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const message = ref('Hello World!')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="my-custom-content">
|
||||
{{ message }}
|
||||
</div>
|
||||
</template>
|
||||
6
cli/templates/.vuepress/theme/shim.d.ts
vendored
Normal file
6
cli/templates/.vuepress/theme/shim.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
declare module '*.vue' {
|
||||
import type { ComponentOptions } from 'vue'
|
||||
|
||||
const comp: ComponentOptions
|
||||
export default comp
|
||||
}
|
||||
50
cli/templates/.vuepress/theme/styles/custom.css
Normal file
50
cli/templates/.vuepress/theme/styles/custom.css
Normal file
@ -0,0 +1,50 @@
|
||||
:root {
|
||||
/** 主题颜色 */
|
||||
|
||||
/*
|
||||
--vp-c-brand-1: #5086a1;
|
||||
--vp-c-brand-2: #6aa1b7;
|
||||
--vp-c-brand-3: #8cccd5;
|
||||
--vp-c-brand-soft: rgba(131, 208, 218, 0.314);
|
||||
*/
|
||||
|
||||
/** 背景颜色 */
|
||||
|
||||
/*
|
||||
--vp-c-bg: #fff;
|
||||
--vp-c-bg-alt: #f6f6f7;
|
||||
--vp-c-bg-elv: #fff;
|
||||
--vp-c-bg-soft: #f6f6f7;
|
||||
*/
|
||||
|
||||
/** 文本颜色 */
|
||||
|
||||
/*
|
||||
--vp-c-text-1: rgba(60, 60, 67);
|
||||
--vp-c-text-2: rgba(60, 60, 67, 0.78);
|
||||
--vp-c-text-3: rgba(60, 60, 67, 0.56);
|
||||
*/
|
||||
}
|
||||
|
||||
/** 深色模式 */
|
||||
.dark {
|
||||
/*
|
||||
--vp-c-brand-1: #8cccd5;
|
||||
--vp-c-brand-2: #6aa1b7;
|
||||
--vp-c-brand-3: #5086a1;
|
||||
--vp-c-brand-soft: rgba(131, 208, 218, 0.314);
|
||||
*/
|
||||
|
||||
/*
|
||||
--vp-c-bg: #1b1b1f;
|
||||
--vp-c-bg-alt: #161618;
|
||||
--vp-c-bg-elv: #202127;
|
||||
--vp-c-bg-soft: #202127;
|
||||
*/
|
||||
|
||||
/*
|
||||
--vp-c-text-1: rgba(255, 255, 245, 0.86);
|
||||
--vp-c-text-2: rgba(235, 235, 245, 0.6);
|
||||
--vp-c-text-3: rgba(235, 235, 245, 0.38);
|
||||
*/
|
||||
}
|
||||
57
cli/templates/common/README.md.handlebars
Normal file
57
cli/templates/common/README.md.handlebars
Normal file
@ -0,0 +1,57 @@
|
||||
# {{ name }}
|
||||
|
||||
The Site is generated using [vuepress](https://vuepress.vuejs.org/) and [vuepress-theme-plume](https://github.com/pengzhanbo/vuepress-theme-plume)
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
{{#if (equal packageManager "pnpm")}}
|
||||
pnpm i
|
||||
{{else if (equal packageManager "yarn")}}
|
||||
yarn
|
||||
{{else}}
|
||||
npm i
|
||||
{{/if}}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
{{#if (equal packageManager "pnpm")}}
|
||||
```sh
|
||||
# start dev server
|
||||
pnpm docs:dev
|
||||
# build for production
|
||||
pnpm docs:build
|
||||
# preview production build in local
|
||||
pnpm docs:preview
|
||||
# update vuepress and theme
|
||||
pnpm vp-update
|
||||
```
|
||||
{{else if (equal packageManager "yarn")}}
|
||||
```sh
|
||||
# start dev server
|
||||
yarn docs:dev
|
||||
# build for production
|
||||
yarn docs:build
|
||||
# preview production build in local
|
||||
yarn docs:preview
|
||||
# update vuepress and theme
|
||||
yarn vp-update
|
||||
```
|
||||
{{else}}
|
||||
```sh
|
||||
# start dev server
|
||||
npm run docs:dev
|
||||
# build for production
|
||||
npm run docs:build
|
||||
# preview production build in local
|
||||
npm run docs:preview
|
||||
# update vuepress and theme
|
||||
npm run vp-update
|
||||
```
|
||||
{{/if}}
|
||||
|
||||
## Documents
|
||||
|
||||
- [vuepress](https://vuepress.vuejs.org/)
|
||||
- [vuepress-theme-plume](https://theme-plume.vuejs.press/)
|
||||
57
cli/templates/common/README.zh-CN.md.handlebars
Normal file
57
cli/templates/common/README.zh-CN.md.handlebars
Normal file
@ -0,0 +1,57 @@
|
||||
# {{ name }}
|
||||
|
||||
网站使用 [vuepress](https://vuepress.vuejs.org/) 和 [vuepress-theme-plume](https://github.com/pengzhanbo/vuepress-theme-plume) 构建生成。
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
{{#if (equal packageManager "pnpm")}}
|
||||
pnpm i
|
||||
{{else if (equal packageManager "yarn")}}
|
||||
yarn
|
||||
{{else}}
|
||||
npm i
|
||||
{{/if}}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
{{#if (equal packageManager "pnpm")}}
|
||||
```sh
|
||||
# 启动开发服务
|
||||
pnpm docs:dev
|
||||
# 构建生产包
|
||||
pnpm docs:build
|
||||
# 本地预览生产服务
|
||||
pnpm docs:preview
|
||||
# 更新 vuepress 和主题
|
||||
pnpm vp-update
|
||||
```
|
||||
{{else if (equal packageManager "yarn")}}
|
||||
```sh
|
||||
# 启动开发服务
|
||||
yarn docs:dev
|
||||
# 构建生产包
|
||||
yarn docs:build
|
||||
# 本地预览生产服务
|
||||
yarn docs:preview
|
||||
# update vuepress and theme
|
||||
yarn vp-update
|
||||
```
|
||||
{{else}}
|
||||
```sh
|
||||
# 启动开发服务
|
||||
npm run docs:dev
|
||||
# 构建生产包
|
||||
npm run docs:build
|
||||
# 本地预览生产服务
|
||||
npm run docs:preview
|
||||
# 更新 vuepress 和主题
|
||||
npm run vp-update
|
||||
```
|
||||
{{/if}}
|
||||
|
||||
## 文档
|
||||
|
||||
- [vuepress](https://vuepress.vuejs.org/)
|
||||
- [vuepress-theme-plume](https://theme-plume.vuejs.press/)
|
||||
70
cli/templates/deploy/github/.github/workflows/deploy.yml.handlebars
vendored
Normal file
70
cli/templates/deploy/github/.github/workflows/deploy.yml.handlebars
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
name: deploy
|
||||
|
||||
on:
|
||||
# 每当 push 到 main 分支时触发部署
|
||||
# Deployment is triggered whenever a push is made to the main branch.
|
||||
push:
|
||||
branches: [main]
|
||||
# 手动触发部署
|
||||
# Manually trigger deployment
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# “最近更新时间” 等 git 日志相关信息,需要拉取全部提交记录
|
||||
# "Last updated time" and other git log-related information require fetching all commit records.
|
||||
fetch-depth: 0
|
||||
|
||||
{{#if (equal packageManager "pnpm")}}
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
# 选择要使用的 pnpm 版本
|
||||
version: 9
|
||||
# 使用 pnpm 安装依赖
|
||||
run_install: true
|
||||
{{/if}}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
# 选择要使用的 node 版本
|
||||
node-version: 20
|
||||
|
||||
{{#if (equal packageManager "yarn")}}
|
||||
- name: Run install
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
cmd: install
|
||||
{{/if}}
|
||||
|
||||
# 运行构建脚本
|
||||
# Run the build script
|
||||
{{#unless (equal packageManager "yarn")}}
|
||||
- name: Build VuePress site
|
||||
run: {{packageManager}} run docs:build
|
||||
{{/unless}}
|
||||
{{#if (equal packageManager "yarn")}}
|
||||
- name: Build VuePress site
|
||||
uses: borales/actions-yarn@v4
|
||||
with:
|
||||
cmd: docs:build
|
||||
{{/if}}
|
||||
|
||||
|
||||
# 查看 workflow 的文档来获取更多信息
|
||||
# @see https://github.com/crazy-max/ghaction-github-pages
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: crazy-max/ghaction-github-pages@v4
|
||||
with:
|
||||
# 部署到 gh-pages 分支
|
||||
target_branch: gh-pages
|
||||
# 部署目录为 VuePress 的默认输出目录
|
||||
build_dir: {{docsDir}}/.vuepress/dist
|
||||
env:
|
||||
# @see https://docs.github.com/cn/actions/reference/authentication-in-a-workflow#about-the-github_token-secret
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
9
cli/templates/deploy/netlify/netlify.toml.handlebars
Normal file
9
cli/templates/deploy/netlify/netlify.toml.handlebars
Normal file
@ -0,0 +1,9 @@
|
||||
# prevent Netlify npm install
|
||||
|
||||
[build]
|
||||
publish = "{{ docsDir }}/.vuepress/dist"
|
||||
command = "{{#if (equal packageManager 'yarn')}}yarn && yarn{{else}}{{packageManager}} run{{/if}} docs:build"
|
||||
|
||||
[build.environment]
|
||||
NODE_VERSION = "20"
|
||||
NPM_FLAGS = "--version"
|
||||
6
cli/templates/deploy/vercel/vercel.json.handlebars
Normal file
6
cli/templates/deploy/vercel/vercel.json.handlebars
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"framework": null,
|
||||
"buildCommand": "{{#if (equal packageManager 'yarn')}}yarn{{else}}{{packageManager}} run{{/if}} docs:build",
|
||||
"installCommand": "{{#if (equal packageManager 'yarn')}}yarn{{else}}{{packageManager}} install{{/if}}",
|
||||
"outputDirectory": "{{ docsDir }}/.vuepress/dist"
|
||||
}
|
||||
22
cli/templates/docs/en/README.md.handlebars
Normal file
22
cli/templates/docs/en/README.md.handlebars
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
pageLayout: home
|
||||
externalLinkIcon: false
|
||||
config:
|
||||
-
|
||||
type: hero
|
||||
full: true
|
||||
background: tint-plate
|
||||
hero:
|
||||
name: Theme Plume
|
||||
tagline: VuePress Next Theme
|
||||
text: A simple, feature-rich, document & blog
|
||||
actions:
|
||||
-
|
||||
theme: brand
|
||||
text: Blog
|
||||
link: {{#if (equal defaultLanguage 'en-US')}}/{{else}}/en/{{/if}}blog/
|
||||
-
|
||||
theme: alt
|
||||
text: Github →
|
||||
link: https://github.com/pengzhanbo/vuepress-theme-plume
|
||||
---
|
||||
6
cli/templates/docs/en/notes/demo/README.md
Normal file
6
cli/templates/docs/en/notes/demo/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Demo
|
||||
---
|
||||
|
||||
- [bar](./bar.md)
|
||||
- [foo](./foo.md)
|
||||
5
cli/templates/docs/en/notes/demo/bar.md
Normal file
5
cli/templates/docs/en/notes/demo/bar.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
title: bar
|
||||
---
|
||||
|
||||
[foo](./foo.md)
|
||||
5
cli/templates/docs/en/notes/demo/foo.md
Normal file
5
cli/templates/docs/en/notes/demo/foo.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
title: foo
|
||||
---
|
||||
|
||||
[bar](./bar.md)
|
||||
@ -0,0 +1,8 @@
|
||||
---
|
||||
title: Custom Component
|
||||
tags:
|
||||
- preview
|
||||
- component
|
||||
---
|
||||
|
||||
<CustomComponent />
|
||||
312
cli/templates/docs/en/preview/markdown.md
Normal file
312
cli/templates/docs/en/preview/markdown.md
Normal file
@ -0,0 +1,312 @@
|
||||
---
|
||||
title: Markdown
|
||||
tags:
|
||||
- markdown
|
||||
---
|
||||
|
||||
## Heading 2
|
||||
|
||||
### Heading 3
|
||||
|
||||
#### Heading 4
|
||||
|
||||
##### Heading 5
|
||||
|
||||
###### Heading 6
|
||||
|
||||
Bold: **Bold text**
|
||||
|
||||
Italic: _Italic text_
|
||||
|
||||
~~Deleted text~~
|
||||
|
||||
Content ==Highlight==
|
||||
|
||||
Mathematical expression: $-(2^{n-1})$ ~ $2^{n-1} -1$
|
||||
|
||||
$\frac {\partial^r} {\partial \omega^r} \left(\frac {y^{\omega}} {\omega}\right)
|
||||
= \left(\frac {y^{\omega}} {\omega}\right) \left\{(\log y)^r + \sum_{i=1}^r \frac {(-1)^ Ir \cdots (r-i+1) (\log y)^{ri}} {\omega^i} \right\}$
|
||||
|
||||
19^th^
|
||||
|
||||
H~2~O
|
||||
|
||||
::: center
|
||||
content center
|
||||
:::
|
||||
|
||||
::: right
|
||||
content right
|
||||
:::
|
||||
|
||||
- Unordered List 1
|
||||
- Unordered List 2
|
||||
- Unordered List 3
|
||||
|
||||
1. Ordered List 1
|
||||
2. Ordered List 2
|
||||
3. Ordered List 3
|
||||
|
||||
- [ ] Task List 1
|
||||
- [ ] Task List 2
|
||||
- [x] Task List 3
|
||||
- [x] Task List 4
|
||||
|
||||
| Tables | Are | Cool |
|
||||
| ------------- |:-------------:| -----:|
|
||||
| col 3 is | right-aligned | $1600 |
|
||||
| col 2 is | centered | $12 |
|
||||
| zebra stripes | are neat | $1 |
|
||||
|
||||
> quote content
|
||||
>
|
||||
> quote content
|
||||
|
||||
[links](/)
|
||||
|
||||
[outside links](https://github.com/pengzhanbo)
|
||||
|
||||
**Badge:**
|
||||
|
||||
- <Badge type="info" text="info badge" />
|
||||
- <Badge type="tip" text="tip badge" />
|
||||
- <Badge type="warning" text="warning badge" />
|
||||
- <Badge type="danger" text="danger badge" />
|
||||
|
||||
**icons:**
|
||||
|
||||
- home - <Icon name="material-symbols:home" color="currentColor" size="1em" />
|
||||
- vscode - <Icon name="skill-icons:vscode-dark" size="2em" />
|
||||
- twitter - <Icon name="skill-icons:twitter" size="2em" />
|
||||
|
||||
**demo wrapper:**
|
||||
|
||||
::: demo-wrapper title="Demo" no-padding height="200px"
|
||||
<style scoped>
|
||||
.open-door {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
.open-door .main {
|
||||
background: #ccc;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="open-door">
|
||||
<div class="main">main</div>
|
||||
<div class="aside">aside</div>
|
||||
</div>
|
||||
|
||||
:::
|
||||
|
||||
**code block:**
|
||||
|
||||
```js whitespace
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
|
||||
// [!code word:obj]
|
||||
const obj = {
|
||||
toLong: {
|
||||
deep: {
|
||||
deep: {
|
||||
deep: {
|
||||
value: 'this is to long text. this is to long text. this is to long text. this is to long text.', // [!code highlight]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**code groups:**
|
||||
|
||||
::: code-tabs
|
||||
@tab tab1
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
@tab tab2
|
||||
|
||||
```ts
|
||||
const a: number = 1
|
||||
const b: number = 2
|
||||
const c: number = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**code highlight:**
|
||||
|
||||
```ts
|
||||
function foo() {
|
||||
const a = 1 // [!code highlight]
|
||||
|
||||
console.log(a)
|
||||
|
||||
const b = 2 // [!code ++]
|
||||
const c = 3 // [!code --]
|
||||
|
||||
console.log(a + b + c) // [!code error]
|
||||
console.log(a + b) // [!code warning]
|
||||
}
|
||||
```
|
||||
|
||||
**code focus:**
|
||||
|
||||
```ts
|
||||
function foo() {
|
||||
const a = 1 // [!code focus]
|
||||
}
|
||||
```
|
||||
|
||||
::: note
|
||||
note content [link](https://github.com/pengzhanbo) `inline code`
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: info
|
||||
content [link](https://github.com/pengzhanbo) `inline code`
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: tip
|
||||
content [link](https://github.com/pengzhanbo) `inline code`
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: warning
|
||||
content [link](https://github.com/pengzhanbo) `inline code`
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: caution
|
||||
content [link](https://github.com/pengzhanbo) `inline code`
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: important
|
||||
content [link](https://github.com/pengzhanbo) `inline code`
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**GFM alert:**
|
||||
|
||||
> [!note]
|
||||
> note
|
||||
|
||||
> [!info]
|
||||
> info
|
||||
|
||||
> [!tip]
|
||||
> tip
|
||||
|
||||
> [!warning]
|
||||
> warning
|
||||
|
||||
> [!caution]
|
||||
> caution
|
||||
|
||||
> [!important]
|
||||
> important
|
||||
|
||||
**code demo:**
|
||||
|
||||
::: normal-demo Demo 演示
|
||||
|
||||
```html
|
||||
<h1>Hello Word!</h1>
|
||||
<p><span id="very">Very</span>Powerful!</p>
|
||||
```
|
||||
|
||||
```js
|
||||
document.querySelector('#very').addEventListener('click', () => {
|
||||
alert('Very Powerful')
|
||||
})
|
||||
```
|
||||
|
||||
```css
|
||||
span {
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**tab card:**
|
||||
|
||||
::: tabs
|
||||
@tab title 1
|
||||
content block
|
||||
|
||||
@tab title 2
|
||||
content block
|
||||
:::
|
||||
|
||||
:::: warning
|
||||
::: tabs
|
||||
@tab title 1
|
||||
content block
|
||||
|
||||
@tab title 2
|
||||
content block
|
||||
:::
|
||||
::::
|
||||
|
||||
**footnote:**
|
||||
|
||||
footnote 1 link[^first]。
|
||||
|
||||
footnote 2 link[^second]。
|
||||
|
||||
inline footnote ^[^first] definition。
|
||||
|
||||
Repeated footnote definition[^second]。
|
||||
|
||||
[^first]: footnote **you can contain special mark**
|
||||
|
||||
also can contain paragraph
|
||||
|
||||
[^second]: footnote content.
|
||||
22
cli/templates/docs/zh/README.md.handlebars
Normal file
22
cli/templates/docs/zh/README.md.handlebars
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
pageLayout: home
|
||||
externalLinkIcon: false
|
||||
config:
|
||||
-
|
||||
type: hero
|
||||
full: true
|
||||
background: tint-plate
|
||||
hero:
|
||||
name: Theme Plume
|
||||
tagline: VuePress Next Theme
|
||||
text: 一个简约的,功能丰富的 vuepress 文档&博客 主题
|
||||
actions:
|
||||
-
|
||||
theme: brand
|
||||
text: 博客
|
||||
link: {{#if (equal defaultLanguage 'zh-CN')}}/{{else}}/zh/{{/if}}blog/
|
||||
-
|
||||
theme: alt
|
||||
text: Github →
|
||||
link: https://github.com/pengzhanbo/vuepress-theme-plume
|
||||
---
|
||||
6
cli/templates/docs/zh/notes/demo/README.md
Normal file
6
cli/templates/docs/zh/notes/demo/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Demo
|
||||
---
|
||||
|
||||
- [bar](./bar.md)
|
||||
- [foo](./foo.md)
|
||||
5
cli/templates/docs/zh/notes/demo/bar.md
Normal file
5
cli/templates/docs/zh/notes/demo/bar.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
title: bar
|
||||
---
|
||||
|
||||
[foo](./foo.md)
|
||||
5
cli/templates/docs/zh/notes/demo/foo.md
Normal file
5
cli/templates/docs/zh/notes/demo/foo.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
title: foo
|
||||
---
|
||||
|
||||
[bar](./bar.md)
|
||||
@ -0,0 +1,8 @@
|
||||
---
|
||||
title: 自定义组件
|
||||
tags:
|
||||
- 预览
|
||||
- 组件
|
||||
---
|
||||
|
||||
<CustomComponent />
|
||||
312
cli/templates/docs/zh/preview/markdown.md
Normal file
312
cli/templates/docs/zh/preview/markdown.md
Normal file
@ -0,0 +1,312 @@
|
||||
---
|
||||
title: Markdown
|
||||
tags:
|
||||
- markdown
|
||||
---
|
||||
|
||||
## 标题 2
|
||||
|
||||
### 标题 3
|
||||
|
||||
#### 标题 4
|
||||
|
||||
##### 标题 5
|
||||
|
||||
###### 标题 6
|
||||
|
||||
加粗:**加粗文字**
|
||||
|
||||
斜体: _斜体文字_
|
||||
|
||||
~~删除文字~~
|
||||
|
||||
内容 ==标记==
|
||||
|
||||
数学表达式: $-(2^{n-1})$ ~ $2^{n-1} -1$
|
||||
|
||||
$\frac {\partial^r} {\partial \omega^r} \left(\frac {y^{\omega}} {\omega}\right)
|
||||
= \left(\frac {y^{\omega}} {\omega}\right) \left\{(\log y)^r + \sum_{i=1}^r \frac {(-1)^ Ir \cdots (r-i+1) (\log y)^{ri}} {\omega^i} \right\}$
|
||||
|
||||
19^th^
|
||||
|
||||
H~2~O
|
||||
|
||||
::: center
|
||||
内容居中
|
||||
:::
|
||||
|
||||
::: right
|
||||
内容右对齐
|
||||
:::
|
||||
|
||||
- 无序列表1
|
||||
- 无序列表2
|
||||
- 无序列表3
|
||||
|
||||
1. 有序列表1
|
||||
2. 有序列表2
|
||||
3. 有序列表3
|
||||
|
||||
- [ ] 任务列表1
|
||||
- [ ] 任务列表2
|
||||
- [x] 任务列表3
|
||||
- [x] 任务列表4
|
||||
|
||||
| Tables | Are | Cool |
|
||||
| ------------- |:-------------:| -----:|
|
||||
| col 3 is | right-aligned | $1600 |
|
||||
| col 2 is | centered | $12 |
|
||||
| zebra stripes | are neat | $1 |
|
||||
|
||||
> 引用内容
|
||||
>
|
||||
> 引用内容
|
||||
|
||||
[链接](/)
|
||||
|
||||
[外部链接](https://github.com/pengzhanbo)
|
||||
|
||||
**Badge:**
|
||||
|
||||
- <Badge type="info" text="info badge" />
|
||||
- <Badge type="tip" text="tip badge" />
|
||||
- <Badge type="warning" text="warning badge" />
|
||||
- <Badge type="danger" text="danger badge" />
|
||||
|
||||
**图标:**
|
||||
|
||||
- home - <Icon name="material-symbols:home" color="currentColor" size="1em" />
|
||||
- vscode - <Icon name="skill-icons:vscode-dark" size="2em" />
|
||||
- twitter - <Icon name="skill-icons:twitter" size="2em" />
|
||||
|
||||
**demo wrapper:**
|
||||
|
||||
::: demo-wrapper title="示例" no-padding height="200px"
|
||||
<style scoped>
|
||||
.open-door {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
.open-door .main {
|
||||
background: #ccc;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="open-door">
|
||||
<div class="main">main</div>
|
||||
<div class="aside">aside</div>
|
||||
</div>
|
||||
|
||||
:::
|
||||
|
||||
**代码:**
|
||||
|
||||
```js whitespace
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
|
||||
// [!code word:obj]
|
||||
const obj = {
|
||||
toLong: {
|
||||
deep: {
|
||||
deep: {
|
||||
deep: {
|
||||
value: 'this is to long text. this is to long text. this is to long text. this is to long text.', // [!code highlight]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**代码分组:**
|
||||
|
||||
::: code-tabs
|
||||
@tab tab1
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
@tab tab2
|
||||
|
||||
```ts
|
||||
const a: number = 1
|
||||
const b: number = 2
|
||||
const c: number = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**代码块高亮:**
|
||||
|
||||
```ts
|
||||
function foo() {
|
||||
const a = 1 // [!code highlight]
|
||||
|
||||
console.log(a)
|
||||
|
||||
const b = 2 // [!code ++]
|
||||
const c = 3 // [!code --]
|
||||
|
||||
console.log(a + b + c) // [!code error]
|
||||
console.log(a + b) // [!code warning]
|
||||
}
|
||||
```
|
||||
|
||||
**代码块聚焦:**
|
||||
|
||||
```ts
|
||||
function foo() {
|
||||
const a = 1 // [!code focus]
|
||||
}
|
||||
```
|
||||
|
||||
::: note 注释
|
||||
注释内容 [link](https://github.com/pengzhanbo) `inline code`
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: info 信息
|
||||
信息内容 [link](https://github.com/pengzhanbo) `inline code`
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: tip 提示
|
||||
提示内容 [link](https://github.com/pengzhanbo) `inline code`
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: warning 警告
|
||||
警告内容 [link](https://github.com/pengzhanbo) `inline code`
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: caution 错误
|
||||
错误内容 [link](https://github.com/pengzhanbo) `inline code`
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: important 重要
|
||||
重要内容 [link](https://github.com/pengzhanbo) `inline code`
|
||||
|
||||
```js
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = a + b
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**GFM alert:**
|
||||
|
||||
> [!note]
|
||||
> note
|
||||
|
||||
> [!info]
|
||||
> info
|
||||
|
||||
> [!tip]
|
||||
> tip
|
||||
|
||||
> [!warning]
|
||||
> warning
|
||||
|
||||
> [!caution]
|
||||
> caution
|
||||
|
||||
> [!important]
|
||||
> important
|
||||
|
||||
**代码演示:**
|
||||
|
||||
::: normal-demo Demo 演示
|
||||
|
||||
```html
|
||||
<h1>Hello Word!</h1>
|
||||
<p><span id="very">非常</span>强大!</p>
|
||||
```
|
||||
|
||||
```js
|
||||
document.querySelector('#very').addEventListener('click', () => {
|
||||
alert('非常强大')
|
||||
})
|
||||
```
|
||||
|
||||
```css
|
||||
span {
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
**选项卡:**
|
||||
|
||||
::: tabs
|
||||
@tab 标题1
|
||||
内容区块
|
||||
|
||||
@tab 标题2
|
||||
内容区块
|
||||
:::
|
||||
|
||||
:::: warning
|
||||
::: tabs
|
||||
@tab 标题1
|
||||
内容区块
|
||||
|
||||
@tab 标题2
|
||||
内容区块
|
||||
:::
|
||||
::::
|
||||
|
||||
**脚注:**
|
||||
|
||||
脚注 1 链接[^first]。
|
||||
|
||||
脚注 2 链接[^second]。
|
||||
|
||||
行内的脚注^[行内脚注文本] 定义。
|
||||
|
||||
重复的页脚定义[^second]。
|
||||
|
||||
[^first]: 脚注 **可以包含特殊标记**
|
||||
|
||||
也可以由多个段落组成
|
||||
|
||||
[^second]: 脚注文字。
|
||||
10
cli/templates/git/.gitattributes.handlebars
Normal file
10
cli/templates/git/.gitattributes.handlebars
Normal file
@ -0,0 +1,10 @@
|
||||
* text eol=lf
|
||||
*.txt text eol=crlf
|
||||
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.ico binary
|
||||
*.tff binary
|
||||
*.woff binary
|
||||
*.woff2 binary
|
||||
8
cli/templates/git/.gitignore.handlebars
Normal file
8
cli/templates/git/.gitignore.handlebars
Normal file
@ -0,0 +1,8 @@
|
||||
node_modules
|
||||
|
||||
{{ docsDir }}/.vuepress/.cache
|
||||
{{ docsDir }}/.vuepress/.temp
|
||||
{{ docsDir }}/.vuepress/dist
|
||||
|
||||
.DS_Store
|
||||
*.log
|
||||
15
cli/tsup.config.ts
Normal file
15
cli/tsup.config.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { defineConfig } from 'tsup'
|
||||
import { version } from './package.json'
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
outDir: 'lib',
|
||||
dts: true,
|
||||
format: 'esm',
|
||||
sourcemap: false,
|
||||
splitting: false,
|
||||
clean: true,
|
||||
define: {
|
||||
__CLI_VERSION__: JSON.stringify(version),
|
||||
},
|
||||
})
|
||||
@ -9,7 +9,7 @@ const packages = fs.readdirSync(path.resolve(__dirname, 'plugins'))
|
||||
export default {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
rules: {
|
||||
'scope-enum': [2, 'always', ['docs', 'theme', ...packages]],
|
||||
'scope-enum': [2, 'always', ['docs', 'theme', 'cli', ...packages]],
|
||||
'footer-max-line-length': [0],
|
||||
},
|
||||
}
|
||||
|
||||
84
pnpm-lock.yaml
generated
84
pnpm-lock.yaml
generated
@ -72,6 +72,27 @@ importers:
|
||||
specifier: ^8.0.0
|
||||
version: 8.0.0
|
||||
|
||||
cli:
|
||||
dependencies:
|
||||
'@clack/prompts':
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0
|
||||
'@pengzhanbo/utils':
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.2
|
||||
cac:
|
||||
specifier: ^6.7.14
|
||||
version: 6.7.14
|
||||
execa:
|
||||
specifier: ^9.3.1
|
||||
version: 9.3.1
|
||||
handlebars:
|
||||
specifier: ^4.7.8
|
||||
version: 4.7.8
|
||||
picocolors:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
|
||||
docs:
|
||||
dependencies:
|
||||
'@iconify/json':
|
||||
@ -463,6 +484,14 @@ packages:
|
||||
'@braintree/sanitize-url@6.0.4':
|
||||
resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==}
|
||||
|
||||
'@clack/core@0.3.4':
|
||||
resolution: {integrity: sha512-H4hxZDXgHtWTwV3RAVenqcC4VbJZNegbBjlPvzOzCouXtS2y3sDvlO3IsbrPNWuLWPPlYVYPghQdSF64683Ldw==}
|
||||
|
||||
'@clack/prompts@0.7.0':
|
||||
resolution: {integrity: sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA==}
|
||||
bundledDependencies:
|
||||
- is-unicode-supported
|
||||
|
||||
'@commitlint/cli@19.4.0':
|
||||
resolution: {integrity: sha512-sJX4J9UioVwZHq7JWM9tjT5bgWYaIN3rC4FP7YwfEwBYiIO+wMyRttRvQLNkow0vCdM0D67r9NEWU0Ui03I4Eg==}
|
||||
engines: {node: '>=v18'}
|
||||
@ -2965,10 +2994,6 @@ packages:
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
escalade@3.1.1:
|
||||
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
escalade@3.1.2:
|
||||
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
|
||||
engines: {node: '>=6'}
|
||||
@ -3204,6 +3229,10 @@ packages:
|
||||
resolution: {integrity: sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==}
|
||||
engines: {node: ^18.19.0 || >=20.5.0}
|
||||
|
||||
execa@9.3.1:
|
||||
resolution: {integrity: sha512-gdhefCCNy/8tpH/2+ajP9IQc14vXchNdd0weyzSJEFURhRMGncQ+zKFxwjAufIewPEJm9BPOaJnvg2UtlH2gPQ==}
|
||||
engines: {node: ^18.19.0 || >=20.5.0}
|
||||
|
||||
expand-tilde@2.0.2:
|
||||
resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -3581,6 +3610,10 @@ packages:
|
||||
resolution: {integrity: sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
|
||||
human-signals@8.0.0:
|
||||
resolution: {integrity: sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
|
||||
husky@9.1.5:
|
||||
resolution: {integrity: sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==}
|
||||
engines: {node: '>=18'}
|
||||
@ -4629,9 +4662,6 @@ packages:
|
||||
resolution: {integrity: sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==}
|
||||
engines: {node: '>= 0.12.0'}
|
||||
|
||||
picocolors@1.0.0:
|
||||
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||
|
||||
picocolors@1.0.1:
|
||||
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
|
||||
|
||||
@ -5868,6 +5898,17 @@ snapshots:
|
||||
|
||||
'@braintree/sanitize-url@6.0.4': {}
|
||||
|
||||
'@clack/core@0.3.4':
|
||||
dependencies:
|
||||
picocolors: 1.0.1
|
||||
sisteransi: 1.0.5
|
||||
|
||||
'@clack/prompts@0.7.0':
|
||||
dependencies:
|
||||
'@clack/core': 0.3.4
|
||||
picocolors: 1.0.1
|
||||
sisteransi: 1.0.5
|
||||
|
||||
'@commitlint/cli@19.4.0(@types/node@20.12.10)(typescript@5.5.4)':
|
||||
dependencies:
|
||||
'@commitlint/format': 19.3.0
|
||||
@ -5999,7 +6040,7 @@ snapshots:
|
||||
'@conventional-changelog/git-client@1.0.0(conventional-commits-parser@6.0.0)':
|
||||
dependencies:
|
||||
'@types/semver': 7.5.8
|
||||
semver: 7.6.0
|
||||
semver: 7.6.3
|
||||
optionalDependencies:
|
||||
conventional-commits-parser: 6.0.0
|
||||
|
||||
@ -8496,8 +8537,6 @@ snapshots:
|
||||
'@esbuild/win32-ia32': 0.23.1
|
||||
'@esbuild/win32-x64': 0.23.1
|
||||
|
||||
escalade@3.1.1: {}
|
||||
|
||||
escalade@3.1.2: {}
|
||||
|
||||
escape-string-regexp@1.0.5: {}
|
||||
@ -8838,6 +8877,21 @@ snapshots:
|
||||
strip-final-newline: 4.0.0
|
||||
yoctocolors: 2.0.0
|
||||
|
||||
execa@9.3.1:
|
||||
dependencies:
|
||||
'@sindresorhus/merge-streams': 4.0.0
|
||||
cross-spawn: 7.0.3
|
||||
figures: 6.1.0
|
||||
get-stream: 9.0.1
|
||||
human-signals: 8.0.0
|
||||
is-plain-obj: 4.1.0
|
||||
is-stream: 4.0.1
|
||||
npm-run-path: 5.3.0
|
||||
pretty-ms: 9.0.0
|
||||
signal-exit: 4.1.0
|
||||
strip-final-newline: 4.0.0
|
||||
yoctocolors: 2.0.0
|
||||
|
||||
expand-tilde@2.0.2:
|
||||
dependencies:
|
||||
homedir-polyfill: 1.0.3
|
||||
@ -9266,6 +9320,8 @@ snapshots:
|
||||
|
||||
human-signals@7.0.0: {}
|
||||
|
||||
human-signals@8.0.0: {}
|
||||
|
||||
husky@9.1.5: {}
|
||||
|
||||
iconv-lite@0.4.24:
|
||||
@ -10481,8 +10537,6 @@ snapshots:
|
||||
|
||||
photoswipe@5.4.4: {}
|
||||
|
||||
picocolors@1.0.0: {}
|
||||
|
||||
picocolors@1.0.1: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
@ -10570,7 +10624,7 @@ snapshots:
|
||||
postcss@8.4.38:
|
||||
dependencies:
|
||||
nanoid: 3.3.7
|
||||
picocolors: 1.0.0
|
||||
picocolors: 1.0.1
|
||||
source-map-js: 1.2.0
|
||||
|
||||
postcss@8.4.40:
|
||||
@ -11330,8 +11384,8 @@ snapshots:
|
||||
update-browserslist-db@1.0.13(browserslist@4.23.0):
|
||||
dependencies:
|
||||
browserslist: 4.23.0
|
||||
escalade: 3.1.1
|
||||
picocolors: 1.0.0
|
||||
escalade: 3.1.2
|
||||
picocolors: 1.0.1
|
||||
|
||||
uri-js@4.4.1:
|
||||
dependencies:
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
packages:
|
||||
- docs
|
||||
- theme
|
||||
- cli
|
||||
- plugins/*
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { Page, Theme } from 'vuepress/core'
|
||||
import { sleep } from '@pengzhanbo/utils'
|
||||
import type { PlumeThemeOptions, PlumeThemePageData } from '../shared/index.js'
|
||||
import { getPlugins } from './plugins/index.js'
|
||||
import { extendsPageData, setupPage } from './setupPages.js'
|
||||
@ -47,9 +48,10 @@ export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
|
||||
},
|
||||
})
|
||||
|
||||
waitForConfigLoaded().then(({ autoFrontmatter }) => {
|
||||
waitForConfigLoaded().then(async ({ autoFrontmatter }) => {
|
||||
autoFrontmatter ??= pluginOptions.frontmatter
|
||||
if (autoFrontmatter !== false) {
|
||||
await sleep(100)
|
||||
generateAutoFrontmatter(app)
|
||||
}
|
||||
})
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
"plugins/**/*",
|
||||
"theme/**/*",
|
||||
"docs/.vuepress/**/*",
|
||||
"scripts/**/*"
|
||||
"cli/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user