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

E
Evan You 已提交
9 10
mkdirp(tempPath)

E
Evan You 已提交
11 12 13 14 15
module.exports = async function prepare (sourceDir) {
  // 1. load options
  const options = await resolveOptions(sourceDir)

  // 2. generate dynamic component registration file
E
tweaks  
Evan You 已提交
16
  const componentCode = await genComponentRegistrationFile(options)
E
Evan You 已提交
17 18

  // 3. generate routes
E
tweaks  
Evan You 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31
  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 已提交
32

33 34 35 36 37 38 39 40 41
  // 5. generate basic polyfill if need to support older browsers
  let polyfillCode = ``
  if (!options.siteConfig.evergreen) {
    polyfillCode =
`import 'es6-promise/auto'
if (!Object.assign) Object.assign = require('object-assign')`
  }
  fs.writeFileSync(path.join(tempPath, 'polyfill.js'), polyfillCode)

E
Evan You 已提交
42 43 44 45
  return options
}

async function resolveOptions (sourceDir) {
E
Evan You 已提交
46 47
  const vuepressDir = path.resolve(sourceDir, '.vuepress')
  const configPath = path.resolve(vuepressDir, 'config.js')
48 49

  delete require.cache[configPath]
E
Evan You 已提交
50
  const siteConfig = fs.existsSync(configPath) ? require(configPath) : {}
E
Evan You 已提交
51

E
Evan You 已提交
52 53
  // normalize description
  if (siteConfig.description) {
E
Evan You 已提交
54 55 56
    (siteConfig.head || (siteConfig.head = [])).unshift([
      'meta', { name: 'description', content: siteConfig.description }
    ])
E
Evan You 已提交
57 58
  }

E
Evan You 已提交
59
  const options = {
E
Evan You 已提交
60
    siteConfig,
E
Evan You 已提交
61
    sourceDir,
E
Evan You 已提交
62 63 64
    outDir: siteConfig.dest
      ? path.resolve(siteConfig.dest)
      : path.resolve(sourceDir, '.vuepress/dist'),
E
Evan You 已提交
65
    publicPath: siteConfig.base || '/',
E
Evan You 已提交
66 67 68 69
    pageFiles: await globby(['**/*.md', '!.vuepress'], { cwd: sourceDir }),
    pagesData: null,
    themePath: null,
    notFoundPath: null
E
Evan You 已提交
70 71
  }

E
Evan You 已提交
72 73 74 75 76 77
  // resolve theme
  const hasTheme = (
    siteConfig.theme ||
    fs.existsSync(path.resolve(vuepressDir, 'theme'))
  )

E
Evan You 已提交
78 79 80 81 82
  if (!hasTheme) {
    // use default theme
    options.themePath = path.resolve(__dirname, 'default-theme/Layout.vue')
    options.notFoundPath = path.resolve(__dirname, 'default-theme/NotFound.vue')
  } else {
E
Evan You 已提交
83 84
    // resolve custom theme
    const themeDir = siteConfig.theme
E
Evan You 已提交
85
      ? path.resolve(__dirname, `../../vuepress-theme-${siteConfig.theme}`)
E
Evan You 已提交
86
      : path.resolve(sourceDir, 'theme')
E
Evan You 已提交
87

E
Evan You 已提交
88 89 90 91
    const themePath = path.resolve(themeDir, 'Layout.vue')
    if (fs.existsSync(themePath)) {
      options.themePath = themePath
    } else {
E
Evan You 已提交
92 93 94
      throw new Error(`[vuepress] Cannot resolve Layout.vue file for custom theme${
        siteConfig.theme ? ` "${siteConfig.theme}"` : ``
      }.`)
E
Evan You 已提交
95 96 97 98 99 100
    }

    const notFoundPath = path.resolve(themeDir, '/NotFound.vue')
    if (fs.existsSync(notFoundPath)) {
      options.notFoundPath = notFoundPath
    } else {
E
Evan You 已提交
101
      options.notFoundPath = path.resolve(__dirname, 'default-theme/NotFound.vue')
E
Evan You 已提交
102
    }
E
tweaks  
Evan You 已提交
103 104
  }

E
Evan You 已提交
105 106
  // resolve pages
  const pagesData = options.pageFiles.map(file => {
E
Evan You 已提交
107 108 109
    const urlPath = isIndexFile(file)
      ? '/'
      : `/${file.replace(/\.md$/, '').replace(/\\/g, '/')}.html`
E
Evan You 已提交
110
    const content = fs.readFileSync(path.resolve(sourceDir, file), 'utf-8')
E
tweaks  
Evan You 已提交
111
    const data = {
E
Evan You 已提交
112 113
      path: urlPath
    }
E
Evan You 已提交
114 115

    // extract yaml frontmatter
E
Evan You 已提交
116
    const frontmatter = yaml.loadFront(content)
E
Evan You 已提交
117
    // infer title
E
Evan You 已提交
118 119 120
    const title = inferTitle(frontmatter)
    if (title) {
      data.title = title
E
Evan You 已提交
121
    }
E
Evan You 已提交
122
    delete frontmatter.__content
E
Evan You 已提交
123 124
    if (Object.keys(frontmatter).length) {
      data.frontmatter = frontmatter
E
Evan You 已提交
125
    }
E
tweaks  
Evan You 已提交
126
    return data
E
Evan You 已提交
127 128
  })

E
Evan You 已提交
129
  // resolve site data
E
Evan You 已提交
130
  options.siteData = {
E
Evan You 已提交
131 132
    title: siteConfig.title || '',
    description: siteConfig.description || '',
E
Evan You 已提交
133
    base: siteConfig.base || '/',
E
Evan You 已提交
134
    pages: pagesData,
E
Evan You 已提交
135
    themeConfig: siteConfig.themeConfig || {}
E
Evan You 已提交
136
  }
E
Evan You 已提交
137 138 139 140

  return options
}

E
Evan You 已提交
141
async function genComponentRegistrationFile ({ sourceDir, pageFiles }) {
E
Evan You 已提交
142 143 144 145
  function genImport (file) {
    const name = toComponentName(file)
    const baseDir = /\.md$/.test(file)
      ? sourceDir
E
Evan You 已提交
146
      : path.resolve(sourceDir, '.vuepress/components')
E
Evan You 已提交
147 148 149 150 151 152
    const absolutePath = path.resolve(baseDir, file)
    const code = `Vue.component(${JSON.stringify(name)}, () => import(${JSON.stringify(absolutePath)}))`
    return code
  }

  const components = (await resolveComponents(sourceDir)) || []
E
Evan You 已提交
153
  const all = [...pageFiles, ...components]
E
tweaks  
Evan You 已提交
154
  return `import Vue from 'vue'\n` + all.map(genImport).join('\n')
E
Evan You 已提交
155 156
}

E
Evan You 已提交
157 158
function toComponentName (file) {
  const isPage = /\.md$/.test(file)
E
Evan You 已提交
159 160 161 162 163 164 165 166 167
  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 已提交
168 169
}

E
Evan You 已提交
170
async function resolveComponents (sourceDir) {
E
Evan You 已提交
171
  const componentDir = path.resolve(sourceDir, '.vuepress/components')
E
Evan You 已提交
172 173 174
  if (!fs.existsSync(componentDir)) {
    return
  }
E
tweaks  
Evan You 已提交
175
  return await globby(['**/*.vue'], { cwd: componentDir })
E
Evan You 已提交
176 177
}

E
Evan You 已提交
178
async function genRoutesFile ({ siteData: { pages }}) {
E
tweaks  
Evan You 已提交
179
  function genRoute ({ path }) {
E
Evan You 已提交
180 181
    const code = `
    {
E
Evan You 已提交
182
      path: ${JSON.stringify(path)},
E
Evan You 已提交
183 184 185 186 187
      component: Theme
    }`
    return code
  }

E
tweaks  
Evan You 已提交
188
  return (
E
Evan You 已提交
189
    `import Theme from '~theme'\n` +
E
tweaks  
Evan You 已提交
190 191
    `export const routes = [${pages.map(genRoute).join(',')}\n]`
  )
E
Evan You 已提交
192
}