dev.js 4.1 KB
Newer Older
E
cli  
Evan You 已提交
1
module.exports = async function dev (sourceDir, cliOptions = {}) {
E
Evan You 已提交
2
  const fs = require('fs')
E
wip  
Evan You 已提交
3
  const path = require('path')
E
Evan You 已提交
4 5
  const chalk = require('chalk')
  const webpack = require('webpack')
E
Evan You 已提交
6
  const chokidar = require('chokidar')
E
Evan You 已提交
7 8
  const serve = require('webpack-serve')
  const convert = require('koa-connect')
E
Evan You 已提交
9
  const mount = require('koa-mount')
E
Evan You 已提交
10
  const serveStatic = require('koa-static')
E
Evan You 已提交
11 12
  const history = require('connect-history-api-fallback')

E
tweaks  
Evan You 已提交
13
  const prepare = require('./prepare')
U
ULIVZ 已提交
14
  const logger = require('./util/logger')
E
Evan You 已提交
15
  const HeadPlugin = require('./webpack/HeadPlugin')
U
ULIVZ 已提交
16
  const DevLogPlugin = require('./webpack/DevLogPlugin')
E
Evan You 已提交
17 18
  const createClientConfig = require('./webpack/createClientConfig')
  const { applyUserWebpackConfig } = require('./util')
19
  const { frontmatterEmitter } = require('./webpack/markdownLoader')
E
Evan You 已提交
20

U
ULIVZ 已提交
21
  logger.wait('\nExtracting site metadata...')
E
Evan You 已提交
22 23
  const options = await prepare(sourceDir)

E
tweaks  
Evan You 已提交
24
  // setup watchers to update options and dynamically generated files
E
Evan You 已提交
25 26
  const update = () => {
    prepare(sourceDir).catch(err => {
U
ULIVZ 已提交
27
      console.error(logger.error(chalk.red(err.stack), false))
E
Evan You 已提交
28 29
    })
  }
30 31

  // watch add/remove of files
E
tweaks  
Evan You 已提交
32
  const pagesWatcher = chokidar.watch([
33 34
    '**/*.md',
    '.vuepress/components/**/*.vue'
E
tweaks  
Evan You 已提交
35
  ], {
36
    cwd: sourceDir,
E
Evan You 已提交
37
    ignored: '.vuepress/**/*.md',
E
tweaks  
Evan You 已提交
38 39
    ignoreInitial: true
  })
E
Evan You 已提交
40 41 42 43
  pagesWatcher.on('add', update)
  pagesWatcher.on('unlink', update)
  pagesWatcher.on('addDir', update)
  pagesWatcher.on('unlinkDir', update)
E
tweaks  
Evan You 已提交
44

45 46
  // watch config file
  const configWatcher = chokidar.watch([
47 48 49 50 51 52 53
    '.vuepress/config.js',
    '.vuepress/config.yml',
    '.vuepress/config.toml'
  ], {
    cwd: sourceDir,
    ignoreInitial: true
  })
54 55 56 57 58
  configWatcher.on('change', update)

  // also listen for frontmatter changes from markdown files
  frontmatterEmitter.on('update', update)

E
tweaks  
Evan You 已提交
59
  // resolve webpack config
E
Evan You 已提交
60
  let config = createClientConfig(options, cliOptions)
E
Evan You 已提交
61

E
Evan You 已提交
62
  config
E
Evan You 已提交
63
    .plugin('html')
64 65 66
    // using a fork of html-webpack-plugin to avoid it requiring webpack
    // internals from an incompatible version.
    .use(require('vuepress-html-webpack-plugin'), [{
E
Evan You 已提交
67 68
      template: path.resolve(__dirname, 'app/index.dev.html')
    }])
E
Evan You 已提交
69

E
Evan You 已提交
70
  config
E
Evan You 已提交
71
    .plugin('site-data')
E
Evan You 已提交
72 73 74
    .use(HeadPlugin, [{
      tags: options.siteConfig.head || []
    }])
E
Evan You 已提交
75

U
ULIVZ 已提交
76 77 78 79 80 81 82 83 84 85 86
  const port = await resolvePort(cliOptions.port || options.siteConfig.port)
  const { host, displayHost } = await resolveHost(cliOptions.host || options.siteConfig.host)

  config
  .plugin('vuepress-log')
  .use(DevLogPlugin, [{
    port,
    displayHost,
    publicPath: options.publicPath
  }])

E
Evan You 已提交
87 88 89 90 91 92
  config = config.toConfig()
  const userConfig = options.siteConfig.configureWebpack
  if (userConfig) {
    config = applyUserWebpackConfig(userConfig, config, false /* isServer */)
  }

E
Evan You 已提交
93 94
  const compiler = webpack(config)

95
  const nonExistentDir = path.resolve(__dirname, 'non-existent')
E
Evan You 已提交
96
  await serve({
97 98 99
    // avoid project cwd from being served. Otherwise if the user has index.html
    // in cwd it would break the server
    content: [nonExistentDir],
E
Evan You 已提交
100
    compiler,
101
    host,
102
    dev: { logLevel: 'warn' },
103 104 105 106
    hot: {
      port: port + 1,
      logLevel: 'error'
    },
E
Evan You 已提交
107 108
    logLevel: 'error',
    port,
E
Evan You 已提交
109 110
    add: app => {
      const userPublic = path.resolve(sourceDir, '.vuepress/public')
E
Evan You 已提交
111
      // respect base when serving static files...
E
Evan You 已提交
112
      if (fs.existsSync(userPublic)) {
E
Evan You 已提交
113
        app.use(mount(options.publicPath, serveStatic(userPublic)))
E
Evan You 已提交
114 115 116 117 118 119 120 121
      }

      app.use(convert(history({
        rewrites: [
          { from: /\.html$/, to: '/' }
        ]
      })))
    }
E
Evan You 已提交
122 123
  })
}
U
ULIVZ 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144

function resolveHost (host) {
  // webpack-serve hot updates doesn't work properly over 0.0.0.0 on Windows,
  // but localhost does not allow visiting over network :/
  const defaultHost = process.platform === 'win32' ? 'localhost' : '0.0.0.0'
  host = host || defaultHost
  const displayHost = host === defaultHost && process.platform !== 'win32'
    ? 'localhost'
    : host
  return {
    displayHost,
    host
  }
}

async function resolvePort (port) {
  const portfinder = require('portfinder')
  portfinder.basePort = port || 8080
  port = await portfinder.getPortPromise()
  return port
}