text: 'use strict'
  
  /**
   * Module dependencies.
   */
  
  const { inferDate, DATE_RE } = require('./util/index')
  const {
    fs,
    path,
    hash,
    chalk,
    logger,
    slugify,
    inferTitle,
    fileToPath,
    getPermalink,
    extractHeaders,
    parseFrontmatter,
    parseVueFrontmatter: { parse: parseVueFrontmatter }
  } = require('@vuepress/shared-utils')
  
  /**
   * Expose Page class.
   */
  
  module.exports = class Page {
    /**
     * @param {string} path the URL (excluding the domain name) for your page/post.
     * @param {string} title markdown title
     * @param {string} content markdown file content
     * @param {string} filePath absolute file path of source markdown file.
     * @param {string} relative relative file path of source markdown file.
     * @param {string} permalink same to path, the URL (excluding the domain name) for your page/post.
     * @param {object} frontmatter
     * @param {string} permalinkPattern
     */
  
    constructor ({
      path: _path,
      meta,
      title,
      content,
      filePath,
      relative,
      permalink,
      frontmatter = {},
      permalinkPattern
    }, context) {
      this.title = title
      this._meta = meta
      this._filePath = filePath
      this._content = content
      this._permalink = permalink
      this.frontmatter = frontmatter
      this._permalinkPattern = permalinkPattern
      this._context = context
  
      if (relative) {
        this.regularPath = fileToPath(relative)
      } else if (_path) {
        this.regularPath = _path
      } else if (permalink) {
        this.regularPath = permalink
      }
  
      if (filePath) {
        this.relativePath = path.relative(context.sourceDir, filePath).replace(/\\/g, '/')
      }
  
      this.key = 'v-' + hash(`${this._filePath}${this.regularPath}`)
      // Using regularPath first, would be override by permalink later.
      this.path = this.regularPath
    }
  
    /**
     * Process a page.
     *
     *   1. If it's a page pointing to a md file, this method will try
     *      to resolve the page's title / headers from the content.
     *   2. If it's a pure route. this method will only enhance it.
     *
     * @api public
     */
  
    async process ({
      computed,
      markdown,
      enhancers = [],
      preRender = {}
    }) {
      if (this._filePath) {
        logger.developer(`static_route`, chalk.cyan(this.path))
        this._content = await fs.readFile(this._filePath, 'utf-8')
      } else if (this._content) {
        logger.developer(`static_route`, chalk.cyan(this.path))
        this._filePath = await this._context.writeTemp(`temp-pages/${this.key}.md`, this._content)
      } else {
        logger.developer(`dynamic_route`, chalk.cyan(this.path))
      }
  
      if (this._content) {
        if (this._filePath.endsWith('.md')) {
          const { excerpt, data, content } = parseFrontmatter(this._content)
          this._strippedContent = content
          Object.assign(this.frontmatter, data)
  
          // infer title
          const title = inferTitle(this.frontmatter, this._strippedContent)
          if (title) {
            this.title = title
          }
  
          // headers
          const headers = extractHeaders(
            this._strippedContent,
            ['h2', 'h3'],
            markdown
          )
          if (headers.length) {
            this.headers = headers
          }
  
          if (excerpt) {
            const { html } = markdown.render(excerpt, {
              frontmatter: this.frontmatter,
              relativePath: this.relativePath
            })
            this.excerpt = html
          }
        } else if (this._filePath.endsWith('.vue')) {
          const { data = {}} = parseVueFrontmatter(this._content)
          // When Vue SFCs are source files, make them as layout components directly.
          this.frontmatter = Object.assign({
            layout: this.key
          }, data)
        }
      }
  
      // resolve i18n
      computed.setPage(this)
      this._computed = computed
      this._localePath = computed.$localePath
  
      this.enhance(enhancers)
      this.buildPermalink()
    }
  
    /**
     * name of page's parent directory.
     *
     * @returns {string}
     * @api public
     */
  
    get dirname () {
      return path.basename(path.dirname(this._filePath || this.regularPath))
    }
  
    /**
     * file name of page's source markdown file, or the last cut of regularPath.
     *
     * @returns {string}
     * @api public
     */
  
    get filename () {
      return path.parse(this._filePath || this.regularPath).name
    }
  
    /**
     * slugified file name.
     *
     * @returns {string}
     * @api public
     */
  
    get slug () {
      const strippedFilename = this.strippedFilename
  
      if (/^(index|readme)$/i.test(strippedFilename)) {
        const strippedFilename = this.stripFilename(
          path.basename(path.dirname(this._filePath || this.regularPath))
        )
  
        if (strippedFilename) {
          return slugify(strippedFilename)
        }
      }
  
      return slugify(strippedFilename)
    }
  
    /**
     * stripped file name.
     *
     * If filename was yyyy-MM-dd-[title], the date prefix will be stripped.
     * If filename was yyyy-MM-[title], the date prefix will be stripped.
     *
     * @returns {string}
     * @api public
     */
  
    get strippedFilename () {
      return this.stripFilename(this.filename)
    }
  
    /**
     * get date of a page.
     *
     * @returns {null|string}
     * @api public
     */
  
    get date () {
      return inferDate(this.frontmatter, this.filename, this.dirname)
    }
  
    /**
     * stripped file name.
     *
     * If filename was yyyy-MM-dd-[title], the date prefix will be stripped.
     * If filename was yyyy-MM-[title], the date prefix will be stripped.
     *
     * @param {string} fileName
     * @returns {string}
     * @private
     */
    stripFilename (fileName) {
      const match = fileName.match(DATE_RE)
  
      return match ? match[3] : fileName
    }
  
    /**
     * Convert page's metadata to JSON, note that all fields beginning
     * with an underscore will not be serialized.
     *
     * @returns {object}
     * @api public
     */
  
    toJson () {
      const json = {}
      Object.keys(this).reduce((json, key) => {
        if (!key.startsWith('_')) {
          json[key] = this[key]
        }
        return json
      }, json)
      return json
    }
  
    /**
     * Build permalink via permalink pattern and page's metadata.
     *
     * @api private
     */
  
    buildPermalink () {
      if (!this._permalink) {
        this._permalink = getPermalink({
          pattern: this.frontmatter.permalink || this._permalinkPattern,
          slug: this.slug,
          date: this.date,
          localePath: this._localePath,
          regularPath: this.regularPath
        })
      }
  
      if (this._permalink) {
        this.path = this._permalink
      }
    }
  
    /**
     * Execute the page enhancers. A enhancer could do following things:
     *
     *   1. Modify page's frontmatter.
     *   2. Add extra field to the page.
     *
     * @api private
     */
  
    enhance (enhancers) {
      for (const { name: pluginName, value: enhancer } of enhancers) {
        try {
          enhancer(this)
        } catch (error) {
          console.log(error)
          throw new Error(`[${pluginName}] excuete extendPageData failed.`)
        }
      }
    }
  }
  