Yes, there is, but it is not easy and may not be worth your problem.
One way to do this is to create your own BuildProvider and replace the default System.Web.Compilation.PageBuildProvider with it in the configuration file:
<compilation debug="true"> <buildProviders> <add extension=".aspx" type="MyProject.MyPageBuildProvider" /> </buildProviders> </compilation>
You would also create your own PageParser, most likely inherited from TemplateParser. BuildProvider is responsible for the supply of PageParser. In the most primitive situation, you can overwrite the ParseFile method, read the ASPX file, process it, create a copy, and pass it to the base method.
Unfortunately, all ASPX analysis code is sealed and embedded in MS libraries, so you cannot inherit. Rewriting this would mean creating the entire compilation mechanism.
An alternative method is to create your own page builder and put it in an attribute. The disadvantage is that you get easy access to literals (all your spaces, etc.) Only at the first level (page). To get to the internal controls and their literals, you need to either crack the parser using reflection, or (correctly) manipulate the dom code. This way you get properly built .cs files and temporary builds.
Here is a simplified example:
namespace MyProject { [FileLevelControlBuilder(typeof(MyPageBuilder))] public partial class _Default : System.Web.UI.Page { //this is Default.aspx } //The builder of the page public class MyPageBuilder : FileLevelPageControlBuilder { //This is where you'd strip white space, but only of top level, //such as between the head and form, or form and the end of file public override void AppendLiteralString(string text) { //let replace some white spaces with garbage base.AppendLiteralString(text.Replace(" ", "#").Replace("\t", "@").Replace("\r", "$").Replace("\n", "%")); } //Here you can manipulate the entire generated code using CodeDom public override void ProcessGeneratedCode(System.CodeDom.CodeCompileUnit codeCompileUnit, System.CodeDom.CodeTypeDeclaration baseType, System.CodeDom.CodeTypeDeclaration derivedType, System.CodeDom.CodeMemberMethod buildMethod, System.CodeDom.CodeMemberMethod dataBindingMethod) { base.ProcessGeneratedCode(codeCompileUnit, baseType, derivedType, buildMethod, dataBindingMethod); } //Alternatively, you can "hack" the PageParser here using reflection //However, the _text field at this point is irrelevant, so it can't be used public override void Init(TemplateParser parser, ControlBuilder parentBuilder, Type type, string tagName, string ID, System.Collections.IDictionary attribs) { FieldInfo fi = parser.GetType().BaseType.BaseType.BaseType.GetField("_text", System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); string s = (string) fi.GetValue(parser); fi.SetValue(parser, s.Replace("\t", "*")); base.Init(parser, parentBuilder, type, tagName, ID, attribs); } } }
In my opinion, this is not worth the effort.
edited typo