@@ -19,13 +19,26 @@ import {
1919 bookConfig ,
2020 isBookIndexPage ,
2121} from "../../project/types/book/book-shared.ts" ;
22- import { join } from "path/mod.ts" ;
22+ import { join , relative } from "path/mod.ts" ;
2323
2424import { plaintextFormat } from "../formats-shared.ts" ;
2525import { dirAndStem } from "../../core/path.ts" ;
2626import { formatResourcePath } from "../../core/resources.ts" ;
2727import { ProjectContext } from "../../project/types.ts" ;
28- import { kShiftHeadingLevelBy } from "../../config/constants.ts" ;
28+ import {
29+ kSectionTitleReferences ,
30+ kShiftHeadingLevelBy ,
31+ } from "../../config/constants.ts" ;
32+ import { existsSync } from "fs/mod.ts" ;
33+ import { ProjectOutputFile } from "../../project/types/types.ts" ;
34+ import { lines } from "../../core/text.ts" ;
35+ import {
36+ bookBibliography ,
37+ generateBibliography ,
38+ } from "../../project/types/book/book-bibliography.ts" ;
39+ import { citeIndex } from "../../project/project-cites.ts" ;
40+ import { projectOutputDir } from "../../project/project-shared.ts" ;
41+ import { PandocOptions } from "../../command/render/types.ts" ;
2942
3043type AsciiDocBookPart = string | {
3144 partPath ?: string ;
@@ -50,6 +63,8 @@ const kFormatOutputDir = "asciidoc";
5063// Ref target marks the refs div so the post process can inject the bibliography
5164const kRefTargetIdentifier = "refs-target-identifier" ;
5265const kRefTargetIndentifierValue = "// quarto-refs-target-378736AB" ;
66+ const kRefTargetIndentifierMatch = / \/ \/ q u a r t o - r e f s - t a r g e t - 3 7 8 7 3 6 A B / g;
67+
5368const kUseAsciidocNativeCites = "use-asciidoc-native-cites" ;
5469
5570// This provide book specific behavior for producing asciidoc books
@@ -93,37 +108,116 @@ const asciidocBookExtension = {
93108 return { format } ;
94109 }
95110 } ,
96- async bookPostProcess ( _format : Format , _project : ProjectContext ) {
111+ async bookPostRender (
112+ format : Format ,
113+ context : ProjectContext ,
114+ _incremental : boolean ,
115+ outputFiles : ProjectOutputFile [ ] ,
116+ ) {
117+ // Find the explicit ref target
118+ let refsTarget ;
119+ let indexPage ;
120+ const projDir = projectOutputDir ( context ) ;
121+ const outDir = join ( projDir , kFormatOutputDir ) ;
122+ for ( const outputFile of outputFiles ) {
123+ const path = outputFile . file ;
124+ if ( existsSync ( path ) ) {
125+ const contents = Deno . readTextFileSync ( path ) ;
126+ if ( contents . match ( kRefTargetIndentifierMatch ) ) {
127+ refsTarget = path ;
128+ }
129+ }
130+ const relativePath = relative ( outDir , outputFile . file ) ;
131+ if ( isBookIndexPage ( relativePath ) ) {
132+ indexPage = outputFile . file ;
133+ }
134+ }
135+
136+ // If there is a refs target, then generate the bibliography and
137+ // replace the refs target with the rendered references
138+ //
139+ // If not, just append the bibliography to the index page itself
140+ if ( refsTarget || indexPage ) {
141+ // Read the cites
142+ const cites : Set < string > = new Set ( ) ;
143+
144+ const citeIndexObj = citeIndex ( context . dir ) ;
145+ for ( const key of Object . keys ( citeIndexObj ) ) {
146+ const citeArr = citeIndexObj [ key ] ;
147+ citeArr . forEach ( ( cite ) => {
148+ cites . add ( cite ) ;
149+ } ) ;
150+ }
151+
152+ // Generate the bibliograp context for this document
153+ const biblio = await bookBibliography ( outputFiles , context ) ;
154+
155+ // Add explicitl added cites via nocite
156+ if ( biblio . nocite ) {
157+ biblio . nocite . forEach ( ( no ) => {
158+ cites . add ( no ) ;
159+ } ) ;
160+ }
161+
162+ // Generate the bibliography
163+ let bibliographyContents = "" ;
164+ if ( biblio . bibliographyPaths && cites . size ) {
165+ bibliographyContents = await generateBibliography (
166+ context ,
167+ biblio . bibliographyPaths ,
168+ Array . from ( cites ) ,
169+ "asciidoc" ,
170+ biblio . csl ,
171+ ) ;
172+ }
173+
174+ // Clean the generated bibliography
175+ // - remove the leading `refs` indicator
176+ // - make the bibliography an unordered list
177+ // see https://docs.asciidoctor.org/asciidoc/latest/sections/bibliography/
178+ const cleanedBibliography = lines ( bibliographyContents ) . filter (
179+ ( line ) => {
180+ return line !== "[[refs]]" ;
181+ } ,
182+ ) . map ( ( line ) => {
183+ if ( line . startsWith ( "[[ref-" ) ) {
184+ return line . replace ( "[[ref-" , "- [[" ) ;
185+ } else {
186+ return ` ${ line } ` ;
187+ }
188+ } ) . join ( "\n" ) . trim ( ) ;
189+
190+ if ( refsTarget ) {
191+ // Replace the refs target with the bibliography (or empty to remove it)
192+ const refTargetContents = Deno . readTextFileSync ( refsTarget ) ;
193+ const updatedContents = refTargetContents . replace (
194+ kRefTargetIndentifierMatch ,
195+ cleanedBibliography ,
196+ ) ;
197+ Deno . writeTextFileSync (
198+ refsTarget ,
199+ updatedContents ,
200+ ) ;
201+ } else if ( indexPage ) {
202+ const title = format . language [ kSectionTitleReferences ] || "References" ;
203+ const titleAdoc = `== ${ title } ` ;
204+
205+ const indexPageContents = Deno . readTextFileSync ( indexPage ) ;
206+ const updatedContents =
207+ `${ indexPageContents } \n\n${ titleAdoc } \n\n[[refs]]\n\n${ cleanedBibliography } ` ;
208+ Deno . writeTextFileSync (
209+ indexPage ,
210+ updatedContents ,
211+ ) ;
212+ }
213+ }
97214 } ,
98215} ;
99216
100217async function bookRootPageMarkdown ( project : ProjectContext ) {
101- const bookContents = bookConfig (
102- kBookChapters ,
103- project . config ,
104- ) as BookChapterEntry [ ] ;
105-
106- // Find chapter and appendices
107- const chapters = await resolveBookInputs (
108- bookContents ,
109- project ,
110- ( input : string ) => {
111- // Exclude the index page from the chapter list (since we'll append
112- // this to the index page contents)
113- return ! isBookIndexPage ( input ) ;
114- } ,
115- ) ;
116-
117- const bookApps = bookConfig (
118- kBookAppendix ,
119- project . config ,
120- ) as string [ ] ;
121- const appendices = bookApps
122- ? await resolveBookInputs (
123- bookApps ,
124- project ,
125- )
126- : [ ] ;
218+ // Read the chapter and appendix inputs
219+ const chapters = await chapterInputs ( project ) ;
220+ const appendices = await appendixInputs ( project ) ;
127221
128222 // Write a book asciidoc file
129223 const fileContents = [
@@ -177,6 +271,37 @@ function appendix(path: string) {
177271 return `[appendix]\n${ chapter ( path ) } \n` ;
178272}
179273
274+ async function chapterInputs ( project : ProjectContext ) {
275+ const bookContents = bookConfig (
276+ kBookChapters ,
277+ project . config ,
278+ ) as BookChapterEntry [ ] ;
279+
280+ // Find chapter and appendices
281+ return await resolveBookInputs (
282+ bookContents ,
283+ project ,
284+ ( input : string ) => {
285+ // Exclude the index page from the chapter list (since we'll append
286+ // this to the index page contents)
287+ return ! isBookIndexPage ( input ) ;
288+ } ,
289+ ) ;
290+ }
291+
292+ async function appendixInputs ( project : ProjectContext ) {
293+ const bookApps = bookConfig (
294+ kBookAppendix ,
295+ project . config ,
296+ ) as string [ ] ;
297+ return bookApps
298+ ? await resolveBookInputs (
299+ bookApps ,
300+ project ,
301+ )
302+ : [ ] ;
303+ }
304+
180305async function resolveBookInputs (
181306 inputs : BookChapterEntry [ ] ,
182307 project : ProjectContext ,
0 commit comments