Skip to content

Commit 19aec1c

Browse files
committed
feat(docs-site): add search
affects: docs-site
1 parent a8e65c2 commit 19aec1c

21 files changed

Lines changed: 851 additions & 406 deletions

packages/docs-site/docs/config.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Don't modify config when runtime
2+
// 'path' should't contains locale and must start with '/'
23

34
import { urlHasDir } from '../src/utils'
45

@@ -41,25 +42,16 @@ export default {
4142
path: '/v1/api',
4243
},
4344
],
45+
search: ['/v1/guide', '/v1/api'],
4446
},
4547
],
48+
SEARCH: ['/v2/guide', '/v2/api'],
4649
I18N: {
4750
locale: 'en',
4851
fallbackLocale: 'en',
4952
locales: { en: 'English', zh: '简体中文' },
5053
messages: {
51-
en: {
52-
Languages: 'Languages',
53-
Home: 'Home',
54-
Works: 'Works',
55-
About: 'About',
56-
Version: 'Version',
57-
Guide: 'Guide',
58-
API: 'API',
59-
Examples: 'Examples',
60-
'Get Started': 'Get Started',
61-
'More Examples': 'More Examples',
62-
},
54+
en: {},
6355
zh: {
6456
Languages: '语言',
6557
Home: '首页',
@@ -71,6 +63,8 @@ export default {
7163
Examples: '例子',
7264
'Get Started': '快速开始',
7365
'More Examples': '更多例子',
66+
Search: '搜索',
67+
'No search results': '没有匹配的结果',
7468
},
7569
},
7670
},

packages/docs-site/docs/en/pro.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ he-tree follows the MIT license and you can use it for free. he-tree pro is comm
1919
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
2020
<input type="hidden" name="cmd" value="_s-xclick">
2121
<table>
22-
<tr><td><input type="hidden" name="on0" value="Licenses">Licenses</td></tr><tr><td><select name="os0">
22+
<tr><td><input type="hidden" name="on0" value="Licenses">Licenses</td></tr><tr><td><select name="os0" class="rounded-sm border border-gray-400 px-2 py-2 outline-none focus:border-primary-400 focus:ring-1 focus:ring-primary-500">
2323
<option value="Regular License">Regular License $ 50.00 USD</option>
2424
<option value="Extended License">Extended License $ 500.00 USD</option>
2525
</select> </td></tr>
26-
<tr><td><input type="hidden" name="on1" value="Your Name">Your Name</td></tr><tr><td><input type="text" name="os1" maxlength="200"></td></tr>
27-
<tr><td><input type="hidden" name="on2" value="Your Email">Your Email</td></tr><tr><td><input type="text" name="os2" maxlength="200"></td></tr>
26+
<tr><td><input type="hidden" name="on1" value="Your Name">Your Name</td></tr><tr><td><input type="text" name="os1" maxlength="200" class="rounded-sm border border-gray-400 px-2 py-2 outline-none focus:border-primary-400 focus:ring-1 focus:ring-primary-500"></td></tr>
27+
<tr><td><input type="hidden" name="on2" value="Your Email">Your Email</td></tr><tr><td><input type="text" name="os2" maxlength="200" class="rounded-sm border border-gray-400 px-2 py-2 outline-none focus:border-primary-400 focus:ring-1 focus:ring-primary-500"></td></tr>
2828
</table>
2929
<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----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-----END PKCS7-----">
3030
<input type="image" src="https://www.paypalobjects.com/en_US/GB/i/btn/btn_buynowCC_LG.gif" border="0" name="submit" alt="PayPal – The safer, easier way to pay online!">

packages/docs-site/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,26 @@
1818
"@tailwindcss/forms": "^0.5.2",
1919
"@tailwindcss/typography": "^0.5.4",
2020
"@types/node": "^18.6.2",
21-
"@virtual-list/vue": "^1.1.4",
2221
"axios": "^0.27.2",
2322
"cheerio": "^1.0.0-rc.12",
2423
"chokidar": "^3.5.3",
2524
"dayjs": "^1.10.4",
2625
"floating-vue": "2.0.0-beta.19",
26+
"fuse.js": "^6.6.2",
2727
"gitalk": "^1.7.2",
2828
"helper-js": "^3.1.2",
2929
"highlight.js": "^11.6.0",
3030
"marked": "^4.0.18",
3131
"mdi-js": "^1.0.1",
3232
"scriptjs": "^2.5.9",
33-
"terser": "^5.7.0",
3433
"vue": "^3.0.5",
3534
"vue-demi": "latest",
3635
"vue-github-button": "^3.0.3",
3736
"vue-gtag": "^2.0.1",
3837
"vue-i18n": "^9.2.0",
39-
"vue-router": "^4.0.6",
40-
"yaml": "^2.1.1"
38+
"vue-router": "^4.1.4",
39+
"@types/fs-extra": "^9.0.13",
40+
"fs-extra": "^10.1.0"
4141
},
4242
"devDependencies": {
4343
"@types/scriptjs": "^0.0.2",
@@ -79,4 +79,4 @@
7979
"*.{css,scss,vue}": "stylelint --fix",
8080
"*": "prettier -w -u"
8181
}
82-
}
82+
}

packages/docs-site/scripts/compile-docs.ts

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as hp from 'helper-js'
2-
import * as fs from 'fs'
2+
import * as fs from 'fs-extra'
33
import * as path from 'path'
4-
import { execSync } from 'child_process'
54
import * as chokidar from 'chokidar'
65
import * as crypto from 'crypto'
76
import * as cheerio from 'cheerio'
@@ -13,11 +12,7 @@ import { docsDir, getLocales } from './utils'
1312
const compiledDir = 'src/compiled-docs'
1413

1514
const start = () => {
16-
if (fs.existsSync(compiledDir)) {
17-
execSync(`rm ${compiledDir} -rf`)
18-
}
19-
fs.mkdirSync(compiledDir)
20-
15+
fs.emptyDirSync(compiledDir) // empty dir; create dir if not existing
2116
const watcher = chokidar.watch(docsDir)
2217
const data = {
2318
routes: {},
@@ -29,6 +24,7 @@ const start = () => {
2924
componentPath: string
3025
path: string
3126
meta: Object
27+
searchData?: boolean
3228
}
3329
}
3430
}
@@ -49,6 +45,19 @@ const start = () => {
4945
fs.writeFileSync(path.join(compiledDir, 'routes.ts'), routesContent)
5046
}, 100).action
5147

48+
const updateSearchDataLoader = hp.debounceTrailing(() => {
49+
let r = `export default {`
50+
for (const info of Object.values(data.routes)) {
51+
if (info.searchData) {
52+
r +=
53+
'\n' +
54+
`"${info.path}": async () => (await import("./searchData/${info.md5Name}.json")).default,`
55+
}
56+
}
57+
r += '}'
58+
fs.outputFileSync(path.join(compiledDir, 'searchDataLoader.ts'), r)
59+
}, 100).action
60+
5261
console.log('start watching doc files')
5362
let first = true
5463
const firstDone = hp.debounceTrailing(async () => {
@@ -77,7 +86,9 @@ const start = () => {
7786
fs.unlinkSync(componentPath)
7887
} else {
7988
const locales = getLocales()
89+
const searchUrls = new Set(getSearchUrls(locales))
8090
let t = filepath.split(path.sep)
91+
let searchData = false
8192
const locale = t[1]
8293
const pathWithoutLocale = t.slice(2).join('/')
8394
const fileContent = fs.readFileSync(filepath).toString()
@@ -86,6 +97,7 @@ const start = () => {
8697
const html = marked(codeDemoReplaced)
8798
let injectedComponents: Record<string, string> = {}
8899
const vueTemplate = handleHtmlForVue(html, filepath, injectedComponents)
100+
const urlPath = genUrl(locale, pathWithoutLocale)
89101

90102
const structure = resolveMdStructure(html)
91103

@@ -95,6 +107,7 @@ const start = () => {
95107
const componentContent = tpl
96108
.replace('<!-- :template -->', vueTemplate)
97109
.replace("':data'", JSON.stringify(structure))
110+
.replaceAll(':url', urlPath)
98111
.replace(
99112
/(?<=\n|^)(<script.*?>)/,
100113
'$1\n' +
@@ -115,7 +128,27 @@ const start = () => {
115128
).replace(/:"(.*?)"/g, ':$1')
116129
)
117130
fs.writeFileSync(componentPath, componentContent)
118-
const urlPath = genUrl(locale, pathWithoutLocale)
131+
if (searchUrls.has(urlPath)) {
132+
const searchDataPath = path.join(
133+
compiledDir,
134+
'./searchData',
135+
md5Name + '.json'
136+
)
137+
searchData = true
138+
const flatSearchData: unknown[] = []
139+
const nodeIDMapping = new WeakMap()
140+
let nodeIndex = 0
141+
hp.walkTreeData(structure, (node, i, parent) => {
142+
nodeIDMapping.set(node, nodeIndex)
143+
flatSearchData.push({
144+
title: node.name,
145+
url: urlPath + '#' + node.id,
146+
pid: parent ? nodeIDMapping.get(parent) : null,
147+
})
148+
nodeIndex++
149+
})
150+
fs.outputJSONSync(searchDataPath, flatSearchData)
151+
}
119152
const alternate: any = {}
120153
for (const locale2 of locales) {
121154
const alternateFile = path.join(docsDir, locale2, pathWithoutLocale)
@@ -130,6 +163,7 @@ const start = () => {
130163
componentPath,
131164
path: urlPath,
132165
meta: routeMeta,
166+
searchData,
133167
}
134168
// update alternate routes
135169
const alternatePaths = Object.values(alternate)
@@ -142,6 +176,7 @@ const start = () => {
142176
}
143177
//
144178
updateRoutes()
179+
updateSearchDataLoader()
145180
//
146181
if (first) {
147182
firstDone()
@@ -152,13 +187,16 @@ const start = () => {
152187
start()
153188

154189
function genUrl(locale: string, pathWithoutLocale: string) {
155-
let t = `/${pathWithoutLocale.replace('.md', '')}`
190+
let t = `/${pathWithoutLocale.replace('.md', '')}`.replace(/^\/\//, '/')
191+
if (t === '/') {
192+
t = ''
193+
}
156194
if (locale !== baseConfig.I18N.locale) {
157195
t = `/${locale}` + t
158196
}
159-
t = t.replace(/index$/, '')
160-
if (!t) {
161-
t = '/'
197+
t = t.replace(/(^|\/)index/, '$1')
198+
if (t[0] !== '/') {
199+
t = '/' + t
162200
}
163201
return t
164202
}
@@ -340,3 +378,24 @@ function internalDocHrefToUrl(href: string, docPath: string) {
340378
}
341379
return url
342380
}
381+
382+
function getSearchUrls(locales: string[]) {
383+
const r: string[] = []
384+
if (baseConfig.SEARCH) {
385+
r.push(...baseConfig.SEARCH)
386+
}
387+
if (baseConfig.SUBPATH) {
388+
for (const sp of baseConfig.SUBPATH) {
389+
if (sp.search) {
390+
r.push(...sp.search)
391+
}
392+
}
393+
}
394+
const r2: string[] = []
395+
for (const url of r) {
396+
for (const locale of locales) {
397+
r2.push(genUrl(locale, url))
398+
}
399+
}
400+
return r2
401+
}

packages/docs-site/src/App.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
.app-inner
33
I18n
44
div(v-is="layout")
5+
VModals
56
PageProgressBar
67
HTMLHead
78
</template>
@@ -10,12 +11,13 @@
1011
import { defineComponent } from 'vue'
1112
import default_layout from './layouts/default_layout.vue'
1213
import I18n from './components/I18n.vue'
14+
import VModals from './components/VModals.vue'
1315
import PageProgressBar from './components/PageProgressBar.vue'
1416
import { api } from './http'
1517
import { HTMLHead } from './HTMLHead'
1618
1719
export default defineComponent({
18-
components: { default_layout, I18n, PageProgressBar, HTMLHead },
20+
components: { default_layout, I18n, VModals, PageProgressBar, HTMLHead },
1921
// props: {},
2022
data() {
2123
return {

packages/docs-site/src/components/Icon.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ span.icon.inline-flex.items-center
66
</template>
77

88
<script lang="ts">
9+
// suitable for google material design icons that imported by css
910
import { defineComponent } from 'vue'
1011
export default defineComponent({
1112
props: {

0 commit comments

Comments
 (0)