<cfscript> // Define tag attributes and defaults. param name="attributes.filePath" type="string"; param name="attributes.dedent" type="string" default=true; param name="attributes.indentation" type="string" default="tab"; param name="attributes.trim" type="boolean" default=true; param name="attributes.append" type="boolean" default=false; param name="attributes.charset" type="string" default="utf-8"; // ------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------- // // Since this ColdFusion custom tag deals with generated output, we only care about // the tag in its "end" mode once we have generated content to consume. if ( thistag.executionMode == "start" ) { exit method = "exitTemplate" ; } // ------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------- // fileContent = thistag.generatedContent; if ( attributes.dedent ) { fileContent = dedentContent( fileContent, attributes.indentation ); } if ( attributes.trim ) { fileContent = trim( fileContent ); } targetFile = ( attributes.append ) ? fileOpen( attributes.filePath, "append", attributes.charset ) : fileOpen( attributes.filePath, "write", attributes.charset ) ; try { fileWrite( targetFile, fileContent, attributes.charset ) } finally { fileClose( targetFile ); } // We don't want this tag to generate content - all content was written to the file. thistag.generatedContent = ""; // ------------------------------------------------------------------------------- // // ------------------------------------------------------------------------------- // /** * I remove indentation from the given input but leave the input such that the relative * indentation across lines remains constant. */ private string function dedentContent( required string input, required string indentation ) { var indentationCharacter = ( indentation == "tab" ) ? chr( 9 ) // Tab. : chr( 32 ) // Space. ; // In order to figure out how much we can dedent the text, we must first locate // the smallest amount of indentation that currently exists across all lines of // text. However, we only care about lines of text that have non-indentation // characters on them (ie, we want to skip over empty lines). As such, we're going // to create a pattern that must end on non-indentation (or line-break) character. var minWidth = input .reMatch( "(?m)^#indentationCharacter#*[^#indentationCharacter#\r\n]" ) .map( ( linePrefix ) => { return ( linePrefix.len() - 1 ); } ) // NOTE: ArrayMin() returns zero if array is empty. .min() ; if ( ! minWidth ) { return input; } // Now that we've found the smallest amount of indentation across all lines of // text, we can remove exactly that amount of indentation from each line of text. var result = input .reReplace( "(?m)^#indentationCharacter#{#minWidth#}", "", "all" ) ; return result; } </cfscript>