diff --git a/lib/prepare.js b/lib/prepare.js deleted file mode 100644 index 6ebb351df5..0000000000 --- a/lib/prepare.js +++ /dev/null @@ -1,352 +0,0 @@ -const path = require('path') -const fs = require('fs-extra') -const globby = require('globby') -const createMarkdown = require('./markdown') -const loadConfig = require('./util/loadConfig') -const tempPath = path.resolve(__dirname, 'app/.temp') -const { - encodePath, - inferTitle, - extractHeaders, - parseFrontmatter, - getGitLastUpdatedTimeStamp -} = require('./util') - -fs.ensureDirSync(tempPath) - -const tempCache = new Map() -async function writeTemp (file, content) { - // cache write to avoid hitting the dist if it didn't change - const cached = tempCache.get(file) - if (cached !== content) { - await fs.writeFile(path.join(tempPath, file), content) - tempCache.set(file, content) - } -} - -async function writeEnhanceTemp (destName, srcPath) { - await writeTemp( - destName, - fs.existsSync(srcPath) - ? `export { default } from ${JSON.stringify(srcPath)}` - : `export default function () {}` - ) -} - -module.exports = async function prepare (sourceDir) { - // 1. load options - const options = await resolveOptions(sourceDir) - - // 2. generate routes & user components registration code - const routesCode = await genRoutesFile(options) - const componentCode = await genComponentRegistrationFile(options) - - await writeTemp('routes.js', [ - componentCode, - routesCode - ].join('\n')) - - // 3. generate siteData - const dataCode = `export const siteData = ${JSON.stringify(options.siteData, null, 2)}` - await writeTemp('siteData.js', dataCode) - - // 4. handle user override - const overridePath = path.resolve(sourceDir, '.vuepress/override.styl') - const hasUserOverride = fs.existsSync(overridePath) - await writeTemp(`override.styl`, hasUserOverride ? `@import(${JSON.stringify(overridePath)})` : ``) - - // 5. handle enhanceApp.js - const enhanceAppPath = path.resolve(sourceDir, '.vuepress/enhanceApp.js') - await writeEnhanceTemp('enhanceApp.js', enhanceAppPath) - - // 6. handle the theme enhanceApp.js - await writeEnhanceTemp('themeEnhanceApp.js', options.themeEnhanceAppPath) - - return options -} - -async function resolveOptions (sourceDir) { - const vuepressDir = path.resolve(sourceDir, '.vuepress') - const siteConfig = loadConfig(vuepressDir) - - // normalize head tag urls for base - const base = siteConfig.base || '/' - if (base !== '/' && siteConfig.head) { - siteConfig.head.forEach(tag => { - const attrs = tag[1] - if (attrs) { - for (const name in attrs) { - if (name === 'src' || name === 'href') { - const value = attrs[name] - if (value.charAt(0) === '/') { - attrs[name] = base + value.slice(1) - } - } - } - } - }) - } - - // resolve outDir - const outDir = siteConfig.dest - ? path.resolve(siteConfig.dest) - : path.resolve(sourceDir, '.vuepress/dist') - - // resolve theme - const useDefaultTheme = ( - !siteConfig.theme && - !fs.existsSync(path.resolve(vuepressDir, 'theme')) - ) - const defaultThemePath = path.resolve(__dirname, 'default-theme') - let themePath = null - let themeLayoutPath = null - let themeNotFoundPath = null - let themeEnhanceAppPath = null - - if (useDefaultTheme) { - // use default theme - themePath = defaultThemePath - themeLayoutPath = path.resolve(defaultThemePath, 'Layout.vue') - themeNotFoundPath = path.resolve(defaultThemePath, 'NotFound.vue') - } else { - // resolve theme Layout - if (siteConfig.theme) { - // use external theme - try { - themeLayoutPath = require.resolve(`vuepress-theme-${siteConfig.theme}/Layout.vue`, { - paths: [ - path.resolve(__dirname, '../node_modules'), - path.resolve(sourceDir) - ] - }) - themePath = path.dirname(themeLayoutPath) - } catch (e) { - throw new Error(`[vuepress] Failed to load custom theme "${ - siteConfig.theme - }". File vuepress-theme-${siteConfig.theme}/Layout.vue does not exist.`) - } - } else { - // use custom theme - themePath = path.resolve(vuepressDir, 'theme') - themeLayoutPath = path.resolve(themePath, 'Layout.vue') - if (!fs.existsSync(themeLayoutPath)) { - throw new Error(`[vuepress] Cannot resolve Layout.vue file in .vuepress/theme.`) - } - } - - // resolve theme NotFound - themeNotFoundPath = path.resolve(themePath, 'NotFound.vue') - if (!fs.existsSync(themeNotFoundPath)) { - themeNotFoundPath = path.resolve(defaultThemePath, 'NotFound.vue') - } - - // resolve theme enhanceApp - themeEnhanceAppPath = path.resolve(themePath, 'enhanceApp.js') - if (!fs.existsSync(themeEnhanceAppPath)) { - themeEnhanceAppPath = null - } - } - - // resolve theme config - const themeConfig = siteConfig.themeConfig || {} - - // resolve algolia - const isAlgoliaSearch = ( - themeConfig.algolia || - Object.keys(siteConfig.locales && themeConfig.locales || {}) - .some(base => themeConfig.locales[base].algolia) - ) - - // resolve markdown - const markdown = createMarkdown(siteConfig) - - // resolve pageFiles - const pageFiles = sort(await globby(['**/*.md', '!.vuepress', '!node_modules'], { cwd: sourceDir })) - - // resolve lastUpdated - const shouldResolveLastUpdated = ( - themeConfig.lastUpdated || - Object.keys(siteConfig.locales && themeConfig.locales || {}) - .some(base => themeConfig.locales[base].lastUpdated) - ) - - // resolve pagesData - const pagesData = await Promise.all(pageFiles.map(async (file) => { - const filepath = path.resolve(sourceDir, file) - const key = 'v-' + Math.random().toString(16).slice(2) - const data = { - key, - path: encodePath(fileToPath(file)) - } - - if (shouldResolveLastUpdated) { - data.lastUpdated = getGitLastUpdatedTimeStamp(filepath) - } - - // extract yaml frontmatter - const content = await fs.readFile(filepath, 'utf-8') - const frontmatter = parseFrontmatter(content) - // infer title - const title = inferTitle(frontmatter) - if (title) { - data.title = title - } - const headers = extractHeaders( - frontmatter.content, - ['h2', 'h3'], - markdown - ) - if (headers.length) { - data.headers = headers - } - if (Object.keys(frontmatter.data).length) { - data.frontmatter = frontmatter.data - } - if (frontmatter.excerpt) { - const { html } = markdown.render(frontmatter.excerpt) - data.excerpt = html - } - return data - })) - - // resolve site data - const siteData = { - title: siteConfig.title || '', - description: siteConfig.description || '', - base, - pages: pagesData, - themeConfig, - locales: siteConfig.locales - } - - const options = { - siteConfig, - siteData, - sourceDir, - outDir, - publicPath: base, - pageFiles, - pagesData, - themePath, - themeLayoutPath, - themeNotFoundPath, - themeEnhanceAppPath, - useDefaultTheme, - isAlgoliaSearch, - markdown - } - - return options -} - -async function genComponentRegistrationFile ({ sourceDir }) { - function genImport (file) { - const name = fileToComponentName(file) - const baseDir = path.resolve(sourceDir, '.vuepress/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)) || [] - return `import Vue from 'vue'\n` + components.map(genImport).join('\n') -} - -const indexRE = /(^|.*\/)(index|readme)\.md$/i -const extRE = /\.(vue|md)$/ - -function fileToPath (file) { - if (isIndexFile(file)) { - // README.md -> / - // foo/README.md -> /foo/ - return file.replace(indexRE, '/$1') - } else { - // foo.md -> /foo.html - // foo/bar.md -> /foo/bar.html - return `/${file.replace(extRE, '').replace(/\\/g, '/')}.html` - } -} - -function fileToComponentName (file) { - let normalizedName = file - .replace(/\/|\\/g, '-') - .replace(extRE, '') - if (isIndexFile(file)) { - normalizedName = normalizedName.replace(/readme$/i, 'index') - } - const pagePrefix = /\.md$/.test(file) ? `page-` : `` - return `${pagePrefix}${normalizedName}` -} - -function isIndexFile (file) { - return indexRE.test(file) -} - -async function resolveComponents (sourceDir) { - const componentDir = path.resolve(sourceDir, '.vuepress/components') - if (!fs.existsSync(componentDir)) { - return - } - return sort(await globby(['**/*.vue'], { cwd: componentDir })) -} - -async function genRoutesFile ({ siteData: { pages }, sourceDir, pageFiles }) { - function genRoute ({ path: pagePath, key: componentName }, index) { - const file = pageFiles[index] - const filePath = path.resolve(sourceDir, file) - let code = ` - { - name: ${JSON.stringify(componentName)}, - path: ${JSON.stringify(pagePath)}, - component: ThemeLayout, - beforeEnter: (to, from, next) => { - import(${JSON.stringify(filePath)}).then(comp => { - Vue.component(${JSON.stringify(componentName)}, comp.default) - next() - }) - } - }` - - const dncodedPath = decodeURIComponent(pagePath) - if (dncodedPath !== pagePath) { - code += `, - { - path: ${JSON.stringify(dncodedPath)}, - redirect: ${JSON.stringify(pagePath)} - }` - } - - if (/\/$/.test(pagePath)) { - code += `, - { - path: ${JSON.stringify(pagePath + 'index.html')}, - redirect: ${JSON.stringify(pagePath)} - }` - } - - return code - } - - const notFoundRoute = `, - { - path: '*', - component: ThemeNotFound - }` - - return ( - `import ThemeLayout from '@themeLayout'\n` + - `import ThemeNotFound from '@themeNotFound'\n` + - `import { injectMixins } from '@app/util'\n` + - `import rootMixins from '@app/root-mixins'\n\n` + - `injectMixins(ThemeLayout, rootMixins)\n` + - `injectMixins(ThemeNotFound, rootMixins)\n\n` + - `export const routes = [${pages.map(genRoute).join(',')}${notFoundRoute}\n]` - ) -} - -function sort (arr) { - return arr.sort((a, b) => { - if (a < b) return -1 - if (a > b) return 1 - return 0 - }) -} diff --git a/lib/prepare/codegen.js b/lib/prepare/codegen.js new file mode 100644 index 0000000000..52e85355a2 --- /dev/null +++ b/lib/prepare/codegen.js @@ -0,0 +1,74 @@ +const path = require('path') +const { fileToComponentName, resolveComponents } = require('./util') + +exports.genRoutesFile = async function ({ + siteData: { pages }, + sourceDir, + pageFiles +}) { + function genRoute ({ path: pagePath, key: componentName }, index) { + const file = pageFiles[index] + const filePath = path.resolve(sourceDir, file) + let code = ` + { + name: ${JSON.stringify(componentName)}, + path: ${JSON.stringify(pagePath)}, + component: ThemeLayout, + beforeEnter: (to, from, next) => { + import(${JSON.stringify(filePath)}).then(comp => { + Vue.component(${JSON.stringify(componentName)}, comp.default) + next() + }) + } + }` + + const dncodedPath = decodeURIComponent(pagePath) + if (dncodedPath !== pagePath) { + code += `, + { + path: ${JSON.stringify(dncodedPath)}, + redirect: ${JSON.stringify(pagePath)} + }` + } + + if (/\/$/.test(pagePath)) { + code += `, + { + path: ${JSON.stringify(pagePath + 'index.html')}, + redirect: ${JSON.stringify(pagePath)} + }` + } + + return code + } + + const notFoundRoute = `, + { + path: '*', + component: ThemeNotFound + }` + + return ( + `import ThemeLayout from '@themeLayout'\n` + + `import ThemeNotFound from '@themeNotFound'\n` + + `import { injectMixins } from '@app/util'\n` + + `import rootMixins from '@app/root-mixins'\n\n` + + `injectMixins(ThemeLayout, rootMixins)\n` + + `injectMixins(ThemeNotFound, rootMixins)\n\n` + + `export const routes = [${pages.map(genRoute).join(',')}${notFoundRoute}\n]` + ) +} + +exports.genComponentRegistrationFile = async function ({ sourceDir }) { + function genImport (file) { + const name = fileToComponentName(file) + const baseDir = path.resolve(sourceDir, '.vuepress/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)) || [] + return `import Vue from 'vue'\n` + components.map(genImport).join('\n') +} + diff --git a/lib/prepare/index.js b/lib/prepare/index.js new file mode 100644 index 0000000000..b2a0a0b45f --- /dev/null +++ b/lib/prepare/index.js @@ -0,0 +1,37 @@ +const path = require('path') +const fs = require('fs-extra') +const resolveOptions = require('./resolveOptions') +const { genRoutesFile, genComponentRegistrationFile } = require('./codegen') +const { writeTemp, writeEnhanceTemp } = require('./util') + +module.exports = async function prepare (sourceDir) { + // 1. load options + const options = await resolveOptions(sourceDir) + + // 2. generate routes & user components registration code + const routesCode = await genRoutesFile(options) + const componentCode = await genComponentRegistrationFile(options) + + await writeTemp('routes.js', [ + componentCode, + routesCode + ].join('\n')) + + // 3. generate siteData + const dataCode = `export const siteData = ${JSON.stringify(options.siteData, null, 2)}` + await writeTemp('siteData.js', dataCode) + + // 4. handle user override + const overridePath = path.resolve(sourceDir, '.vuepress/override.styl') + const hasUserOverride = fs.existsSync(overridePath) + await writeTemp(`override.styl`, hasUserOverride ? `@import(${JSON.stringify(overridePath)})` : ``) + + // 5. handle enhanceApp.js + const enhanceAppPath = path.resolve(sourceDir, '.vuepress/enhanceApp.js') + await writeEnhanceTemp('enhanceApp.js', enhanceAppPath) + + // 6. handle the theme enhanceApp.js + await writeEnhanceTemp('themeEnhanceApp.js', options.themeEnhanceAppPath) + + return options +} diff --git a/lib/util/loadConfig.js b/lib/prepare/loadConfig.js similarity index 100% rename from lib/util/loadConfig.js rename to lib/prepare/loadConfig.js diff --git a/lib/prepare/resolveOptions.js b/lib/prepare/resolveOptions.js new file mode 100644 index 0000000000..76fa2fcc78 --- /dev/null +++ b/lib/prepare/resolveOptions.js @@ -0,0 +1,185 @@ +const fs = require('fs-extra') +const path = require('path') +const globby = require('globby') +const createMarkdown = require('../markdown') +const loadConfig = require('./loadConfig') +const { encodePath, fileToPath, sort, getGitLastUpdatedTimeStamp } = require('./util') +const { + inferTitle, + extractHeaders, + parseFrontmatter +} = require('../util/index') + +module.exports = async function resolveOptions (sourceDir) { + const vuepressDir = path.resolve(sourceDir, '.vuepress') + const siteConfig = loadConfig(vuepressDir) + + // normalize head tag urls for base + const base = siteConfig.base || '/' + if (base !== '/' && siteConfig.head) { + siteConfig.head.forEach(tag => { + const attrs = tag[1] + if (attrs) { + for (const name in attrs) { + if (name === 'src' || name === 'href') { + const value = attrs[name] + if (value.charAt(0) === '/') { + attrs[name] = base + value.slice(1) + } + } + } + } + }) + } + + // resolve outDir + const outDir = siteConfig.dest + ? path.resolve(siteConfig.dest) + : path.resolve(sourceDir, '.vuepress/dist') + + // resolve theme + const useDefaultTheme = ( + !siteConfig.theme && + !fs.existsSync(path.resolve(vuepressDir, 'theme')) + ) + const defaultThemePath = path.resolve(__dirname, '../default-theme') + let themePath = null + let themeLayoutPath = null + let themeNotFoundPath = null + let themeEnhanceAppPath = null + + if (useDefaultTheme) { + // use default theme + themePath = defaultThemePath + themeLayoutPath = path.resolve(defaultThemePath, 'Layout.vue') + themeNotFoundPath = path.resolve(defaultThemePath, 'NotFound.vue') + } else { + // resolve theme Layout + if (siteConfig.theme) { + // use external theme + try { + themeLayoutPath = require.resolve(`vuepress-theme-${siteConfig.theme}/Layout.vue`, { + paths: [ + path.resolve(__dirname, '../../node_modules'), + path.resolve(sourceDir) + ] + }) + themePath = path.dirname(themeLayoutPath) + } catch (e) { + throw new Error(`[vuepress] Failed to load custom theme "${ + siteConfig.theme + }". File vuepress-theme-${siteConfig.theme}/Layout.vue does not exist.`) + } + } else { + // use custom theme + themePath = path.resolve(vuepressDir, 'theme') + themeLayoutPath = path.resolve(themePath, 'Layout.vue') + if (!fs.existsSync(themeLayoutPath)) { + throw new Error(`[vuepress] Cannot resolve Layout.vue file in .vuepress/theme.`) + } + } + + // resolve theme NotFound + themeNotFoundPath = path.resolve(themePath, 'NotFound.vue') + if (!fs.existsSync(themeNotFoundPath)) { + themeNotFoundPath = path.resolve(defaultThemePath, 'NotFound.vue') + } + + // resolve theme enhanceApp + themeEnhanceAppPath = path.resolve(themePath, 'enhanceApp.js') + if (!fs.existsSync(themeEnhanceAppPath)) { + themeEnhanceAppPath = null + } + } + + // resolve theme config + const themeConfig = siteConfig.themeConfig || {} + + // resolve algolia + const isAlgoliaSearch = ( + themeConfig.algolia || + Object.keys(siteConfig.locales && themeConfig.locales || {}) + .some(base => themeConfig.locales[base].algolia) + ) + + // resolve markdown + const markdown = createMarkdown(siteConfig) + + // resolve pageFiles + const pageFiles = sort(await globby(['**/*.md', '!.vuepress', '!node_modules'], { cwd: sourceDir })) + + // resolve lastUpdated + const shouldResolveLastUpdated = ( + themeConfig.lastUpdated || + Object.keys(siteConfig.locales && themeConfig.locales || {}) + .some(base => themeConfig.locales[base].lastUpdated) + ) + + // resolve pagesData + const pagesData = await Promise.all(pageFiles.map(async (file) => { + const filepath = path.resolve(sourceDir, file) + const key = 'v-' + Math.random().toString(16).slice(2) + const data = { + key, + path: encodePath(fileToPath(file)) + } + + if (shouldResolveLastUpdated) { + data.lastUpdated = getGitLastUpdatedTimeStamp(filepath) + } + + // extract yaml frontmatter + const content = await fs.readFile(filepath, 'utf-8') + const frontmatter = parseFrontmatter(content) + // infer title + const title = inferTitle(frontmatter) + if (title) { + data.title = title + } + const headers = extractHeaders( + frontmatter.content, + ['h2', 'h3'], + markdown + ) + if (headers.length) { + data.headers = headers + } + if (Object.keys(frontmatter.data).length) { + data.frontmatter = frontmatter.data + } + if (frontmatter.excerpt) { + const { html } = markdown.render(frontmatter.excerpt) + data.excerpt = html + } + return data + })) + + // resolve site data + const siteData = { + title: siteConfig.title || '', + description: siteConfig.description || '', + base, + pages: pagesData, + themeConfig, + locales: siteConfig.locales + } + + const options = { + siteConfig, + siteData, + sourceDir, + outDir, + publicPath: base, + pageFiles, + pagesData, + themePath, + themeLayoutPath, + themeNotFoundPath, + themeEnhanceAppPath, + useDefaultTheme, + isAlgoliaSearch, + markdown + } + + return options +} diff --git a/lib/prepare/util.js b/lib/prepare/util.js new file mode 100644 index 0000000000..efa96cc9b7 --- /dev/null +++ b/lib/prepare/util.js @@ -0,0 +1,80 @@ +const path = require('path') +const spawn = require('cross-spawn') +const fs = require('fs-extra') +const globby = require('globby') + +const tempPath = path.resolve(__dirname, '../app/.temp') +fs.ensureDirSync(tempPath) + +const tempCache = new Map() +exports.writeTemp = async function (file, content) { + // cache write to avoid hitting the dist if it didn't change + const cached = tempCache.get(file) + if (cached !== content) { + await fs.writeFile(path.join(tempPath, file), content) + tempCache.set(file, content) + } +} + +exports.writeEnhanceTemp = async function (destName, srcPath) { + await exports.writeTemp( + destName, + fs.existsSync(srcPath) + ? `export { default } from ${JSON.stringify(srcPath)}` + : `export default function () {}` + ) +} + +const indexRE = /(^|.*\/)(index|readme)\.md$/i +const extRE = /\.(vue|md)$/ + +exports.fileToPath = function (file) { + if (exports.isIndexFile(file)) { + // README.md -> / + // foo/README.md -> /foo/ + return file.replace(indexRE, '/$1') + } else { + // foo.md -> /foo.html + // foo/bar.md -> /foo/bar.html + return `/${file.replace(extRE, '').replace(/\\/g, '/')}.html` + } +} + +exports.fileToComponentName = function (file) { + let normalizedName = file + .replace(/\/|\\/g, '-') + .replace(extRE, '') + if (exports.isIndexFile(file)) { + normalizedName = normalizedName.replace(/readme$/i, 'index') + } + const pagePrefix = /\.md$/.test(file) ? `page-` : `` + return `${pagePrefix}${normalizedName}` +} + +exports.isIndexFile = function (file) { + return indexRE.test(file) +} + +exports.resolveComponents = async function (sourceDir) { + const componentDir = path.resolve(sourceDir, '.vuepress/components') + if (!fs.existsSync(componentDir)) { + return + } + return exports.sort(await globby(['**/*.vue'], { cwd: componentDir })) +} + +exports.sort = function (arr) { + return arr.sort((a, b) => { + if (a < b) return -1 + if (a > b) return 1 + return 0 + }) +} + +exports.encodePath = function (userpath) { + return userpath.split('/').map(item => encodeURIComponent(item)).join('/') +} + +exports.getGitLastUpdatedTimeStamp = function (filepath) { + return parseInt(spawn.sync('git', ['log', '-1', '--format=%ct', filepath]).stdout.toString('utf-8')) * 1000 +} diff --git a/lib/util/index.js b/lib/util/index.js index 4924ebe0ab..12caf0e7ac 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -1,11 +1,6 @@ -const spawn = require('cross-spawn') const parseHeaders = require('./parseHeaders') -exports.encodePath = path => { - return path.split('/').map(item => encodeURIComponent(item)).join('/') -} - -exports.normalizeHeadTag = tag => { +exports.normalizeHeadTag = function (tag) { if (typeof tag === 'string') { tag = [tag] } @@ -18,7 +13,7 @@ exports.normalizeHeadTag = tag => { } } -exports.applyUserWebpackConfig = (userConfig, config, isServer) => { +exports.applyUserWebpackConfig = function (userConfig, config, isServer) { const merge = require('webpack-merge') if (typeof userConfig === 'object') { return merge(config, userConfig) @@ -32,7 +27,7 @@ exports.applyUserWebpackConfig = (userConfig, config, isServer) => { return config } -exports.inferTitle = frontmatter => { +exports.inferTitle = function (frontmatter) { if (frontmatter.data.home) { return 'Home' } @@ -45,7 +40,7 @@ exports.inferTitle = frontmatter => { } } -exports.parseFrontmatter = content => { +exports.parseFrontmatter = function (content) { const matter = require('gray-matter') const toml = require('toml') @@ -61,7 +56,7 @@ exports.parseFrontmatter = content => { const LRU = require('lru-cache') const cache = LRU({ max: 1000 }) -exports.extractHeaders = (content, include = [], md) => { +exports.extractHeaders = function (content, include = [], md) { const key = content + include.join(',') const hit = cache.get(key) if (hit) { @@ -86,7 +81,3 @@ exports.extractHeaders = (content, include = [], md) => { cache.set(key, res) return res } - -exports.getGitLastUpdatedTimeStamp = filepath => { - return parseInt(spawn.sync('git', ['log', '-1', '--format=%ct', filepath]).stdout.toString('utf-8')) * 1000 -} diff --git a/test/prepare/fixtures/docs-custom-theme/.vuepress/config.js b/test/prepare/fixtures/docs-custom-theme/.vuepress/config.js new file mode 100644 index 0000000000..a5f568cb6d --- /dev/null +++ b/test/prepare/fixtures/docs-custom-theme/.vuepress/config.js @@ -0,0 +1,4 @@ +module.exports = { + title: 'Hello VuePress', + description: 'Just playing around' +} diff --git a/test/prepare/fixtures/docs-custom-theme/.vuepress/theme/Layout.vue b/test/prepare/fixtures/docs-custom-theme/.vuepress/theme/Layout.vue new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/prepare/fixtures/docs-custom-theme/README.md b/test/prepare/fixtures/docs-custom-theme/README.md new file mode 100644 index 0000000000..fdb4d2b582 --- /dev/null +++ b/test/prepare/fixtures/docs-custom-theme/README.md @@ -0,0 +1 @@ +# Simple Docs diff --git a/test/prepare/fixtures/docs-simple-config/.vuepress/config.js b/test/prepare/fixtures/docs-simple-config/.vuepress/config.js new file mode 100644 index 0000000000..70f49c13b3 --- /dev/null +++ b/test/prepare/fixtures/docs-simple-config/.vuepress/config.js @@ -0,0 +1,9 @@ +module.exports = { + title: 'Hello VuePress', + description: 'Just playing around', + dest: 'vuepress', + base: 'vuepress', + head: [ + ['link', { rel: 'icon', href: '/logo.png' }] + ] +} diff --git a/test/prepare/fixtures/docs-simple-config/README.md b/test/prepare/fixtures/docs-simple-config/README.md new file mode 100644 index 0000000000..fdb4d2b582 --- /dev/null +++ b/test/prepare/fixtures/docs-simple-config/README.md @@ -0,0 +1 @@ +# Simple Docs diff --git a/test/prepare/fixtures/docs-simple/README.md b/test/prepare/fixtures/docs-simple/README.md new file mode 100644 index 0000000000..fdb4d2b582 --- /dev/null +++ b/test/prepare/fixtures/docs-simple/README.md @@ -0,0 +1 @@ +# Simple Docs diff --git a/test/prepare/resolveOptions.spec.js b/test/prepare/resolveOptions.spec.js new file mode 100644 index 0000000000..433c565214 --- /dev/null +++ b/test/prepare/resolveOptions.spec.js @@ -0,0 +1,95 @@ +import path from 'path' +import resolveOptions from '@/prepare/resolveOptions.js' + +const DEFAULT_THEME_PATH = path.resolve(__dirname, '../../lib/default-theme') +const DEFAULT_THEME_LAYOUT_PATH = path.resolve(DEFAULT_THEME_PATH, 'Layout.vue') +const DEFAULT_THEME_NOT_FOOUND_PATH = path.resolve(DEFAULT_THEME_PATH, 'NotFound.vue') + +describe('prepare - resolveOptions', () => { + test('single file docs without config.', async () => { + const { docsDir } = getDocsPaths('docs-simple') + const options = await resolveOptions(docsDir) + const { + siteConfig, + siteData, + sourceDir, + outDir, + publicPath, + pageFiles, + pagesData, + themePath, + themeLayoutPath, + themeNotFoundPath, + themeEnhanceAppPath, + useDefaultTheme, + isAlgoliaSearch, + markdown + } = options + expect(siteConfig).toEqual({}) + expect(siteData).toEqual({ + title: '', + description: '', + base: '/', + pages: pagesData, + themeConfig: {}, + locales: undefined + }) + expect(sourceDir).toBe(docsDir) + expect(outDir).toBe(path.resolve(docsDir, '.vuepress/dist')) + expect(publicPath).toBe('/') + expect(pageFiles).toHaveLength(1) + expect(pageFiles[0]).toBe('README.md') + expect(themePath).toBe(DEFAULT_THEME_PATH) + expect(themeLayoutPath).toBe(DEFAULT_THEME_LAYOUT_PATH) + expect(themeNotFoundPath).toBe(DEFAULT_THEME_NOT_FOOUND_PATH) + expect(themeEnhanceAppPath).toBe(null) + expect(useDefaultTheme).toBe(true) + expect(isAlgoliaSearch).toBe(false) + expect(typeof markdown).toBe('object') + }) + + test('single file docs with config', async () => { + const { docsDir, configPath } = getDocsPaths('docs-simple-config') + const options = await resolveOptions(docsDir) + const { + siteConfig, + outDir, + publicPath + } = options + expect(siteConfig).toEqual(require(configPath)) + expect(siteConfig.base).toBe('vuepress') + expect(siteConfig.dest).toBe('vuepress') + expect(outDir).toBe(path.resolve('vuepress')) + expect(publicPath).toBe('vuepress') + }) + + test('simple docs with custom theme', async () => { + const paths = getDocsPaths('docs-custom-theme') + const options = await resolveOptions(paths.docsDir) + const { + themePath, + themeLayoutPath, + themeNotFoundPath, + useDefaultTheme + } = options + expect(useDefaultTheme).toBe(false) + expect(themePath).toBe(paths.themePath) + expect(themeLayoutPath).toBe(paths.themeLayoutPath) + expect(themeNotFoundPath).toBe(DEFAULT_THEME_NOT_FOOUND_PATH) // fallbacks to default theme's NotFound component. + }) +}) + +function getDocsPaths (name) { + const docsDir = path.join(__dirname, `fixtures/${name}`) + const configPath = path.join(docsDir, '.vuepress/config.js') + const themePath = path.join(docsDir, '.vuepress/theme') + const themeLayoutPath = path.join(themePath, 'Layout.vue') + const themeNotFoundPath = path.join(themePath, 'NotFound.vue') + return { + docsDir, + configPath, + themePath, + themeLayoutPath, + themeNotFoundPath + } +} diff --git a/test/prepare/util.spec.js b/test/prepare/util.spec.js new file mode 100644 index 0000000000..ca8e1691a4 --- /dev/null +++ b/test/prepare/util.spec.js @@ -0,0 +1,37 @@ +import { + isIndexFile, + fileToPath +} from '@/prepare/util.js' + +describe('prepare - util', () => { + test('isIndexFile', () => { + [ + 'README.md', + 'readme.md', + 'INDEX.md', + 'index.md', + 'foo/README.md', + 'foo/index.md' + ].forEach(file => { + expect(isIndexFile(file)).toBe(true) + }); + [ + 'foo/one.md', + 'one.md' + ].forEach(file => { + expect(isIndexFile(file)).toBe(false) + }) + }) + + test('fileToPath', () => { + const asserts = { + 'README.md': '/', + 'foo/README.md': '/foo/', + 'foo.md': '/foo.html', + 'foo/bar.md': '/foo/bar.html' + } + Object.keys(asserts).forEach(file => { + expect(fileToPath(file)).toBe(asserts[file]) + }) + }) +})