From ded5a31eb3ae8eaa66df84524a00dac6704be551 Mon Sep 17 00:00:00 2001 From: Sarah Zakarias Date: Wed, 23 Apr 2025 10:32:23 +0000 Subject: [PATCH 1/5] Make redirect pages for categories --- lib/src/generator/generator_backend.dart | 14 ++++++- lib/src/model/category.dart | 20 +++++---- test/end2end/model_test.dart | 5 +++ test/templates/category_test.dart | 53 ++++++++++++++---------- 4 files changed, 61 insertions(+), 31 deletions(-) diff --git a/lib/src/generator/generator_backend.dart b/lib/src/generator/generator_backend.dart index 43577ce99e..548e78d201 100644 --- a/lib/src/generator/generator_backend.dart +++ b/lib/src/generator/generator_backend.dart @@ -86,8 +86,7 @@ abstract class GeneratorBackend { ); } var e = data.self; - writer.write(filename, content, - element: e is Warnable ? e : null); + writer.write(filename, content, element: e is Warnable ? e : null); } /// Emits JSON describing the [categories] defined by the package. @@ -117,11 +116,22 @@ abstract class GeneratorBackend { writer.write(_pathContext.join('index.json'), '$json\n'); } + String _redirectContent(Category category) => ''' + + + + + '''; + /// Emits documentation content for the [category]. void generateCategory(PackageGraph packageGraph, Category category) { var data = CategoryTemplateData(options, packageGraph, category); var content = templates.renderCategory(data); write(writer, category.filePath, data, content); + if (category.filePath != category.legacyFilePath) { + writer.write(category.legacyFilePath, _redirectContent(category)); + } + runtimeStats.incrementAccumulator('writtenCategoryFileCount'); } diff --git a/lib/src/model/category.dart b/lib/src/model/category.dart index 5008b3ef0e..483c0e7238 100644 --- a/lib/src/model/category.dart +++ b/lib/src/model/category.dart @@ -27,7 +27,7 @@ class Category @override final Package package; - final String? _name; + final String? _internalName; @override final DartdocOptionContext config; @@ -62,10 +62,10 @@ class Category final CategoryDefinition _categoryDefinition; - Category(this._name, this.package, this.config) + Category(this._internalName, this.package, this.config) : _categoryDefinition = - config.categories.categoryDefinitions[_name.orDefault] ?? - CategoryDefinition(_name, null, null); + config.categories.categoryDefinitions[_internalName.orDefault] ?? + CategoryDefinition(_internalName, null, null); Iterable get externalItems => _categoryDefinition.externalItems; @@ -84,7 +84,7 @@ class Category String get name => _categoryDefinition.displayName; @override - String get sortKey => _name.orDefault; + String get sortKey => _internalName.orDefault; @override List get containerOrder => config.categoryOrder; @@ -105,11 +105,15 @@ class Category late final bool isDocumented = documentedWhere != DocumentLocation.missing && documentationFile != null; - String get filePath { - assert(_name != null); - return 'topics/$_name-topic.html'; + String get fileName { + assert(_internalName != null); + return '$_internalName-topic.html'; } + String get filePath => 'topics/$fileName'; + + String get legacyFilePath => 'topics/$name-topic.html'; + @override String? get href => isCanonical ? '${package.baseHref}$filePath' : null; diff --git a/test/end2end/model_test.dart b/test/end2end/model_test.dart index 331f2f9d56..cdfda7e82c 100644 --- a/test/end2end/model_test.dart +++ b/test/end2end/model_test.dart @@ -730,6 +730,11 @@ void main() async { }); }); + test('Verify legacyFilePath set', () { + var category = packageGraph.publicPackages.first.categories.first; + expect(category.legacyFilePath, 'topics/Superb-topic.html'); + }); + group('LibraryContainer', () { late final TestLibraryContainer topLevel; var sortOrderBasic = ['theFirst', 'second', 'fruit']; diff --git a/test/templates/category_test.dart b/test/templates/category_test.dart index 9b7255fd2b..29db4bb567 100644 --- a/test/templates/category_test.dart +++ b/test/templates/category_test.dart @@ -74,52 +74,53 @@ analyzer: dartdocOptions: ''' dartdoc: categories: - One: + cat1: markdown: one.md + displayName: One Documented: markdown: documented.md ''', libFiles: [ d.file('lib.dart', ''' /// A class. -/// {@category One} +/// {@category cat1} class C1 {} /// A constant. -/// {@category One} +/// {@category cat1} const c1 = 1; /// An enum. -/// {@category One} +/// {@category cat1} enum E1 { one, two } /// A function. -/// {@category One} +/// {@category cat1} void F1() {} /// A mixin. -/// {@category One} +/// {@category cat1} mixin M1 {} /// A property. -/// {@category One} +/// {@category cat1} var p1 = 1; /// A typedef. -/// {@category One} +/// {@category cat1} typedef T1 = void Function(); /// A typedef. -/// {@category One} +/// {@category cat1} // TODO(srawlins): Properly unit-test "typedef pointing to typedef". typedef T2 = T1; /// An extension. -/// {@category One} +/// {@category cat1} extension Ex on int {} /// An extension type. -/// {@category One} +/// {@category cat1} extension type ExType(int it) {} '''), d.file('other.dart', ''' @@ -136,7 +137,7 @@ library; await utils.writeDartdocResources(resourceProvider); await (await buildDartdoc()).generateDocs(); topicOneLines = resourceProvider - .getFile(path.join(packagePath, 'doc', 'topics', 'One-topic.html')) + .getFile(path.join(packagePath, 'doc', 'topics', 'cat1-topic.html')) .readAsStringSync() .split('\n'); indexPageLines = resourceProvider @@ -145,6 +146,14 @@ library; .split('\n'); }); + test('redirect category file created', () async { + final redirectContent = resourceProvider + .getFile(path.join(packagePath, 'doc', 'topics', 'One-topic.html')) + .readAsStringSync(); + + expect(redirectContent, contains('Classes'), + matches('Classes'), matches('C1'), ]), ); @@ -225,7 +234,7 @@ library; topicOneLines, containsAllInOrder([ matches('
Enums'), + matches('Enums'), matches('E1'), ]), ); @@ -236,7 +245,7 @@ library; topicOneLines, containsAllInOrder([ matches('
Mixins'), + matches('Mixins'), matches('M1'), ]), ); @@ -247,7 +256,7 @@ library; topicOneLines, containsAllInOrder([ matches('
Constants'), + matches('Constants'), matches('c1'), ]), ); @@ -258,7 +267,8 @@ library; topicOneLines, containsAllInOrder([ matches('
Properties'), + matches( + 'Properties'), matches('p1'), ]), ); @@ -269,7 +279,7 @@ library; topicOneLines, containsAllInOrder([ matches('
Functions'), + matches('Functions'), matches('F1'), ]), ); @@ -280,7 +290,7 @@ library; topicOneLines, containsAllInOrder([ matches('
Typedefs'), + matches('Typedefs'), matches('T1'), ]), ); @@ -291,7 +301,8 @@ library; topicOneLines, containsAllInOrder([ matches('
Extensions'), + matches( + 'Extensions'), matches('Ex'), ]), ); @@ -302,7 +313,7 @@ library; topicOneLines, containsAllInOrder([ matches('
' + matches('' 'Extension Types'), matches('ExType'), ]), From 8032bfdc06a3de7388c6c7769e487ffefe2c9943 Mon Sep 17 00:00:00 2001 From: Sarah Zakarias Date: Wed, 23 Apr 2025 20:38:25 +0000 Subject: [PATCH 2/5] use templates for redirect --- lib/src/generator/generator_backend.dart | 12 +-- .../templates.aot_renderers_for_html.dart | 34 ++++++++ lib/src/generator/templates.dart | 12 +++ .../templates.runtime_renderers.dart | 84 ++++++++++++++++++- lib/src/model/category.dart | 16 ++-- test/end2end/model_test.dart | 2 +- 6 files changed, 138 insertions(+), 22 deletions(-) diff --git a/lib/src/generator/generator_backend.dart b/lib/src/generator/generator_backend.dart index 548e78d201..ffc54781ce 100644 --- a/lib/src/generator/generator_backend.dart +++ b/lib/src/generator/generator_backend.dart @@ -116,20 +116,14 @@ abstract class GeneratorBackend { writer.write(_pathContext.join('index.json'), '$json\n'); } - String _redirectContent(Category category) => ''' - - - - - '''; - /// Emits documentation content for the [category]. void generateCategory(PackageGraph packageGraph, Category category) { var data = CategoryTemplateData(options, packageGraph, category); var content = templates.renderCategory(data); write(writer, category.filePath, data, content); - if (category.filePath != category.legacyFilePath) { - writer.write(category.legacyFilePath, _redirectContent(category)); + if (category.filePath != category.redirectFilePath) { + var redirectContent = templates.renderCategoryRedirect(data); + write(writer, category.redirectFilePath, data, redirectContent); } runtimeStats.incrementAccumulator('writtenCategoryFileCount'); diff --git a/lib/src/generator/templates.aot_renderers_for_html.dart b/lib/src/generator/templates.aot_renderers_for_html.dart index 4f19ebb48b..a7511204bc 100644 --- a/lib/src/generator/templates.aot_renderers_for_html.dart +++ b/lib/src/generator/templates.aot_renderers_for_html.dart @@ -234,6 +234,40 @@ String renderCategory(CategoryTemplateData context0) { return buffer.toString(); } +String renderCategoryRedirect(CategoryTemplateData context0) { + final buffer = StringBuffer(); + buffer.write(''' + + + + + + +

New URL

+ +'''); + + return buffer.toString(); +} + String renderClass(ClassTemplateData context0) { final buffer = StringBuffer(); buffer.write(_renderClass_partial_head_0(context0)); diff --git a/lib/src/generator/templates.dart b/lib/src/generator/templates.dart index 4fcd71becb..eb624456af 100644 --- a/lib/src/generator/templates.dart +++ b/lib/src/generator/templates.dart @@ -12,6 +12,9 @@ @Renderer(#renderCategory, Context(), 'category', visibleTypes: _visibleTypes) +@Renderer(#renderCategoryRedirect, Context(), + 'category_redirect', + visibleTypes: _visibleTypes) @Renderer(#renderClass, Context(), 'class') @Renderer(#renderConstructor, Context(), 'constructor') @Renderer(#renderEnum, Context(), 'enum') @@ -92,6 +95,7 @@ const _visibleTypes = { /// The collection of [Template] objects. abstract class Templates { String renderCategory(CategoryTemplateData context); + String renderCategoryRedirect(CategoryTemplateData context); String renderClass(ClassTemplateData context); String renderConstructor(ConstructorTemplateData context); String renderEnum(EnumTemplateData context); @@ -141,6 +145,10 @@ class HtmlAotTemplates implements Templates { String renderCategory(CategoryTemplateData context) => aot_renderers_for_html.renderCategory(context); + @override + String renderCategoryRedirect(CategoryTemplateData context) => + aot_renderers_for_html.renderCategoryRedirect(context); + @override String renderClass(ClassTemplateData context) => aot_renderers_for_html.renderClass(context); @@ -222,6 +230,10 @@ class RuntimeTemplates implements Templates { String renderCategory(CategoryTemplateData context) => runtime_renderers.renderCategory(context, _categoryTemplate); + @override + String renderCategoryRedirect(CategoryTemplateData context) => + runtime_renderers.renderCategoryRedirect(context, _categoryTemplate); + @override String renderClass(ClassTemplateData context) => runtime_renderers.renderClass(context, _classTemplate); diff --git a/lib/src/generator/templates.runtime_renderers.dart b/lib/src/generator/templates.runtime_renderers.dart index cc9d11902d..bc01161928 100644 --- a/lib/src/generator/templates.runtime_renderers.dart +++ b/lib/src/generator/templates.runtime_renderers.dart @@ -1846,6 +1846,38 @@ class _Renderer_Category extends RendererBase { ); }, ), + 'fileName': Property( + getValue: (CT_ c) => c.fileName, + renderVariable: ( + CT_ c, + Property self, + List remainingNames, + ) { + if (remainingNames.isEmpty) { + return self.getValue(c).toString(); + } + var name = remainingNames.first; + var nextProperty = _Renderer_String.propertyMap().getValue( + name, + ); + return nextProperty.renderVariable( + self.getValue(c) as String, + nextProperty, + [...remainingNames.skip(1)], + ); + }, + + isNullValue: (CT_ c) => false, + + renderValue: ( + CT_ c, + RendererBase r, + List ast, + StringSink sink, + ) { + _render_String(c.fileName, ast, r.template, sink, parent: r); + }, + ), 'filePath': Property( getValue: (CT_ c) => c.filePath, renderVariable: ( @@ -2155,6 +2187,44 @@ class _Renderer_Category extends RendererBase { ); }, ), + 'redirectFilePath': Property( + getValue: (CT_ c) => c.redirectFilePath, + renderVariable: ( + CT_ c, + Property self, + List remainingNames, + ) { + if (remainingNames.isEmpty) { + return self.getValue(c).toString(); + } + var name = remainingNames.first; + var nextProperty = _Renderer_String.propertyMap().getValue( + name, + ); + return nextProperty.renderVariable( + self.getValue(c) as String, + nextProperty, + [...remainingNames.skip(1)], + ); + }, + + isNullValue: (CT_ c) => false, + + renderValue: ( + CT_ c, + RendererBase r, + List ast, + StringSink sink, + ) { + _render_String( + c.redirectFilePath, + ast, + r.template, + sink, + parent: r, + ); + }, + ), 'referenceChildren': Property( getValue: (CT_ c) => c.referenceChildren, renderVariable: @@ -2285,7 +2355,7 @@ class _Renderer_Category extends RendererBase { } } -String renderCategory(CategoryTemplateData context, Template template) { +String renderCategoryRedirect(CategoryTemplateData context, Template template) { var buffer = StringBuffer(); _render_CategoryTemplateData(context, template.ast, template, buffer); return buffer.toString(); @@ -2543,6 +2613,12 @@ class _Renderer_CategoryTemplateData } } +String renderCategory(CategoryTemplateData context, Template template) { + var buffer = StringBuffer(); + _render_CategoryTemplateData(context, template.ast, template, buffer); + return buffer.toString(); +} + void _render_Class( Class context, List ast, @@ -19948,7 +20024,7 @@ class _Renderer_Package extends RendererBase { } } -String renderIndex(PackageTemplateData context, Template template) { +String renderSearchPage(PackageTemplateData context, Template template) { var buffer = StringBuffer(); _render_PackageTemplateData(context, template.ast, template, buffer); return buffer.toString(); @@ -20305,13 +20381,13 @@ class _Renderer_PackageTemplateData extends RendererBase { } } -String renderError(PackageTemplateData context, Template template) { +String renderIndex(PackageTemplateData context, Template template) { var buffer = StringBuffer(); _render_PackageTemplateData(context, template.ast, template, buffer); return buffer.toString(); } -String renderSearchPage(PackageTemplateData context, Template template) { +String renderError(PackageTemplateData context, Template template) { var buffer = StringBuffer(); _render_PackageTemplateData(context, template.ast, template, buffer); return buffer.toString(); diff --git a/lib/src/model/category.dart b/lib/src/model/category.dart index 483c0e7238..0d3764e7a9 100644 --- a/lib/src/model/category.dart +++ b/lib/src/model/category.dart @@ -27,7 +27,7 @@ class Category @override final Package package; - final String? _internalName; + final String? _name; @override final DartdocOptionContext config; @@ -62,10 +62,10 @@ class Category final CategoryDefinition _categoryDefinition; - Category(this._internalName, this.package, this.config) + Category(this._name, this.package, this.config) : _categoryDefinition = - config.categories.categoryDefinitions[_internalName.orDefault] ?? - CategoryDefinition(_internalName, null, null); + config.categories.categoryDefinitions[_name.orDefault] ?? + CategoryDefinition(_name, null, null); Iterable get externalItems => _categoryDefinition.externalItems; @@ -84,7 +84,7 @@ class Category String get name => _categoryDefinition.displayName; @override - String get sortKey => _internalName.orDefault; + String get sortKey => _name.orDefault; @override List get containerOrder => config.categoryOrder; @@ -106,13 +106,13 @@ class Category documentedWhere != DocumentLocation.missing && documentationFile != null; String get fileName { - assert(_internalName != null); - return '$_internalName-topic.html'; + assert(_name != null); + return '$_name-topic.html'; } String get filePath => 'topics/$fileName'; - String get legacyFilePath => 'topics/$name-topic.html'; + String get redirectFilePath => 'topics/$name-topic.html'; @override String? get href => isCanonical ? '${package.baseHref}$filePath' : null; diff --git a/test/end2end/model_test.dart b/test/end2end/model_test.dart index cdfda7e82c..4d79562380 100644 --- a/test/end2end/model_test.dart +++ b/test/end2end/model_test.dart @@ -732,7 +732,7 @@ void main() async { test('Verify legacyFilePath set', () { var category = packageGraph.publicPackages.first.categories.first; - expect(category.legacyFilePath, 'topics/Superb-topic.html'); + expect(category.redirectFilePath, 'topics/Superb-topic.html'); }); group('LibraryContainer', () { From 14813065438bc681c6aaa3dd8228a4dccdbeb5b3 Mon Sep 17 00:00:00 2001 From: Sarah Zakarias Date: Wed, 23 Apr 2025 20:59:44 +0000 Subject: [PATCH 3/5] update test description --- test/end2end/model_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/end2end/model_test.dart b/test/end2end/model_test.dart index 4d79562380..0d1170d860 100644 --- a/test/end2end/model_test.dart +++ b/test/end2end/model_test.dart @@ -730,7 +730,7 @@ void main() async { }); }); - test('Verify legacyFilePath set', () { + test('Verify redirectFilePath set', () { var category = packageGraph.publicPackages.first.categories.first; expect(category.redirectFilePath, 'topics/Superb-topic.html'); }); From bced1e3ecf266672cfd3b7e7ed2af29e3ee3b690 Mon Sep 17 00:00:00 2001 From: Sarah Zakarias Date: Thu, 24 Apr 2025 08:55:46 +0000 Subject: [PATCH 4/5] add file --- lib/templates/category_redirect.html | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 lib/templates/category_redirect.html diff --git a/lib/templates/category_redirect.html b/lib/templates/category_redirect.html new file mode 100644 index 0000000000..8f0c613233 --- /dev/null +++ b/lib/templates/category_redirect.html @@ -0,0 +1,10 @@ + + + + + + + +

New URL

+ + \ No newline at end of file From 2a9046e4866a517f4c2ddc39effa62c32e72be0a Mon Sep 17 00:00:00 2001 From: Sarah Zakarias Date: Fri, 25 Apr 2025 08:26:56 +0000 Subject: [PATCH 5/5] comment --- lib/src/generator/templates.aot_renderers_for_html.dart | 3 ++- lib/src/model/category.dart | 2 ++ lib/templates/category_redirect.html | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/src/generator/templates.aot_renderers_for_html.dart b/lib/src/generator/templates.aot_renderers_for_html.dart index a7511204bc..9333c1b73c 100644 --- a/lib/src/generator/templates.aot_renderers_for_html.dart +++ b/lib/src/generator/templates.aot_renderers_for_html.dart @@ -263,7 +263,8 @@ String renderCategoryRedirect(CategoryTemplateData context0) { buffer.writeEscaped(context0.self.href); buffer.write('''">New URL

-'''); + +'''); return buffer.toString(); } diff --git a/lib/src/model/category.dart b/lib/src/model/category.dart index 0d3764e7a9..225ff7532b 100644 --- a/lib/src/model/category.dart +++ b/lib/src/model/category.dart @@ -112,6 +112,8 @@ class Category String get filePath => 'topics/$fileName'; + /// Prior to dartdoc 8.3.4 the `displayName` was used in the file path + /// for category pages. We now create a redirect file here instead. String get redirectFilePath => 'topics/$name-topic.html'; @override diff --git a/lib/templates/category_redirect.html b/lib/templates/category_redirect.html index 8f0c613233..548f6676ea 100644 --- a/lib/templates/category_redirect.html +++ b/lib/templates/category_redirect.html @@ -7,4 +7,4 @@

New URL

- \ No newline at end of file +