@@ -4,6 +4,11 @@ import {
44 removeAtag ,
55 escapeHtml ,
66} from '../../core/render/utils.js' ;
7+ import {
8+ getPath ,
9+ getParentPath ,
10+ isAbsolutePath ,
11+ } from '../../core/router/util.js' ;
712import { markdownToTxt } from './markdown-to-txt.js' ;
813import Dexie from 'dexie' ;
914
@@ -16,10 +21,29 @@ db.version(1).stores({
1621} ) ;
1722
1823async function saveData ( maxAge , expireKey ) {
19- INDEXES = Object . values ( INDEXES ) . flatMap ( innerData =>
20- Object . values ( innerData ) ,
21- ) ;
22- await /** @type {any } */ ( db ) . search . bulkPut ( INDEXES ) ;
24+ const records = [ ] ;
25+
26+ Object . values ( INDEXES ) . forEach ( entry => {
27+ if ( ! entry || typeof entry !== 'object' ) {
28+ return ;
29+ }
30+
31+ // Entry may already be a flat record read from IndexedDB.
32+ if ( 'slug' in entry ) {
33+ records . push ( entry ) ;
34+ return ;
35+ }
36+
37+ // Entry may be a per-path map of slug -> record produced by genIndex().
38+ Object . values ( entry ) . forEach ( item => {
39+ if ( item && typeof item === 'object' && 'slug' in item ) {
40+ records . push ( item ) ;
41+ }
42+ } ) ;
43+ } ) ;
44+
45+ INDEXES = records ;
46+ await /** @type {any } */ ( db ) . search . bulkPut ( records ) ;
2347 await /** @type {any } */ ( db ) . expires . put ( {
2448 key : expireKey ,
2549 value : Date . now ( ) + maxAge ,
@@ -96,6 +120,111 @@ function getListData(token) {
96120 return token . text ;
97121}
98122
123+ function extractFragmentContent ( text , fragment , fullLine ) {
124+ if ( ! fragment ) {
125+ return text ;
126+ }
127+
128+ let fragmentRegex = `(?:###|\\/\\/\\/)\\s*\\[${ fragment } \\]` ;
129+ if ( fullLine ) {
130+ fragmentRegex = `.*${ fragmentRegex } .*\n` ;
131+ }
132+
133+ const pattern = new RegExp (
134+ `(?:${ fragmentRegex } )([\\s\\S]*?)(?:${ fragmentRegex } )` ,
135+ ) ;
136+ const match = text . match ( pattern ) ;
137+ return ( ( match || [ ] ) [ 1 ] || '' ) . trim ( ) ;
138+ }
139+
140+ function collectEmbedRequests ( raw = '' , path , vm ) {
141+ const tokens = window . marked . lexer ( raw ) ;
142+ const requests = [ ] ;
143+
144+ const maybePushEmbed = inlineToken => {
145+ if (
146+ ! inlineToken ||
147+ ( inlineToken . type !== 'link' && inlineToken . type !== 'image' )
148+ ) {
149+ return ;
150+ }
151+
152+ const { config } = getAndRemoveConfig ( inlineToken . title || '' ) ;
153+ if ( ! config . include || ! inlineToken . href ) {
154+ return ;
155+ }
156+
157+ const href = isAbsolutePath ( inlineToken . href )
158+ ? inlineToken . href
159+ : getPath ( vm . router . getBasePath ( ) , getParentPath ( path ) , inlineToken . href ) ;
160+
161+ let type = 'code' ;
162+ if ( / \. ( m d | m a r k d o w n ) / . test ( href ) ) {
163+ type = 'markdown' ;
164+ } else if ( / \. m m d / . test ( href ) ) {
165+ type = 'mermaid' ;
166+ }
167+
168+ requests . push ( {
169+ url : href ,
170+ type,
171+ fragment : config . fragment ,
172+ omitFragmentLine : config . omitFragmentLine ,
173+ } ) ;
174+ } ;
175+
176+ tokens . forEach ( token => {
177+ if ( token . type === 'paragraph' ) {
178+ ( token . tokens || [ ] ) . forEach ( maybePushEmbed ) ;
179+ } else if ( token . type === 'table' ) {
180+ ( token . header || [ ] ) . forEach ( cell => {
181+ ( cell . tokens || [ ] ) . forEach ( maybePushEmbed ) ;
182+ } ) ;
183+ ( token . rows || [ ] ) . forEach ( row => {
184+ row . forEach ( cell => {
185+ ( cell . tokens || [ ] ) . forEach ( maybePushEmbed ) ;
186+ } ) ;
187+ } ) ;
188+ }
189+ } ) ;
190+
191+ return requests ;
192+ }
193+
194+ async function getEmbeddedContent ( raw = '' , path , vm ) {
195+ const requests = collectEmbedRequests ( raw , path , vm ) ;
196+ if ( ! requests . length ) {
197+ return '' ;
198+ }
199+
200+ const results = await Promise . all (
201+ requests . map (
202+ request =>
203+ new Promise ( resolve => {
204+ Docsify . get ( request . url , false , vm . config . requestHeaders ) . then (
205+ text => {
206+ let content = text || '' ;
207+ if ( request . fragment ) {
208+ content = extractFragmentContent (
209+ content ,
210+ request . fragment ,
211+ request . omitFragmentLine ,
212+ ) ;
213+ }
214+
215+ resolve (
216+ request . type === 'markdown' ? content : markdownToTxt ( content ) ,
217+ ) ;
218+ } ,
219+ ( ) => resolve ( '' ) ,
220+ ) ;
221+ } ) ,
222+ ) ,
223+ ) ;
224+
225+ return results . filter ( Boolean ) . join ( '\n' ) ;
226+ }
227+
99228export function genIndex ( path , content = '' , router , depth , indexKey ) {
100229 const tokens = window . marked . lexer ( content ) ;
101230 const slugify = window . Docsify . slugify ;
@@ -205,29 +334,24 @@ export function search(query) {
205334 ) ,
206335 'gi' ,
207336 ) ;
208- let indexTitle = - 1 ;
209- let indexContent = - 1 ;
210337 handlePostTitle = postTitle
211338 ? escapeHtml ( ignoreDiacriticalMarks ( postTitle ) )
212339 : postTitle ;
213340 handlePostContent = postContent
214341 ? escapeHtml ( ignoreDiacriticalMarks ( postContent ) )
215342 : postContent ;
216343
217- indexTitle = postTitle ? handlePostTitle . search ( regEx ) : - 1 ;
218- indexContent = postContent ? handlePostContent . search ( regEx ) : - 1 ;
344+ const indexTitle = postTitle ? handlePostTitle . search ( regEx ) : - 1 ;
345+ let indexContent = postContent ? handlePostContent . search ( regEx ) : - 1 ;
219346
220347 if ( indexTitle >= 0 || indexContent >= 0 ) {
221348 matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0 ;
222349 if ( indexContent < 0 ) {
223350 indexContent = 0 ;
224351 }
225352
226- let start = 0 ;
227- let end = 0 ;
228-
229- start = indexContent < 11 ? 0 : indexContent - 10 ;
230- end = start === 0 ? 100 : indexContent + keyword . length + 90 ;
353+ const start = indexContent < 11 ? 0 : indexContent - 10 ;
354+ let end = start === 0 ? 100 : indexContent + keyword . length + 90 ;
231355
232356 if ( handlePostContent && end > handlePostContent . length ) {
233357 end = handlePostContent . length ;
@@ -306,26 +430,38 @@ export async function init(config, vm) {
306430 const len = paths . length ;
307431 let count = 0 ;
308432
433+ const markComplete = async ( ) => {
434+ if ( len === ++ count ) {
435+ await saveData ( config . maxAge , expireKey ) ;
436+ }
437+ } ;
438+
309439 paths . forEach ( path => {
310440 const pathExists = Array . isArray ( INDEXES )
311441 ? INDEXES . some ( obj => obj . path === path )
312442 : false ;
313443 if ( pathExists ) {
314- return count ++ ;
444+ void markComplete ( ) ;
445+ return ;
315446 }
316447
317448 Docsify . get ( vm . router . getFile ( path ) , false , vm . config . requestHeaders ) . then (
318449 async result => {
450+ const embeddedContent = await getEmbeddedContent ( result , path , vm ) ;
451+ const contentToIndex = embeddedContent
452+ ? `${ result } \n${ embeddedContent } `
453+ : result ;
319454 INDEXES [ path ] = genIndex (
320455 path ,
321- result ,
456+ contentToIndex ,
322457 vm . router ,
323458 config . depth ,
324459 indexKey ,
325460 ) ;
326- if ( len === ++ count ) {
327- await saveData ( config . maxAge , expireKey ) ;
328- }
461+ return markComplete ( ) ;
462+ } ,
463+ ( ) => {
464+ return markComplete ( ) ;
329465 } ,
330466 ) ;
331467 } ) ;
0 commit comments