prepare.js 4.5 KB
Newer Older
E
Evan You 已提交
1 2 3 4 5 6 7 8 9 10 11
const fs = require('fs')
const path = require('path')
const globby = require('globby')
const yaml = require('yaml-front-matter')
const tempPath = path.resolve(__dirname, 'app/.temp')

module.exports = async function prepare (sourceDir) {
  // 1. load options
  const options = await resolveOptions(sourceDir)

  // 2. generate dynamic component registration file
E
tweaks  
Evan You 已提交
12
  const componentCode = await genComponentRegistrationFile(options)
E
Evan You 已提交
13 14

  // 3. generate routes
E
tweaks  
Evan You 已提交
15 16 17 18 19 20 21 22 23 24 25 26 27
  const routesCode = await genRoutesFile(options)

  // 4. generate siteData
  const dataCode = `export const siteData = ${JSON.stringify(options.siteData, null, 2)}`

  fs.writeFileSync(
    path.join(tempPath, 'siteData.js'),
    [
      componentCode,
      routesCode,
      dataCode
    ].join('\n\n')
  )
E
Evan You 已提交
28 29 30 31 32 33

  return options
}

async function resolveOptions (sourceDir) {
  const configPath = path.resolve(sourceDir, 'vuepress.config.js')
E
Evan You 已提交
34 35 36
  const userConfig = fs.existsSync(configPath) ? require(configPath) : {}

  const hasTheme = userConfig.theme || fs.existsSync(path.resolve(sourceDir, '_theme'))
E
Evan You 已提交
37 38 39

  const options = {
    sourceDir,
E
Evan You 已提交
40
    publicPath: userConfig.baseUrl || '/',
E
Evan You 已提交
41 42 43
    pages: await globby(['**/*.md'], { cwd: sourceDir })
  }

E
Evan You 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
  if (!hasTheme) {
    // use default theme
    options.siteConfig = userConfig
    options.themePath = path.resolve(__dirname, 'default-theme/Layout.vue')
    options.notFoundPath = path.resolve(__dirname, 'default-theme/NotFound.vue')
  } else {
    // resolve theme
    const themeDir = userConfig.theme
      ? path.resolve(__dirname, `../../${userConfig.theme}`)
      : path.resolve(sourceDir, '_theme')
    const themeConfigPath = path.resolve(themeDir, 'config.js')
    const themeConfig = fs.existsSync(themeConfigPath)
      ? require(themeConfigPath)
      : {}
    options.siteConfig = Object.assign(themeConfig, userConfig)
E
Evan You 已提交
59

E
Evan You 已提交
60 61 62 63 64 65 66 67 68 69 70 71 72
    const themePath = path.resolve(themeDir, 'Layout.vue')
    if (fs.existsSync(themePath)) {
      options.themePath = themePath
    } else {
      throw new Error('[vuepress] Custom theme must have a Layout.vue file.')
    }

    const notFoundPath = path.resolve(themeDir, '/NotFound.vue')
    if (fs.existsSync(notFoundPath)) {
      options.notFoundPath = notFoundPath
    } else {
      throw new Error('[vuepress] Custom theme must have a NotFound.vue file.')
    }
E
tweaks  
Evan You 已提交
73 74 75
  }

  const pagesData = options.pages.map(file => {
E
Evan You 已提交
76 77 78
    const urlPath = isIndexFile(file)
      ? '/'
      : `/${file.replace(/\.md$/, '').replace(/\\/g, '/')}.html`
E
Evan You 已提交
79
    const content = fs.readFileSync(path.resolve(sourceDir, file), 'utf-8')
E
tweaks  
Evan You 已提交
80
    const data = {
E
Evan You 已提交
81 82
      path: urlPath
    }
E
Evan You 已提交
83 84

    // extract yaml frontmatter
E
Evan You 已提交
85
    const frontmatter = yaml.loadFront(content)
E
Evan You 已提交
86 87 88 89 90
    // infer title
    const titleMatch = frontmatter.__content.match(/^#+\s+(.*)/)
    if (titleMatch) {
      data.title = titleMatch[1]
    }
E
Evan You 已提交
91
    delete frontmatter.__content
E
Evan You 已提交
92 93
    if (Object.keys(frontmatter).length) {
      data.frontmatter = frontmatter
E
Evan You 已提交
94
    }
E
tweaks  
Evan You 已提交
95
    return data
E
Evan You 已提交
96 97
  })

E
Evan You 已提交
98
  options.siteData = Object.assign({}, userConfig.data, {
E
Evan You 已提交
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    pages: pagesData
  })

  return options
}

async function genComponentRegistrationFile ({ sourceDir, pages }) {
  function genImport (file) {
    const name = toComponentName(file)
    const baseDir = /\.md$/.test(file)
      ? sourceDir
      : path.resolve(sourceDir, '_components')
    const absolutePath = path.resolve(baseDir, file)
    const code = `Vue.component(${JSON.stringify(name)}, () => import(${JSON.stringify(absolutePath)}))`
    return code
  }

  const components = (await resolveComponents(sourceDir)) || []
  const all = [...pages, ...components]
E
tweaks  
Evan You 已提交
118
  return `import Vue from 'vue'\n` + all.map(genImport).join('\n')
E
Evan You 已提交
119 120
}

E
Evan You 已提交
121 122
function toComponentName (file) {
  const isPage = /\.md$/.test(file)
E
Evan You 已提交
123 124 125 126 127 128 129 130 131
  const isIndex = isIndexFile(file)
  const normalizedName = isIndex
    ? 'index'
    : file.replace(/\.(vue|md)$/, '').replace(/\/|\\/g, '-')
  return isPage ? `page-${normalizedName}` : normalizedName
}

function isIndexFile (file) {
  return /^(index|readme)\.md$/i.test(file)
E
Evan You 已提交
132 133
}

E
Evan You 已提交
134 135 136 137 138
async function resolveComponents (sourceDir) {
  const componentDir = path.resolve(sourceDir, '_components')
  if (!fs.existsSync(componentDir)) {
    return
  }
E
tweaks  
Evan You 已提交
139
  return await globby(['**/*.vue'], { cwd: componentDir })
E
Evan You 已提交
140 141
}

E
Evan You 已提交
142
async function genRoutesFile ({ sourceDir, siteData: { pages }}) {
E
tweaks  
Evan You 已提交
143
  function genRoute ({ path }) {
E
Evan You 已提交
144 145
    const code = `
    {
E
Evan You 已提交
146
      path: ${JSON.stringify(path)},
E
Evan You 已提交
147 148 149 150 151
      component: Theme
    }`
    return code
  }

E
tweaks  
Evan You 已提交
152
  return (
E
Evan You 已提交
153
    `import Theme from '~theme'\n` +
E
tweaks  
Evan You 已提交
154 155
    `export const routes = [${pages.map(genRoute).join(',')}\n]`
  )
E
Evan You 已提交
156
}