diff options
| author | ruki <waruqi@gmail.com> | 2018-11-08 00:38:48 +0800 |
|---|---|---|
| committer | ruki <waruqi@gmail.com> | 2018-11-07 21:53:09 +0800 |
| commit | 26105034da4fcce7ac883c899d781f016559310d (patch) | |
| tree | c459a5dc4e3aa0972d9919033ece511ce76dd129 /node_modules/vuepress/lib/prepare | |
| parent | 2c77f00f1a7ecb6c8192f9c16d3b2001b254a107 (diff) | |
| download | xmake-docs-26105034da4fcce7ac883c899d781f016559310d.tar.gz xmake-docs-26105034da4fcce7ac883c899d781f016559310d.zip | |
switch to vuepress
Diffstat (limited to 'node_modules/vuepress/lib/prepare')
| -rw-r--r-- | node_modules/vuepress/lib/prepare/codegen.js | 74 | ||||
| -rw-r--r-- | node_modules/vuepress/lib/prepare/index.js | 51 | ||||
| -rw-r--r-- | node_modules/vuepress/lib/prepare/loadConfig.js | 56 | ||||
| -rw-r--r-- | node_modules/vuepress/lib/prepare/resolveOptions.js | 194 | ||||
| -rw-r--r-- | node_modules/vuepress/lib/prepare/util.js | 80 |
5 files changed, 455 insertions, 0 deletions
diff --git a/node_modules/vuepress/lib/prepare/codegen.js b/node_modules/vuepress/lib/prepare/codegen.js new file mode 100644 index 00000000..52e85355 --- /dev/null +++ b/node_modules/vuepress/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/node_modules/vuepress/lib/prepare/index.js b/node_modules/vuepress/lib/prepare/index.js new file mode 100644 index 00000000..7561f791 --- /dev/null +++ b/node_modules/vuepress/lib/prepare/index.js @@ -0,0 +1,51 @@ +const path = require('path') +const fs = require('fs-extra') +const resolveOptions = require('./resolveOptions') +const { genRoutesFile, genComponentRegistrationFile } = require('./codegen') +const { writeTemp, writeEnhanceTemp } = require('./util') +const logger = require('../util/logger') +const chalk = require('chalk') + +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').replace(/[\\]+/g, '/') + const hasUserOverride = fs.existsSync(overridePath) + await writeTemp('override.styl', hasUserOverride ? `@import(${JSON.stringify(overridePath)})` : ``) + + const stylePath = path.resolve(sourceDir, '.vuepress/style.styl').replace(/[\\]+/g, '/') + const hasUserStyle = fs.existsSync(stylePath) + await writeTemp('style.styl', hasUserStyle ? `@import(${JSON.stringify(stylePath)})` : ``) + + // Temporary tip, will be removed at next release. + if (hasUserOverride && !hasUserStyle) { + logger.tip( + `${chalk.magenta('override.styl')} has been split into 2 APIs, we recommend you upgrade to continue.\n` + + ` See: ${chalk.magenta('https://vuepress.vuejs.org/default-theme-config/#simple-css-override')}` + ) + } + + // 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/node_modules/vuepress/lib/prepare/loadConfig.js b/node_modules/vuepress/lib/prepare/loadConfig.js new file mode 100644 index 00000000..aa913ba0 --- /dev/null +++ b/node_modules/vuepress/lib/prepare/loadConfig.js @@ -0,0 +1,56 @@ +const fs = require('fs-extra') +const path = require('path') +const yamlParser = require('js-yaml') +const tomlParser = require('toml') + +module.exports = function loadConfig (vuepressDir, bustCache = true) { + const configPath = path.resolve(vuepressDir, 'config.js') + const configYmlPath = path.resolve(vuepressDir, 'config.yml') + const configTomlPath = path.resolve(vuepressDir, 'config.toml') + + if (bustCache) { + delete require.cache[configPath] + } + + // resolve siteConfig + let siteConfig = {} + if (fs.existsSync(configYmlPath)) { + siteConfig = parseConfig(configYmlPath) + } else if (fs.existsSync(configTomlPath)) { + siteConfig = parseConfig(configTomlPath) + } else if (fs.existsSync(configPath)) { + siteConfig = require(configPath) + } + + return siteConfig +} + +function parseConfig (file) { + const content = fs.readFileSync(file, 'utf-8') + const [extension] = /.\w+$/.exec(file) + let data + + switch (extension) { + case '.yml': + case '.yaml': + data = yamlParser.safeLoad(content) + break + + case '.toml': + data = tomlParser.parse(content) + // reformat to match config since TOML does not allow different data type + // https://github.com/toml-lang/toml#array + const format = [] + if (data.head) { + Object.keys(data.head).forEach(meta => { + data.head[meta].forEach(values => { + format.push([meta, values]) + }) + }) + } + data.head = format + break + } + + return data || {} +} diff --git a/node_modules/vuepress/lib/prepare/resolveOptions.js b/node_modules/vuepress/lib/prepare/resolveOptions.js new file mode 100644 index 00000000..9b97ca8e --- /dev/null +++ b/node_modules/vuepress/lib/prepare/resolveOptions.js @@ -0,0 +1,194 @@ +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 patterns = ['**/*.md', '!.vuepress', '!node_modules'] + if (siteConfig.dest) { + // #654 exclude dest folder when dest dir was set in + // sourceDir but not in '.vuepress' + const outDirRelative = path.relative(sourceDir, outDir) + if (!outDirRelative.includes('..')) { + patterns.push('!' + outDirRelative) + } + } + const pageFiles = sort(await globby(patterns, { 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/node_modules/vuepress/lib/prepare/util.js b/node_modules/vuepress/lib/prepare/util.js new file mode 100644 index 00000000..efa96cc9 --- /dev/null +++ b/node_modules/vuepress/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 +} |
