mirror of https://github.com/vuejs/vitepress
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
5.2 KiB
206 lines
5.2 KiB
import {
|
|
intro,
|
|
outro,
|
|
group,
|
|
text,
|
|
select,
|
|
cancel,
|
|
confirm
|
|
} from '@clack/prompts'
|
|
import fs from 'fs-extra'
|
|
import path from 'path'
|
|
import { black, cyan, bgCyan, bold, yellow } from 'picocolors'
|
|
import { fileURLToPath } from 'url'
|
|
import template from 'lodash.template'
|
|
|
|
export enum ScaffoldThemeType {
|
|
Default = 'default theme',
|
|
DefaultCustom = 'default theme + customization',
|
|
Custom = 'custom theme'
|
|
}
|
|
|
|
export interface ScaffoldOptions {
|
|
root: string
|
|
title?: string
|
|
description?: string
|
|
theme: ScaffoldThemeType
|
|
useTs: boolean
|
|
injectNpmScripts: boolean
|
|
}
|
|
|
|
export async function init() {
|
|
intro(bgCyan(bold(black(' Welcome to VitePress! '))))
|
|
|
|
const options: ScaffoldOptions = await group(
|
|
{
|
|
root: () =>
|
|
text({
|
|
message: 'Where should VitePress initialize the config?',
|
|
initialValue: './',
|
|
validate(value) {
|
|
// TODO make sure directory is inside
|
|
}
|
|
}),
|
|
|
|
title: () =>
|
|
text({
|
|
message: 'Site title:',
|
|
placeholder: 'My Awesome Project'
|
|
}),
|
|
|
|
description: () =>
|
|
text({
|
|
message: 'Site description:',
|
|
placeholder: 'A VitePress Site'
|
|
}),
|
|
|
|
theme: () =>
|
|
select({
|
|
message: 'Theme:',
|
|
options: [
|
|
{
|
|
value: ScaffoldThemeType.Default,
|
|
label: 'Default Theme',
|
|
hint: 'Out of the box, good-looking docs'
|
|
},
|
|
{
|
|
value: ScaffoldThemeType.DefaultCustom,
|
|
label: 'Default Theme + Customization',
|
|
hint: 'Add custom CSS and layout slots'
|
|
},
|
|
{
|
|
value: ScaffoldThemeType.Custom,
|
|
label: 'Custom Theme',
|
|
hint: 'Build your own or use external'
|
|
}
|
|
]
|
|
}),
|
|
|
|
useTs: () =>
|
|
confirm({ message: 'Use TypeScript for config and theme files?' }),
|
|
|
|
injectNpmScripts: () =>
|
|
confirm({
|
|
message: 'Add VitePress npm scripts to package.json?'
|
|
})
|
|
},
|
|
{
|
|
onCancel: () => {
|
|
cancel('Cancelled.')
|
|
process.exit(0)
|
|
}
|
|
}
|
|
)
|
|
|
|
outro(scaffold(options))
|
|
}
|
|
|
|
export function scaffold({
|
|
root = './',
|
|
title = 'My Awesome Project',
|
|
description = 'A VitePress Site',
|
|
theme,
|
|
useTs,
|
|
injectNpmScripts
|
|
}: ScaffoldOptions): string {
|
|
const resolvedRoot = path.resolve(root)
|
|
const templateDir = path.resolve(
|
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
'../../template'
|
|
)
|
|
|
|
const data = {
|
|
title: JSON.stringify(title),
|
|
description: JSON.stringify(description),
|
|
useTs,
|
|
defaultTheme:
|
|
theme === ScaffoldThemeType.Default ||
|
|
theme === ScaffoldThemeType.DefaultCustom
|
|
}
|
|
|
|
const pkgPath = path.resolve('package.json')
|
|
const userPkg = fs.existsSync(pkgPath)
|
|
? JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
|
|
: {}
|
|
|
|
const useMjs = userPkg.type !== 'module'
|
|
|
|
const renderFile = (file: string) => {
|
|
const filePath = path.resolve(templateDir, file)
|
|
let targetPath = path.resolve(resolvedRoot, file)
|
|
if (useMjs && targetPath.includes('.vitepress/config')) {
|
|
targetPath = targetPath.replace(/\.js$/, '.mjs')
|
|
}
|
|
if (useTs) {
|
|
targetPath = targetPath.replace(/\.(m?)js$/, '.$1ts')
|
|
}
|
|
const src = fs.readFileSync(filePath, 'utf-8')
|
|
const compiled = template(src)(data)
|
|
fs.outputFileSync(targetPath, compiled)
|
|
}
|
|
|
|
const filesToScaffold = [
|
|
'index.md',
|
|
'api-examples.md',
|
|
'markdown-examples.md',
|
|
'.vitepress/config.js'
|
|
]
|
|
|
|
if (theme === ScaffoldThemeType.DefaultCustom) {
|
|
filesToScaffold.push(
|
|
'.vitepress/theme/index.js',
|
|
'.vitepress/theme/style.css'
|
|
)
|
|
} else if (theme === ScaffoldThemeType.Custom) {
|
|
filesToScaffold.push(
|
|
'.vitepress/theme/index.js',
|
|
'.vitepress/theme/style.css',
|
|
'.vitepress/theme/Layout.vue'
|
|
)
|
|
}
|
|
|
|
for (const file of filesToScaffold) {
|
|
renderFile(file)
|
|
}
|
|
|
|
const dir =
|
|
root === './' ? '' : ` ${root.replace(/^\.\//, '').replace(/[/\\]$/, '')}`
|
|
const gitignorePrefix = dir ? `${dir}/.vitepress` : '.vitepress'
|
|
|
|
const tips = []
|
|
if (fs.existsSync('.git')) {
|
|
tips.push(
|
|
`Make sure to add ${cyan(`${gitignorePrefix}/dist`)} and ` +
|
|
`${cyan(`${gitignorePrefix}/cache`)} to your ` +
|
|
`${cyan(`.gitignore`)} file.`
|
|
)
|
|
}
|
|
if (
|
|
theme !== ScaffoldThemeType.Default &&
|
|
!userPkg.dependencies?.['vue'] &&
|
|
!userPkg.devDependencies?.['vue']
|
|
) {
|
|
tips.push(
|
|
`Since you've chosen to customize the theme, ` +
|
|
`you should also explicitly install ${cyan(`vue`)} as a dev dependency.`
|
|
)
|
|
}
|
|
|
|
const tip = tips.length ? yellow([`\n\nTips:`, ...tips].join('\n- ')) : ``
|
|
|
|
if (injectNpmScripts) {
|
|
const scripts = {
|
|
'docs:dev': `vitepress dev${dir}`,
|
|
'docs:build': `vitepress build${dir}`,
|
|
'docs:preview': `vitepress preview${dir}`
|
|
}
|
|
Object.assign(userPkg.scripts || (userPkg.scripts = {}), scripts)
|
|
fs.writeFileSync(pkgPath, JSON.stringify(userPkg, null, 2))
|
|
return `Done! Now run ${cyan(`npm run docs:dev`)} and start writing.${tip}`
|
|
} else {
|
|
return `You're all set! Now run ${cyan(
|
|
`npx vitepress dev${dir}`
|
|
)} and start writing.${tip}`
|
|
}
|
|
}
|