11const { resolve } = require ( 'path' )
2+ const _ = require ( 'lodash' )
23const ImmutableStack = require ( '../../lib/ImmutableStack' )
34const { removeExtension } = require ( '../../lib/contentModelHelpers' )
45const ContentModelEntryNode = require ( '../../lib/ContentModelEntryNode' )
@@ -16,47 +17,74 @@ const models = {
1617const LINKED_FIELD_SYNTAX = / ^ \+ [ ^ ] + $ /
1718
1819const parseLink = ( value ) => {
19- const [ collectionSlug , ...restPath ] = value . replace ( / ^ \+ / g, '' ) . split ( '/' )
20- const entrySlug = restPath . pop ( )
21- const categorySlugs = restPath
22- return {
23- collectionSlug,
24- categorySlugs,
25- entrySlug
26- }
20+ return value . replace ( / ^ \+ / g, '' ) . split ( '/' ) . filter ( Boolean )
2721}
2822
29- const findLinkedEntry = ( contentModel , link ) => {
30- const collectionSlugRe = new RegExp ( link . collectionSlug , 'i' )
31- const collection = contentModel . subtree . collections . find ( c => c . slug . match ( collectionSlugRe ) )
32- let container = collection
23+ const findLinkedNode = ( allNodes , linkPath ) => {
24+ const leafSlug = linkPath . pop ( )
25+ const leafRe = new RegExp ( `^ ${ leafSlug } $` , 'i' )
26+ const leafMatches = allNodes . filter ( p => p . slug . match ( leafRe ) )
3327
34- for ( const categorySlug of link . categorySlugs ) {
35- const categorySlugRe = new RegExp ( categorySlug , 'i' )
36- const category = container . subtree . categories ?. find ( c => c . slug . match ( categorySlugRe ) )
37- if ( ! category ) {
38- break
39- }
40- container = category
28+ if ( ! leafMatches . length ) {
29+ return undefined
4130 }
4231
43- const entrySlugRe = new RegExp ( link . entrySlug , 'i' )
44- const queue = [ container ]
45- while ( queue . length > 0 ) {
46- const node = queue . shift ( )
47- if ( node . slug ?. match ( entrySlugRe ) ) {
48- return node
49- }
50- const match = node . subtree . levelPosts ?. find ( p => p . slug . match ( entrySlugRe ) )
51- if ( match ) {
52- return match
53- }
54- if ( node . subtree . categories ) {
55- queue . push ( ...node . subtree . categories )
56- }
32+ if ( leafMatches . length === 1 ) {
33+ return leafMatches [ 0 ]
5734 }
5835
59- return undefined
36+ if ( ! linkPath . length ) {
37+ return undefined
38+ }
39+
40+ const paths = linkPath . reverse ( )
41+ return leafMatches . find ( node => {
42+ let ctx = node . context
43+ for ( const path of paths ) {
44+ ctx = ctx . throwUntil ( item => {
45+ return item . slug ?. match ( new RegExp ( `^${ path } $` , 'i' ) )
46+ } )
47+ }
48+ return ! ! ctx . items . length
49+ } )
50+ }
51+
52+ const linkNodes = ( nodes ) => {
53+ nodes . forEach ( node => {
54+ const fields = Object . keys ( node )
55+ Object . keys ( node ) . forEach ( key => {
56+ const value = node [ key ]
57+ if ( Array . isArray ( value ) ) {
58+ for ( let i = 0 ; i < value . length ; i ++ ) {
59+ let valueItem = value [ i ]
60+ if ( ! LINKED_FIELD_SYNTAX . test ( valueItem ) ) {
61+ break
62+ }
63+ const link = parseLink ( valueItem )
64+ const linkedNode = findLinkedNode ( nodes , link )
65+ if ( linkedNode ) {
66+ node [ key ] [ i ] = Object . assign ( { } , linkedNode )
67+ linkBack ( node , linkedNode , key )
68+ } else {
69+ node [ key ] . splice ( i , 1 )
70+ i --
71+ }
72+ }
73+ } else {
74+ if ( ! LINKED_FIELD_SYNTAX . test ( value ) ) {
75+ return
76+ }
77+ const link = parseLink ( value )
78+ const linkedNode = findLinkedNode ( nodes , link )
79+ if ( linkedNode ) {
80+ node [ key ] = Object . assign ( { } , linkedNode )
81+ linkBack ( node , linkedNode , key )
82+ } else {
83+ node [ key ] = undefined
84+ }
85+ }
86+ } )
87+ } )
6088}
6189
6290const linkBack = ( post , entry , key ) => {
@@ -93,46 +121,6 @@ const linkBack = (post, entry, key) => {
93121 }
94122}
95123
96- const linkEntries = ( contentModel ) => {
97- contentModel . subtree . collections . forEach ( collection => {
98- collection . subtree . posts . forEach ( post => {
99- const fields = Object . keys ( post )
100- Object . keys ( post ) . forEach ( key => {
101- const value = post [ key ]
102- if ( Array . isArray ( value ) ) {
103- for ( let i = 0 ; i < value . length ; i ++ ) {
104- let valueItem = value [ i ]
105- if ( ! LINKED_FIELD_SYNTAX . test ( valueItem ) ) {
106- break
107- }
108- const link = parseLink ( valueItem )
109- const entry = findLinkedEntry ( contentModel , link )
110- if ( entry ) {
111- post [ key ] [ i ] = Object . assign ( { } , entry )
112- linkBack ( post , entry , key )
113- } else {
114- post [ key ] . splice ( i , 1 )
115- i --
116- }
117- }
118- } else {
119- if ( ! LINKED_FIELD_SYNTAX . test ( value ) ) {
120- return
121- }
122- const link = parseLink ( value )
123- const entry = findLinkedEntry ( contentModel , link )
124- if ( entry ) {
125- post [ key ] = Object . assign ( { } , entry )
126- linkBack ( post , entry , key )
127- } else {
128- post [ key ] = undefined
129- }
130- }
131- } )
132- } )
133- } )
134- }
135-
136124const defaultSettings = {
137125 permalinkPrefix : '/' ,
138126 out : resolve ( '.' ) ,
@@ -308,7 +296,24 @@ class ContentModel extends ContentModelEntryNode {
308296 }
309297
310298 afterEffects ( ) {
311- linkEntries ( this )
299+ const flatMapDeepCategories = ( container ) => {
300+ return _ . flatMapDeep ( container , ( { subtree } ) => {
301+ if ( subtree . categories . length ) {
302+ return [
303+ subtree . categories ,
304+ flatMapDeepCategories ( subtree . categories )
305+ ]
306+ }
307+ return [ ]
308+ } )
309+ }
310+
311+ linkNodes ( [
312+ ...this . subtree . subpages ,
313+ ...this . subtree . collections ,
314+ ...flatMapDeepCategories ( this . subtree . collections ) ,
315+ ..._ . flatMap ( this . subtree . collections , ( { subtree } ) => subtree . posts )
316+ ] )
312317
313318 this . subtree . collections . forEach ( collection => {
314319 collection . afterEffects ( this . subtree )
0 commit comments