@@ -62,6 +62,8 @@ import {
6262 isFilterEntryPoint ,
6363 QuartoFilter ,
6464 QuartoFilterEntryPoint ,
65+ QuartoFilterEntryPointQualified ,
66+ QuartoFilterEntryPointQualifiedFull ,
6567} from "../../config/types.ts" ;
6668import { QuartoFilterSpec } from "./types.ts" ;
6769import { Metadata } from "../../config/types.ts" ;
@@ -85,7 +87,7 @@ import { quartoConfig } from "../../core/quarto.ts";
8587import { metadataNormalizationFilterActive } from "./normalize.ts" ;
8688import { kCodeAnnotations } from "../../format/html/format-html-shared.ts" ;
8789import { projectOutputDir } from "../../project/project-shared.ts" ;
88- import { join , relative } from "../../deno_ral/path.ts" ;
90+ import { extname , join , relative , resolve } from "../../deno_ral/path.ts" ;
8991import { citeIndexFilterParams } from "../../project/project-cites.ts" ;
9092import { debug } from "../../deno_ral/log.ts" ;
9193import { kJatsSubarticle } from "../../format/jats/format-jats-types.ts" ;
@@ -95,6 +97,7 @@ import { pythonExec } from "../../core/jupyter/exec.ts";
9597import { kTocIndent } from "../../config/constants.ts" ;
9698import { isWindows } from "../../deno_ral/platform.ts" ;
9799import { tinyTexBinDir } from "../../tools/impl/tinytex-info.ts" ;
100+ import { warn } from "log" ;
98101
99102const kQuartoParams = "quarto-params" ;
100103
@@ -716,7 +719,7 @@ const kQuartoCiteProcMarker = "citeproc";
716719
717720// NB: this mutates `pandoc.citeproc`
718721export async function resolveFilters (
719- filters : QuartoFilter [ ] ,
722+ filtersParam : QuartoFilter [ ] ,
720723 options : PandocOptions ,
721724 pandoc : FormatPandoc ,
722725) : Promise < QuartoFilterSpec | undefined > {
@@ -729,8 +732,10 @@ export async function resolveFilters(
729732 quartoFilters . push ( quartoMainFilter ( ) ) ;
730733
731734 // Resolve any filters that are provided by an extension
732- filters = await resolveFilterExtension ( options , filters ) ;
733- let quartoLoc = filters . findIndex ( ( filter ) => filter === kQuartoFilterMarker ) ;
735+ const filters = await resolveFilterExtension ( options , filtersParam ) ;
736+ let quartoLoc = filters . findIndex ( ( filter ) =>
737+ filter . type === kQuartoFilterMarker
738+ ) ;
734739 if ( quartoLoc === - 1 ) {
735740 quartoLoc = Infinity ; // if no quarto marker, put our filters at the beginning
736741 }
@@ -742,31 +747,28 @@ export async function resolveFilters(
742747 // if 'quarto' is not in the filter, all declarations go to the kQuartoPre entry point
743748 //
744749 // (note that citeproc will in all cases run last)
745- const entryPoints : QuartoFilterEntryPoint [ ] = filters
746- . filter ( ( f ) => f !== "quarto" ) // remove quarto marker
750+
751+ // citeproc at the very end so all other filters can interact with citations
752+
753+ // remove special filter markers
754+ const fullFilters = filters . filter ( ( filter ) =>
755+ filter . type !== kQuartoCiteProcMarker && filter . type !== kQuartoFilterMarker
756+ ) as QuartoFilterEntryPointQualifiedFull [ ] ;
757+
758+ const entryPoints : QuartoFilterEntryPoint [ ] = fullFilters
747759 . map ( ( filter , i ) => {
748- if ( isFilterEntryPoint ( filter ) ) {
749- return {
750- ...filter ,
751- path : join ( options . project . dir , filter . path ) ,
752- } ; // send entry-point-style filters unchanged
753- }
754- const at = quartoLoc > i ? kQuartoPre : kQuartoPost ;
755- const result : QuartoFilterEntryPoint = typeof filter === "string"
756- ? {
757- "at" : at ,
758- "type" : filter . endsWith ( ".lua" ) ? "lua" : "json" ,
759- "path" : join ( options . project . dir , filter ) ,
760- }
761- : {
762- "at" : at ,
763- ...filter ,
764- } ;
760+ const at = filter . at === "__quarto-auto"
761+ ? ( quartoLoc > i ? kQuartoPre : kQuartoPost )
762+ : filter . at ;
763+
764+ const result : QuartoFilterEntryPoint = {
765+ "at" : at ,
766+ "type" : filter . type ,
767+ "path" : filter . path . path ,
768+ } ;
765769 return result ;
766770 } ) ;
767771
768- // citeproc at the very end so all other filters can interact with citations
769- filters = filters . filter ( ( filter ) => filter !== kQuartoCiteProcMarker ) ;
770772 const citeproc = citeMethod ( options ) === kQuartoCiteProcMarker ;
771773 if ( citeproc ) {
772774 // If we're explicitely adding the citeproc filter, turn off
@@ -847,60 +849,133 @@ function pdfEngine(options: PandocOptions): string {
847849 return pdfEngine ;
848850}
849851
852+ // Resolve any filters that are provided by an extension
850853async function resolveFilterExtension (
851854 options : PandocOptions ,
852855 filters : QuartoFilter [ ] ,
853- ) : Promise < QuartoFilter [ ] > {
854- // Resolve any filters that are provided by an extension
855- const results : ( QuartoFilter | QuartoFilter [ ] ) [ ] = [ ] ;
856- const getFilter = async ( filter : QuartoFilter ) => {
857- // Look for extension names in the filter list and result them
858- // into the filters provided by the extension
859- if (
860- filter !== kQuartoFilterMarker && filter !== kQuartoCiteProcMarker &&
861- typeof filter === "string"
862- ) {
863- // The filter string points to an executable file which exists
864- if ( existsSync ( filter ) && ! Deno . statSync ( filter ) . isDirectory ) {
865- return filter ;
856+ ) : Promise < QuartoFilterEntryPointQualified [ ] > {
857+ const results :
858+ ( QuartoFilterEntryPointQualified | QuartoFilterEntryPointQualified [ ] ) [ ] =
859+ [ ] ;
860+
861+ // Look for extension names in the filter list and result them
862+ // into the filters provided by the extension
863+ const getFilter = async (
864+ filter : QuartoFilter ,
865+ ) : Promise <
866+ QuartoFilterEntryPointQualified | QuartoFilterEntryPointQualified [ ]
867+ > => {
868+ if ( filter === kQuartoFilterMarker || filter === kQuartoCiteProcMarker ) {
869+ return { type : filter } ;
870+ }
871+ if ( typeof filter !== "string" ) {
872+ // deno-lint-ignore no-explicit-any
873+ if ( ( filter as any ) . at ) {
874+ const entryPoint = filter as QuartoFilterEntryPoint ;
875+ return {
876+ ...entryPoint ,
877+ path : {
878+ type : "relative" ,
879+ path : entryPoint . path ,
880+ } ,
881+ } ;
882+ } else {
883+ return {
884+ at : "__quarto-auto" ,
885+ type : filter . type ,
886+ path : {
887+ type : "relative" ,
888+ path : filter . path ,
889+ } ,
890+ } ;
866891 }
892+ }
867893
868- const extensions = await options . services . extension ?. find (
869- filter ,
870- options . source ,
871- "filters" ,
872- options . project ?. config ,
873- options . project ?. dir ,
874- ) || [ ] ;
875-
876- // Filter this list of extensions
877- const filteredExtensions = filterExtensions (
878- extensions || [ ] ,
879- filter ,
880- "filter" ,
894+ // The filter string points to an executable file which exists
895+ if ( existsSync ( filter ) && ! Deno . statSync ( filter ) . isDirectory ) {
896+ const type = extname ( filter ) !== "lua" ? "json" : "lua" ;
897+ return {
898+ at : "__quarto-auto" ,
899+ type,
900+ path : {
901+ type : "absolute" ,
902+ path : resolve ( filter ) ,
903+ } ,
904+ } ;
905+ }
906+
907+ const extensions = await options . services . extension ?. find (
908+ filter ,
909+ options . source ,
910+ "filters" ,
911+ options . project ?. config ,
912+ options . project ?. dir ,
913+ ) || [ ] ;
914+
915+ if ( extensions . length === 0 ) {
916+ // There were no extensions matching this name,
917+ // this should not happen
918+ //
919+ // Previously, we allowed it to pass, we're warning now and dropping
920+ warn (
921+ `No extensions matching name but filter (${ filter } ) is a string that isn't an existing path or quarto or citeproc. Ignoring` ,
881922 ) ;
882- // Return any contributed plugins
883- if ( filteredExtensions . length > 0 ) {
884- // This matches an extension, use the contributed filters
885- const filters = extensions [ 0 ] . contributes . filters ;
886- if ( filters ) {
887- return filters ;
888- } else {
889- return filter ;
890- }
891- } else if ( extensions . length > 0 ) {
892- // There was a matching extension with this name, but
893- // it was filtered out, just hide the filter altogether
894- return [ ] ;
895- } else {
896- // There were no extensions matching this name, just allow it
897- // through
898- return filter ;
899- }
900- } else {
901- return filter ;
923+ return [ ] ;
902924 }
925+
926+ // Filter this list of extensions
927+ const filteredExtensions = filterExtensions (
928+ extensions || [ ] ,
929+ filter ,
930+ "filter" ,
931+ ) ;
932+
933+ if ( filteredExtensions . length === 0 ) {
934+ // There was a matching extension with this name, but
935+ // it was filtered out, just hide the filter altogether
936+ return [ ] ;
937+ }
938+
939+ // Return any contributed plugins
940+ // This matches an extension, use the contributed filters
941+ const filters = extensions [ 0 ] . contributes . filters ;
942+ if ( ! filters ) {
943+ warn (
944+ `No extensions matching name but filter (${ filter } ) is a string that isn't an existing path or quarto or citeproc. Ignoring` ,
945+ ) ;
946+ return [ ] ;
947+ }
948+
949+ // our extension-finding service returns absolute paths
950+ // so any paths below will be "type": "absolute"
951+ // and need no conversion
952+
953+ return filters . map ( ( f ) => {
954+ if ( typeof f === "string" ) {
955+ const isExistingFile = existsSync ( f ) && ! Deno . statSync ( f ) . isDirectory ;
956+ const type = ( isExistingFile && extname ( f ) !== ".lua" ) ? "json" : "lua" ;
957+ return {
958+ at : "__quarto-auto" ,
959+ type,
960+ path : {
961+ type : "absolute" ,
962+ path : f ,
963+ } ,
964+ } ;
965+ }
966+
967+ return {
968+ ...f ,
969+ // deno-lint-ignore no-explicit-any
970+ at : ( f as any ) . at ?? "__quarto-auto" ,
971+ path : {
972+ type : "absolute" ,
973+ path : f . path ,
974+ } ,
975+ } ;
976+ } ) ;
903977 } ;
978+
904979 for ( const filter of filters ) {
905980 const r = await getFilter ( filter ) ;
906981 results . push ( r ) ;
0 commit comments