From 9aac7b72ed327d975265153b9247340975ff9bbd Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Tue, 2 Mar 2021 14:14:45 -0800 Subject: [PATCH 1/4] Refactor header and footer support to use mustache templating. * Remove `mixin GeneratorContext` and move contained code into DartdocGeneratorOptionContext, the only class which mixed in this mixin. * **Breaking change**: Move DartdocGeneratorOptionContext from `lib/dartdoc.dart` to `lib/options.dart`, which is more directly related to the class, and is the only library in which this class is constructed or extended. * Add `header`, `footer`, and `footerText` getters to DartdocGeneratorOptionContext. * Add `customHeaderContent`, `customFooterContent`, and `customInnerFooterText` to DartdocGneratorBackendOptions. * **Breaking change**: Privatize the DartdocGeneratorBackendOptions into a new private constructor which takes no parameters. * Change all header/footer/footer-text placeholder comments in templates to instead be mustache interpolations. --- lib/dartdoc.dart | 9 +-- lib/options.dart | 46 ++++++++++++ .../generator/dartdoc_generator_backend.dart | 35 ++++++--- lib/src/generator/generator.dart | 21 ------ lib/src/generator/html_generator.dart | 4 +- lib/src/generator/markdown_generator.dart | 2 +- lib/src/generator/template_data.dart | 9 +++ lib/src/generator/templates.dart | 71 +++---------------- lib/templates/html/_footer.html | 4 +- lib/templates/html/_head.html | 2 +- lib/templates/md/_footer.md | 4 +- lib/templates/md/_head.md | 2 +- test/end2end/dartdoc_test.dart | 1 + .../templates/_footer.html | 4 +- .../templates/_head.html | 2 +- 15 files changed, 105 insertions(+), 111 deletions(-) diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart index dce9194c64..a38579ba61 100644 --- a/lib/dartdoc.dart +++ b/lib/dartdoc.dart @@ -13,6 +13,7 @@ import 'dart:convert'; import 'dart:io' show exitCode, stderr; import 'package:analyzer/file_system/file_system.dart'; +import 'package:dartdoc/options.dart'; import 'package:dartdoc/src/dartdoc_options.dart'; import 'package:dartdoc/src/generator/empty_generator.dart'; import 'package:dartdoc/src/generator/generator.dart'; @@ -40,14 +41,6 @@ const String programName = 'dartdoc'; // Update when pubspec version changes by running `pub run build_runner build` const String dartdocVersion = packageVersion; -/// Helper class that consolidates option contexts for instantiating generators. -class DartdocGeneratorOptionContext extends DartdocOptionContext - with GeneratorContext { - DartdocGeneratorOptionContext( - DartdocOptionSet optionSet, Folder dir, ResourceProvider resourceProvider) - : super(optionSet, dir, resourceProvider); -} - class DartdocFileWriter implements FileWriter { final String outputDir; @override diff --git a/lib/options.dart b/lib/options.dart index 76948087fc..968215b83d 100644 --- a/lib/options.dart +++ b/lib/options.dart @@ -5,6 +5,52 @@ import 'package:args/args.dart'; import 'package:dartdoc/dartdoc.dart'; import 'package:dartdoc/src/logging.dart'; +/// Helper class that consolidates option contexts for instantiating generators. +class DartdocGeneratorOptionContext extends DartdocOptionContext { + DartdocGeneratorOptionContext( + DartdocOptionSet optionSet, Folder dir, ResourceProvider resourceProvider) + : super(optionSet, dir, resourceProvider); + + // TODO(migration): Make late final with initializer when Null Safe. + String _header; + + /// Returns the joined contents of any 'header' files specified in options. + String get header => + _header ??= _joinCustomTextFiles(optionSet['header'].valueAt(context)); + + // TODO(migration): Make late final with initializer when Null Safe. + String _footer; + + /// Returns the joined contents of any 'footer' files specified in options. + String get footer => + _footer ??= _joinCustomTextFiles(optionSet['footer'].valueAt(context)); + + // TODO(migration): Make late final with initializer when Null Safe. + String _footerText; + + /// Returns the joined contents of any 'footer-text' files specified in + /// options. + String get footerText => _footerText ??= + _joinCustomTextFiles(optionSet['footerText'].valueAt(context)); + + String _joinCustomTextFiles(Iterable paths) => paths + .map((p) => resourceProvider.getFile(p).readAsStringSync()) + .join('\n'); + + bool get prettyIndexJson => optionSet['prettyIndexJson'].valueAt(context); + + String get favicon => optionSet['favicon'].valueAt(context); + + String get relCanonicalPrefix => + optionSet['relCanonicalPrefix'].valueAt(context); + + String get templatesDir => optionSet['templatesDir'].valueAt(context); + + // TODO(jdkoren): duplicated temporarily so that GeneratorContext is enough for configuration. + @override + bool get useBaseHref => optionSet['useBaseHref'].valueAt(context); +} + class DartdocProgramOptionContext extends DartdocGeneratorOptionContext with LoggingContext { DartdocProgramOptionContext( diff --git a/lib/src/generator/dartdoc_generator_backend.dart b/lib/src/generator/dartdoc_generator_backend.dart index 1f8c45c3f1..bd03ba7073 100644 --- a/lib/src/generator/dartdoc_generator_backend.dart +++ b/lib/src/generator/dartdoc_generator_backend.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:dartdoc/dartdoc.dart'; +import 'package:dartdoc/options.dart'; import 'package:dartdoc/src/generator/generator_frontend.dart'; import 'package:dartdoc/src/generator/generator_utils.dart' as generator_util; import 'package:dartdoc/src/generator/template_data.dart'; @@ -18,6 +19,7 @@ import 'package:path/path.dart' as path show Context; class DartdocGeneratorBackendOptions implements TemplateOptions { @override final String relCanonicalPrefix; + @override final String toolVersion; @@ -28,20 +30,35 @@ class DartdocGeneratorBackendOptions implements TemplateOptions { @override final bool useBaseHref; + @override + final String customHeaderContent; + + @override + final String customFooterContent; + + @override + final String customInnerFooterText; + DartdocGeneratorBackendOptions.fromContext( DartdocGeneratorOptionContext context) : relCanonicalPrefix = context.relCanonicalPrefix, toolVersion = dartdocVersion, favicon = context.favicon, prettyIndexJson = context.prettyIndexJson, - useBaseHref = context.useBaseHref; - - DartdocGeneratorBackendOptions( - {this.relCanonicalPrefix, - this.toolVersion, - this.favicon, - this.prettyIndexJson = false, - this.useBaseHref = false}); + useBaseHref = context.useBaseHref, + customHeaderContent = context.header, + customFooterContent = context.footer, + customInnerFooterText = context.footerText; + + DartdocGeneratorBackendOptions._defaults() + : relCanonicalPrefix = null, + toolVersion = null, + favicon = null, + prettyIndexJson = false, + useBaseHref = false, + customHeaderContent = '', + customFooterContent = '', + customInnerFooterText = ''; } class SidebarGenerator { @@ -67,7 +84,7 @@ abstract class DartdocGeneratorBackend implements GeneratorBackend { DartdocGeneratorBackend( DartdocGeneratorBackendOptions options, this.templates, this._pathContext) - : options = options ?? DartdocGeneratorBackendOptions(), + : options = options ?? DartdocGeneratorBackendOptions._defaults(), sidebarForContainer = SidebarGenerator(templates.sidebarContainerTemplate), sidebarForLibrary = SidebarGenerator(templates.sidebarLibraryTemplate); diff --git a/lib/src/generator/generator.dart b/lib/src/generator/generator.dart index 79258ece49..c0096fc5d7 100644 --- a/lib/src/generator/generator.dart +++ b/lib/src/generator/generator.dart @@ -32,27 +32,6 @@ abstract class Generator { Future generate(PackageGraph packageGraph, FileWriter writer); } -/// Dartdoc options related to generators generally. -mixin GeneratorContext on DartdocOptionContextBase { - List get footer => optionSet['footer'].valueAt(context); - - List get footerText => optionSet['footerText'].valueAt(context); - - List get header => optionSet['header'].valueAt(context); - - bool get prettyIndexJson => optionSet['prettyIndexJson'].valueAt(context); - - String get favicon => optionSet['favicon'].valueAt(context); - - String get relCanonicalPrefix => - optionSet['relCanonicalPrefix'].valueAt(context); - - String get templatesDir => optionSet['templatesDir'].valueAt(context); - - // TODO(jdkoren): duplicated temporarily so that GeneratorContext is enough for configuration. - bool get useBaseHref => optionSet['useBaseHref'].valueAt(context); -} - Future>> createGeneratorOptions( PackageMetaProvider packageMetaProvider) async { var resourceProvider = packageMetaProvider.resourceProvider; diff --git a/lib/src/generator/html_generator.dart b/lib/src/generator/html_generator.dart index 5a7cb2c176..c8a00152d8 100644 --- a/lib/src/generator/html_generator.dart +++ b/lib/src/generator/html_generator.dart @@ -4,7 +4,7 @@ library dartdoc.html_generator; -import 'package:dartdoc/dartdoc.dart'; +import 'package:dartdoc/options.dart'; import 'package:dartdoc/src/generator/dartdoc_generator_backend.dart'; import 'package:dartdoc/src/generator/generator.dart'; import 'package:dartdoc/src/generator/generator_frontend.dart'; @@ -12,6 +12,8 @@ import 'package:dartdoc/src/generator/html_resources.g.dart' as resources; import 'package:dartdoc/src/generator/resource_loader.dart'; import 'package:dartdoc/src/generator/template_data.dart'; import 'package:dartdoc/src/generator/templates.dart'; +import 'package:dartdoc/src/model/package.dart'; +import 'package:dartdoc/src/model/package_graph.dart'; import 'package:path/path.dart' as path show Context; Future initHtmlGenerator( diff --git a/lib/src/generator/markdown_generator.dart b/lib/src/generator/markdown_generator.dart index b4770d0ba1..9554dc0199 100644 --- a/lib/src/generator/markdown_generator.dart +++ b/lib/src/generator/markdown_generator.dart @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:dartdoc/dartdoc.dart'; +import 'package:dartdoc/options.dart'; import 'package:dartdoc/src/generator/dartdoc_generator_backend.dart'; import 'package:dartdoc/src/generator/generator.dart'; import 'package:dartdoc/src/generator/generator_frontend.dart'; diff --git a/lib/src/generator/template_data.dart b/lib/src/generator/template_data.dart index eaafc55710..8e76e94f1e 100644 --- a/lib/src/generator/template_data.dart +++ b/lib/src/generator/template_data.dart @@ -12,6 +12,9 @@ abstract class TemplateOptions { String get relCanonicalPrefix; String get toolVersion; bool get useBaseHref; + String get customHeaderContent; + String get customFooterContent; + String get customInnerFooterText; } abstract class TemplateData { @@ -61,6 +64,12 @@ abstract class TemplateData { String _layoutTitle(String name, String kind, bool isDeprecated) => _packageGraph.rendererFactory.templateRenderer .composeLayoutTitle(name, kind, isDeprecated); + + String get customHeader => htmlOptions.customHeaderContent; + + String get customFooter => htmlOptions.customFooterContent; + + String get customInnerFooter => htmlOptions.customInnerFooterText; } /// A [TemplateData] which contains a library, for rendering the diff --git a/lib/src/generator/templates.dart b/lib/src/generator/templates.dart index 1fdcd0102b..1c90f8a4af 100644 --- a/lib/src/generator/templates.dart +++ b/lib/src/generator/templates.dart @@ -9,6 +9,7 @@ library dartdoc.templates; import 'package:analyzer/file_system/file_system.dart'; import 'package:dartdoc/dartdoc.dart'; +import 'package:dartdoc/options.dart'; import 'package:dartdoc/src/generator/resource_loader.dart'; import 'package:dartdoc/src/generator/template_data.dart'; import 'package:dartdoc/src/mustachio/annotations.dart'; @@ -67,36 +68,6 @@ const _partials_md = [ 'source_link', ]; -const String _headerPlaceholder = '{{! header placeholder }}'; -const String _footerPlaceholder = '{{! footer placeholder }}'; -const String _footerTextPlaceholder = '{{! footer-text placeholder }}'; - -Future> _loadPartials( - _TemplatesLoader templatesLoader, - List headerPaths, - List footerPaths, - List footerTextPaths) async { - var partials = await templatesLoader.loadPartials(); - - void replacePlaceholder(String key, String placeholder, List paths) { - var template = partials[key]; - if (template != null && paths != null && paths.isNotEmpty) { - var replacement = paths - .map((p) => - templatesLoader.resourceProvider.getFile(p).readAsStringSync()) - .join('\n'); - template = template.replaceAll(placeholder, replacement); - partials[key] = template; - } - } - - replacePlaceholder('head', _headerPlaceholder, headerPaths); - replacePlaceholder('footer', _footerPlaceholder, footerPaths); - replacePlaceholder('footer', _footerTextPlaceholder, footerTextPaths); - - return partials; -} - abstract class _TemplatesLoader { ResourceProvider get resourceProvider; @@ -207,53 +178,29 @@ class Templates { DartdocGeneratorOptionContext context) async { var templatesDir = context.templatesDir; var format = context.format; - var footerTextPaths = context.footerText; if (templatesDir != null) { return _fromDirectory( context.resourceProvider.getFolder(templatesDir), format, - resourceProvider: context.resourceProvider, - headerPaths: context.header, - footerPaths: context.footer, - footerTextPaths: footerTextPaths); + resourceProvider: context.resourceProvider); } else { - return createDefault(format, - resourceProvider: context.resourceProvider, - headerPaths: context.header, - footerPaths: context.footer, - footerTextPaths: footerTextPaths); + return createDefault(format, resourceProvider: context.resourceProvider); } } @visibleForTesting static Future createDefault(String format, - {@required ResourceProvider resourceProvider, - List headerPaths = const [], - List footerPaths = const [], - List footerTextPaths = const []}) async { - return _create(_DefaultTemplatesLoader.create(format, resourceProvider), - headerPaths: headerPaths, - footerPaths: footerPaths, - footerTextPaths: footerTextPaths); + {@required ResourceProvider resourceProvider}) async { + return _create(_DefaultTemplatesLoader.create(format, resourceProvider)); } static Future _fromDirectory(Folder dir, String format, - {@required ResourceProvider resourceProvider, - @required List headerPaths, - @required List footerPaths, - @required List footerTextPaths}) async { - return _create(_DirectoryTemplatesLoader(dir, format, resourceProvider), - headerPaths: headerPaths, - footerPaths: footerPaths, - footerTextPaths: footerTextPaths); + {@required ResourceProvider resourceProvider}) async { + return _create(_DirectoryTemplatesLoader(dir, format, resourceProvider)); } - static Future _create(_TemplatesLoader templatesLoader, - {@required List headerPaths, - @required List footerPaths, - @required List footerTextPaths}) async { - var partials = await _loadPartials( - templatesLoader, headerPaths, footerPaths, footerTextPaths); + static Future _create(_TemplatesLoader templatesLoader) async { + var partials = await templatesLoader.loadPartials(); Template _partial(String name) { var partial = partials[name]; diff --git a/lib/templates/html/_footer.html b/lib/templates/html/_footer.html index b8e12094d2..1afd06eb52 100644 --- a/lib/templates/html/_footer.html +++ b/lib/templates/html/_footer.html @@ -8,14 +8,14 @@ {{/hasFooterVersion}} - {{! footer-text placeholder }} + {{ customInnerFooter }} {{! TODO(jdkoren): unwrap ^useBaseHref sections when the option is removed.}} -{{! footer placeholder }} +{{ customFooter }} diff --git a/lib/templates/html/_head.html b/lib/templates/html/_head.html index 2b5c513f8a..26d10ba9a3 100644 --- a/lib/templates/html/_head.html +++ b/lib/templates/html/_head.html @@ -28,7 +28,7 @@ - {{! header placeholder }} + {{ customHeader }} {{! We don't use , but we do lookup the htmlBase from javascript. }} diff --git a/lib/templates/md/_footer.md b/lib/templates/md/_footer.md index 3fdd12cb58..4675b396f1 100644 --- a/lib/templates/md/_footer.md +++ b/lib/templates/md/_footer.md @@ -1,3 +1,3 @@ {{! markdown has no dedicated footer element, so both placeholders are siblings }} -{{! footer-text placeholder }} -{{! footer placeholder }} +{{ customInnerFooter }} +{{ customFooter }} diff --git a/lib/templates/md/_head.md b/lib/templates/md/_head.md index c51fcf05ab..dba624f46b 100644 --- a/lib/templates/md/_head.md +++ b/lib/templates/md/_head.md @@ -1 +1 @@ -{{! header placeholder }} +{{ customHeader }} diff --git a/test/end2end/dartdoc_test.dart b/test/end2end/dartdoc_test.dart index 6a129c961f..1e008cdb82 100644 --- a/test/end2end/dartdoc_test.dart +++ b/test/end2end/dartdoc_test.dart @@ -9,6 +9,7 @@ import 'dart:io' show Platform; import 'package:analyzer/file_system/file_system.dart'; import 'package:dartdoc/dartdoc.dart'; +import 'package:dartdoc/options.dart'; import 'package:dartdoc/src/io_utils.dart'; import 'package:dartdoc/src/logging.dart'; import 'package:dartdoc/src/model/model.dart'; diff --git a/testing/test_package_custom_templates/templates/_footer.html b/testing/test_package_custom_templates/templates/_footer.html index b8e12094d2..1afd06eb52 100644 --- a/testing/test_package_custom_templates/templates/_footer.html +++ b/testing/test_package_custom_templates/templates/_footer.html @@ -8,14 +8,14 @@ {{/hasFooterVersion}} - {{! footer-text placeholder }} + {{ customInnerFooter }} {{! TODO(jdkoren): unwrap ^useBaseHref sections when the option is removed.}} -{{! footer placeholder }} +{{ customFooter }} diff --git a/testing/test_package_custom_templates/templates/_head.html b/testing/test_package_custom_templates/templates/_head.html index 2b5c513f8a..26d10ba9a3 100644 --- a/testing/test_package_custom_templates/templates/_head.html +++ b/testing/test_package_custom_templates/templates/_head.html @@ -28,7 +28,7 @@ - {{! header placeholder }} + {{ customHeader }} {{! We don't use , but we do lookup the htmlBase from javascript. }} From 7440c01d35c4b868762117b8b030441b34b1b118 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Tue, 2 Mar 2021 14:40:14 -0800 Subject: [PATCH 2/4] Do not escape HTML in headers or footers --- analysis_options.yaml | 1 + lib/templates/html/_footer.html | 4 ++-- lib/templates/html/_head.html | 2 +- lib/templates/md/_footer.md | 4 ++-- lib/templates/md/_head.md | 2 +- test/end2end/dartdoc_test.dart | 2 +- testing/test_package_custom_templates/templates/_footer.html | 4 ++-- testing/test_package_custom_templates/templates/_head.html | 2 +- testing/test_package_options/extras/footer-things.html | 2 +- 9 files changed, 12 insertions(+), 11 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 89f4150259..7d9e507144 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -6,6 +6,7 @@ analyzer: errors: unused_import: warning unused_shown_name: warning + todo: ignore exclude: - 'doc/**' - 'lib/src/third_party/pkg/**' diff --git a/lib/templates/html/_footer.html b/lib/templates/html/_footer.html index 1afd06eb52..906ebeb633 100644 --- a/lib/templates/html/_footer.html +++ b/lib/templates/html/_footer.html @@ -8,14 +8,14 @@ {{/hasFooterVersion}} - {{ customInnerFooter }} + {{{ customInnerFooter }}} {{! TODO(jdkoren): unwrap ^useBaseHref sections when the option is removed.}} -{{ customFooter }} +{{{ customFooter }}} diff --git a/lib/templates/html/_head.html b/lib/templates/html/_head.html index 26d10ba9a3..17039906b7 100644 --- a/lib/templates/html/_head.html +++ b/lib/templates/html/_head.html @@ -28,7 +28,7 @@ - {{ customHeader }} + {{{ customHeader }}} {{! We don't use , but we do lookup the htmlBase from javascript. }} diff --git a/lib/templates/md/_footer.md b/lib/templates/md/_footer.md index 4675b396f1..74002bf837 100644 --- a/lib/templates/md/_footer.md +++ b/lib/templates/md/_footer.md @@ -1,3 +1,3 @@ {{! markdown has no dedicated footer element, so both placeholders are siblings }} -{{ customInnerFooter }} -{{ customFooter }} +{{{ customInnerFooter }}} +{{{ customFooter }}} diff --git a/lib/templates/md/_head.md b/lib/templates/md/_head.md index dba624f46b..8eebb628dd 100644 --- a/lib/templates/md/_head.md +++ b/lib/templates/md/_head.md @@ -1 +1 @@ -{{ customHeader }} +{{{ customHeader }}} diff --git a/test/end2end/dartdoc_test.dart b/test/end2end/dartdoc_test.dart index 1e008cdb82..28480e7348 100644 --- a/test/end2end/dartdoc_test.dart +++ b/test/end2end/dartdoc_test.dart @@ -117,7 +117,7 @@ void main() { expect(favicon.readAsStringSync(), contains('Not really a png, but a test file')); var indexString = index.readAsStringSync(); - expect(indexString, contains('Footer things')); + expect(indexString, contains('Footer things')); expect(indexString, contains('footer.txt data')); expect(indexString, contains('HTML header file')); }); diff --git a/testing/test_package_custom_templates/templates/_footer.html b/testing/test_package_custom_templates/templates/_footer.html index 1afd06eb52..906ebeb633 100644 --- a/testing/test_package_custom_templates/templates/_footer.html +++ b/testing/test_package_custom_templates/templates/_footer.html @@ -8,14 +8,14 @@ {{/hasFooterVersion}} - {{ customInnerFooter }} + {{{ customInnerFooter }}} {{! TODO(jdkoren): unwrap ^useBaseHref sections when the option is removed.}} -{{ customFooter }} +{{{ customFooter }}} diff --git a/testing/test_package_custom_templates/templates/_head.html b/testing/test_package_custom_templates/templates/_head.html index 26d10ba9a3..17039906b7 100644 --- a/testing/test_package_custom_templates/templates/_head.html +++ b/testing/test_package_custom_templates/templates/_head.html @@ -28,7 +28,7 @@ - {{ customHeader }} + {{{ customHeader }}} {{! We don't use , but we do lookup the htmlBase from javascript. }} diff --git a/testing/test_package_options/extras/footer-things.html b/testing/test_package_options/extras/footer-things.html index e593de747d..7ada24808a 100644 --- a/testing/test_package_options/extras/footer-things.html +++ b/testing/test_package_options/extras/footer-things.html @@ -1,2 +1,2 @@ -Footer things. +Footer things. From de8f8e19a3af5e7fab5bb71a28501fe7ed8eb347 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Tue, 2 Mar 2021 14:40:30 -0800 Subject: [PATCH 3/4] undo --- analysis_options.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 7d9e507144..89f4150259 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -6,7 +6,6 @@ analyzer: errors: unused_import: warning unused_shown_name: warning - todo: ignore exclude: - 'doc/**' - 'lib/src/third_party/pkg/**' From 2dd5b4bd58f270f27f23a95ca39bebfeda3486da Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Tue, 2 Mar 2021 15:20:17 -0800 Subject: [PATCH 4/4] regenerate --- lib/src/generator/templates.renderers.dart | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/lib/src/generator/templates.renderers.dart b/lib/src/generator/templates.renderers.dart index b4e471a0cb..2a6e603395 100644 --- a/lib/src/generator/templates.renderers.dart +++ b/lib/src/generator/templates.renderers.dart @@ -1283,6 +1283,42 @@ class _Renderer_TemplateData return renderSimple(c.bareHref, ast, r.template, parent: r); }, ), + 'customFooter': Property( + getValue: (CT_ c) => c.customFooter, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable(c, remainingNames, 'String'), + isNullValue: (CT_ c) => c.customFooter == null, + renderValue: + (CT_ c, RendererBase r, List ast) { + return renderSimple(c.customFooter, ast, r.template, + parent: r); + }, + ), + 'customHeader': Property( + getValue: (CT_ c) => c.customHeader, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable(c, remainingNames, 'String'), + isNullValue: (CT_ c) => c.customHeader == null, + renderValue: + (CT_ c, RendererBase r, List ast) { + return renderSimple(c.customHeader, ast, r.template, + parent: r); + }, + ), + 'customInnerFooter': Property( + getValue: (CT_ c) => c.customInnerFooter, + renderVariable: (CT_ c, Property self, + List remainingNames) => + self.renderSimpleVariable(c, remainingNames, 'String'), + isNullValue: (CT_ c) => c.customInnerFooter == null, + renderValue: + (CT_ c, RendererBase r, List ast) { + return renderSimple(c.customInnerFooter, ast, r.template, + parent: r); + }, + ), 'defaultPackage': Property( getValue: (CT_ c) => c.defaultPackage, renderVariable: