-
Notifications
You must be signed in to change notification settings - Fork 152
/
module.ts
198 lines (169 loc) · 6.06 KB
/
module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import { resolve, join } from 'path'
import gracefulFs from 'graceful-fs'
import { Module } from '@nuxt/types'
import hash from 'hasha'
import mkdirp from 'mkdirp'
import { DocusDocument, ParserOptions } from '../types'
import { generatePosition, generateSlug, generateTo, isDraft, processDocumentInfo } from './utils/document'
import { destroyStorage, initStorage } from './storage'
import { destroyDB, useDB } from './database'
import { createServerMiddleware } from './server'
import { initParser } from './parser'
import { destroyHooks } from './hooks'
import { updateNavigation } from './utils/navigation'
import { useHooks, logger } from './'
const fs = gracefulFs.promises
function isUrl(string) {
try {
// quick test if the string is an URL
// eslint-disable-next-line no-new
new URL(string)
} catch (_) {
return false
}
return true
}
export default <Module>async function docusModule() {
// Wait for nuxt options to be normalized
const { nuxt, addServerMiddleware, addPlugin, addModule, $docus } = this
const { options } = nuxt
const isSSG = options.dev === false && (options.target === 'static' || options._generate || options.mode === 'spa')
// Inject Docus Admin module in development
if (!isSSG) addModule(resolve(__dirname, '../admin'))
const pluginOptions = {
apiBase: '_docus',
watch: options.dev,
isSSG,
dbPath: ''
}
// Setup docus cache
options.alias['~docus-cache'] = join(options.srcDir, 'node_modules/.cache/docus')
// Inject Docus theme as ~docus
options.alias['~docus'] = resolve(__dirname, 'runtime')
// extend parser options
const parserOptions: Partial<ParserOptions> = { markdown: {} }
await nuxt.callHook('docus:parserOptions', parserOptions)
initParser(parserOptions)
const coreHooks = useHooks()
// Configure content after each hook
coreHooks.hook('docus:storage:beforeInsert', (document: DocusDocument) => {
if (document.extension !== '.md') {
return
}
// Locales or empty array
let locales = options.i18n?.locales || []
// If locales is function, resolve it
locales = typeof locales === 'function' ? locales() : locales
// Map locales or default to 'en'
locales = locales.map(({ code }: { code: string }) => code).join('|') || 'en'
// Get default locale or default to 'en'
const defaultLocale = options.i18n?.defaultLocale || 'en'
const regexp = new RegExp(`^/(${locales})`, 'gi')
const { dir, slug } = document
const _dir = dir.replace(regexp, '')
const _language = dir.replace(_dir, '').replace(/\//, '') || defaultLocale
const _to = `${_dir}/${slug}`.replace(/\/+/, '/')
const position = generatePosition(_to, document)
processDocumentInfo(document)
document.slug = generateSlug(slug)
document.position = position
document.to = generateTo(_to)
document.language = _language
document.draft = document.draft || isDraft(slug)
})
// Initiate storage
const { storage, lazyIndex } = initStorage({
drivers: [
{
base: resolve(options.srcDir, $docus.settings.contentDir),
mountPoint: 'pages'
},
{
base: resolve(options.srcDir, 'data'),
mountPoint: 'data'
}
]
})
addServerMiddleware(createServerMiddleware({ storage, base: pluginOptions.apiBase }))
if (options.dev) {
nuxt.hook('listen', server => server.on('upgrade', (...args) => coreHooks.callHook('upgrade', ...args)))
storage.watch((event, key) => {
updateNavigation(nuxt)
logger.info(`File ${event}: ${key}`)
})
}
nuxt.hook('components:dirs', (dirs: any) => {
dirs.push({
path: resolve(__dirname, 'runtime/components'),
global: true,
level: 2
})
})
nuxt.hook('build:before', () => {
;(async () => {
await lazyIndex()
await updateNavigation(nuxt)
})()
})
nuxt.hook('generate:before', async () => {
await lazyIndex()
await updateNavigation(nuxt)
})
if (isSSG) {
let publicPath = this.options.build.publicPath // can be an url
let routerBasePath = this.options.router.base
/* istanbul ignore if */
if (publicPath[publicPath.length - 1] !== '/') {
publicPath += '/'
}
if (routerBasePath[routerBasePath.length - 1] === '/') {
routerBasePath = routerBasePath.slice(0, -1)
}
pluginOptions.dbPath = isUrl(publicPath)
? `${publicPath}${pluginOptions.apiBase}`
: `${routerBasePath}${publicPath}${pluginOptions.apiBase}`
nuxt.hook('generate:distRemoved', async () => {
const { items, db } = useDB()
// Create a hash to fetch the database
const dbHash = hash(JSON.stringify(items._data)).substr(0, 8)
if (this.options.publicRuntimeConfig) {
;(this.options.publicRuntimeConfig as any).docusDbHash = dbHash
} else {
this.nuxt.hook('vue-renderer:ssr:context', renderContext => {
renderContext.nuxt.docusDbHash = dbHash
})
}
const dir = resolve(this.options.buildDir, 'dist', 'client', pluginOptions.apiBase)
await mkdirp(dir)
await fs.writeFile(join(dir, `db-${dbHash}.json`), db.serialize(), 'utf-8')
})
}
addPlugin({
src: resolve(__dirname, 'plugin.js'),
filename: 'docus.js',
options: pluginOptions
})
nuxt.hook('vue-renderer:context', (ssrContext: any) => {
const { query } = useDB()
ssrContext.docus = ssrContext.docus || {}
ssrContext.docus.createQuery = query
})
// If pages/ does not exists, disable Nuxt pages parser (to avoid warning) and watch pages/ creation for full restart
nuxt.hook('build:before', async () => {
// To support older version of Nuxt
const pagesDirPath = resolve(options.srcDir, options.dir.pages)
const pagesDirExists = await fs.stat(pagesDirPath).catch(() => false)
if (!pagesDirExists) {
options.build.createRoutes = () => []
options.watch.push(pagesDirPath)
}
})
nuxt.callHook('docus:storage:ready')
// Watch Docus while DOCUS_DEV is set
if (process.env.DOCUS_DEV) options.watch.push(resolve(__dirname, '../'))
nuxt.hook('close', () => {
destroyHooks()
destroyDB()
destroyStorage()
})
}