Skip to content

Move generator options into generator.dart #2100

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/dartdoc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'dart:convert';
import 'dart:io';

import 'package:dartdoc/src/dartdoc_options.dart';
import 'package:dartdoc/src/empty_generator.dart';
import 'package:dartdoc/src/generator.dart';
import 'package:dartdoc/src/html/html_generator.dart';
import 'package:dartdoc/src/logging.dart';
Expand All @@ -36,8 +37,7 @@ const String programName = 'dartdoc';
// Update when pubspec version changes by running `pub run build_runner build`
const String dartdocVersion = packageVersion;

/// Helper class to initialize the default generators since they require
/// GeneratorContext.
/// Helper class that consolidates option contexts for instantiating generators.
class DartdocGeneratorOptionContext extends DartdocOptionContext
with GeneratorContext {
DartdocGeneratorOptionContext(DartdocOptionSet optionSet, Directory dir)
Expand All @@ -64,7 +64,7 @@ class Dartdoc extends PackageBuilder {
/// and returns a Dartdoc object with them.
static Future<Dartdoc> withDefaultGenerators(
DartdocGeneratorOptionContext config) async {
List<Generator> generators = await initGenerators(config);
List<Generator> generators = await initHtmlGenerators(config);
return Dartdoc._(config, generators);
}

Expand Down
13 changes: 0 additions & 13 deletions lib/src/dartdoc_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1421,8 +1421,6 @@ class DartdocOptionContext extends DartdocOptionContextBase
bool isPackageExcluded(String name) =>
excludePackages.any((pattern) => name == pattern);

String get templatesDir => optionSet['templatesDir'].valueAt(context);

// TODO(jdkoren): temporary while we confirm href base behavior doesn't break important clients
bool get useBaseHref => optionSet['useBaseHref'].valueAt(context);
}
Expand Down Expand Up @@ -1627,17 +1625,6 @@ Future<List<DartdocOption>> createDartdocOptions() async {
'exist. Executables for different platforms are specified by '
'giving the platform name as a key, and a list of strings as the '
'command.'),
DartdocOptionArgOnly<String>("templatesDir", null,
isDir: true,
mustExist: true,
hide: true,
help:
'Path to a directory containing templates to use instead of the default ones. '
'Directory must contain an html file for each of the following: 404error, category, '
'class, constant, constructor, enum, function, index, library, method, mixin, '
'property, top_level_constant, top_level_property, typedef. Partial templates are '
'supported; they must begin with an underscore, and references to them must omit the '
'leading underscore (e.g. use {{>foo}} to reference the partial template _foo.html).'),
DartdocOptionArgOnly<bool>('useBaseHref', false,
help:
'Use <base href> in generated files (legacy behavior). This option '
Expand Down
5 changes: 5 additions & 0 deletions lib/src/empty_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ library dartdoc.empty_generator;

import 'dart:async';

import 'package:dartdoc/src/dartdoc_options.dart';
import 'package:dartdoc/src/generator.dart';
import 'package:dartdoc/src/model/model.dart';
import 'package:dartdoc/src/model_utils.dart';
Expand Down Expand Up @@ -39,3 +40,7 @@ class EmptyGenerator extends Generator {
@override
final Map<String, Warnable> writtenFiles = {};
}

Future<List<Generator>> initEmptyGenerators(DartdocOptionContext config) async {
return [EmptyGenerator()];
}
112 changes: 112 additions & 0 deletions lib/src/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
library dartdoc.generator;

import 'dart:async' show Stream, Future;
import 'dart:io' show Directory;
import 'dart:isolate';

import 'package:dartdoc/src/dartdoc_options.dart';
import 'package:dartdoc/src/model/model.dart' show PackageGraph;
import 'package:dartdoc/src/package_meta.dart';
import 'package:dartdoc/src/warnings.dart';
import 'package:path/path.dart' as path;

/// An abstract class that defines a generator that generates documentation for
/// a given package.
Expand All @@ -25,3 +30,110 @@ abstract class Generator {
/// Fetches all filenames written by this generator.
Map<String, Warnable> get writtenFiles;
}

/// Dartdoc options related to generators generally.
mixin GeneratorContext on DartdocOptionContextBase {
List<String> get footer => optionSet['footer'].valueAt(context);

/// _footerText is only used to construct synthetic options.
// ignore: unused_element
List<String> get _footerText => optionSet['footerText'].valueAt(context);

List<String> get footerTextPaths =>
optionSet['footerTextPaths'].valueAt(context);

List<String> 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);
}

Uri _sdkFooterCopyrightUri;

Future<void> _setSdkFooterCopyrightUri() async {
if (_sdkFooterCopyrightUri == null) {
// TODO(jdkoren): find a way to make this not specific to HTML, or have
// alternatives for other supported formats.
_sdkFooterCopyrightUri = await Isolate.resolvePackageUri(
Uri.parse('package:dartdoc/resources/sdk_footer_text.html'));
}
}

Future<List<DartdocOption>> createGeneratorOptions() async {
await _setSdkFooterCopyrightUri();
return <DartdocOption>[
DartdocOptionArgFile<List<String>>('footer', [],
isFile: true,
help:
'Paths to files with content to add to page footers, but possibly '
'outside of dedicated footer elements for the generator (e.g. '
'outside of <footer> for an HTML generator). To add text content '
'to dedicated footer elements, use --footer-text instead.',
mustExist: true,
splitCommas: true),
DartdocOptionArgFile<List<String>>('footerText', [],
isFile: true,
help: 'Paths to files with content to add to page footers (next to the '
'package name and version).',
mustExist: true,
splitCommas: true),
DartdocOptionSyntheticOnly<List<String>>(
'footerTextPaths',
(DartdocSyntheticOption<List<String>> option, Directory dir) {
final List<String> footerTextPaths = <String>[];
final PackageMeta topLevelPackageMeta =
option.root['topLevelPackageMeta'].valueAt(dir);
// TODO(jcollins-g): Eliminate special casing for SDK and use config file.
if (topLevelPackageMeta.isSdk == true) {
footerTextPaths
.add(path.canonicalize(_sdkFooterCopyrightUri.toFilePath()));
}
footerTextPaths.addAll(option.parent['footerText'].valueAt(dir));
return footerTextPaths;
},
isFile: true,
help: 'paths to footer-text-files (adding special case for SDK)',
mustExist: true,
),
DartdocOptionArgFile<List<String>>('header', [],
isFile: true,
help: 'Paths to files with content to add to page headers.',
splitCommas: true),
DartdocOptionArgOnly<bool>('prettyIndexJson', false,
help:
'Generates `index.json` with indentation and newlines. The file is '
'larger, but it\'s also easier to diff.',
negatable: false),
DartdocOptionArgFile<String>('favicon', null,
isFile: true,
help: 'A path to a favicon for the generated docs.',
mustExist: true),
DartdocOptionArgOnly<String>('relCanonicalPrefix', null,
help:
'If provided, add a rel="canonical" prefixed with provided value. '
'Consider using if building many versions of the docs for public '
'SEO; learn more at https://goo.gl/gktN6F.'),
DartdocOptionArgOnly<String>("templatesDir", null,
isDir: true,
mustExist: true,
hide: true,
help:
'Path to a directory with templates to use instead of the default '
'ones. Directory must contain a file for each of the following: '
'404error, category, class, constant, constructor, enum, function, '
'index, library, method, mixin, property, top_level_constant, '
'top_level_property, typedef. Partial templates are supported; '
'they must begin with an underscore, and references to them must '
'omit the leading underscore (e.g. use {{>foo}} to reference the '
'partial template _foo.html).'),
];
}
108 changes: 10 additions & 98 deletions lib/src/html/html_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ class HtmlGenerator extends Generator {
}

class HtmlGeneratorOptions implements HtmlOptions {
final String url;
final String faviconPath;
final bool prettyIndexJson;
final String templatesDir;
Expand All @@ -146,8 +145,7 @@ class HtmlGeneratorOptions implements HtmlOptions {
final bool useBaseHref;

HtmlGeneratorOptions(
{this.url,
this.relCanonicalPrefix,
{this.relCanonicalPrefix,
this.faviconPath,
String toolVersion,
this.prettyIndexJson = false,
Expand All @@ -156,109 +154,23 @@ class HtmlGeneratorOptions implements HtmlOptions {
: this.toolVersion = toolVersion ?? 'unknown';
}

Future<List<Generator>> initEmptyGenerators(DartdocOptionContext config) async {
return [EmptyGenerator()];
}

/// Initialize and setup the generators.
Future<List<Generator>> initGenerators(GeneratorContext config) async {
Future<List<Generator>> initHtmlGenerators(GeneratorContext context) async {
// TODO(jcollins-g): Rationalize based on GeneratorContext all the way down
// through the generators.
HtmlGeneratorOptions options = HtmlGeneratorOptions(
url: config.hostedUrl,
relCanonicalPrefix: config.relCanonicalPrefix,
relCanonicalPrefix: context.relCanonicalPrefix,
toolVersion: dartdocVersion,
faviconPath: config.favicon,
prettyIndexJson: config.prettyIndexJson,
templatesDir: config.templatesDir,
useBaseHref: config.useBaseHref);

faviconPath: context.favicon,
prettyIndexJson: context.prettyIndexJson,
templatesDir: context.templatesDir,
useBaseHref: context.useBaseHref);
return [
await HtmlGenerator.create(
options: options,
headers: config.header,
footers: config.footer,
footerTexts: config.footerTextPaths,
headers: context.header,
footers: context.footer,
footerTexts: context.footerTextPaths,
)
];
}

Uri _sdkFooterCopyrightUri;
Future<void> _setSdkFooterCopyrightUri() async {
if (_sdkFooterCopyrightUri == null) {
_sdkFooterCopyrightUri = await Isolate.resolvePackageUri(
Uri.parse('package:dartdoc/resources/sdk_footer_text.html'));
}
}

abstract class GeneratorContext implements DartdocOptionContext {
String get favicon => optionSet['favicon'].valueAt(context);
List<String> get footer => optionSet['footer'].valueAt(context);

/// _footerText is only used to construct synthetic options.
// ignore: unused_element
List<String> get _footerText => optionSet['footerText'].valueAt(context);
List<String> get footerTextPaths =>
optionSet['footerTextPaths'].valueAt(context);
List<String> get header => optionSet['header'].valueAt(context);
String get hostedUrl => optionSet['hostedUrl'].valueAt(context);
bool get prettyIndexJson => optionSet['prettyIndexJson'].valueAt(context);
String get relCanonicalPrefix =>
optionSet['relCanonicalPrefix'].valueAt(context);
}

Future<List<DartdocOption>> createGeneratorOptions() async {
await _setSdkFooterCopyrightUri();
return <DartdocOption>[
DartdocOptionArgFile<String>('favicon', null,
isFile: true,
help: 'A path to a favicon for the generated docs.',
mustExist: true),
DartdocOptionArgFile<List<String>>('footer', [],
isFile: true,
help: 'paths to footer files containing HTML text.',
mustExist: true,
splitCommas: true),
DartdocOptionArgFile<List<String>>('footerText', [],
isFile: true,
help:
'paths to footer-text files (optional text next to the package name '
'and version).',
mustExist: true,
splitCommas: true),
DartdocOptionSyntheticOnly<List<String>>(
'footerTextPaths',
(DartdocSyntheticOption<List<String>> option, Directory dir) {
final List<String> footerTextPaths = <String>[];
final PackageMeta topLevelPackageMeta =
option.root['topLevelPackageMeta'].valueAt(dir);
// TODO(jcollins-g): Eliminate special casing for SDK and use config file.
if (topLevelPackageMeta.isSdk == true) {
footerTextPaths
.add(path.canonicalize(_sdkFooterCopyrightUri.toFilePath()));
}
footerTextPaths.addAll(option.parent['footerText'].valueAt(dir));
return footerTextPaths;
},
isFile: true,
help: 'paths to footer-text-files (adding special case for SDK)',
mustExist: true,
),
DartdocOptionArgFile<List<String>>('header', [],
isFile: true,
help: 'paths to header files containing HTML text.',
splitCommas: true),
DartdocOptionArgOnly<String>('hostedUrl', null,
help:
'URL where the docs will be hosted (used to generate the sitemap).'),
DartdocOptionArgOnly<bool>('prettyIndexJson', false,
help:
"Generates `index.json` with indentation and newlines. The file is larger, but it's also easier to diff.",
negatable: false),
DartdocOptionArgOnly<String>('relCanonicalPrefix', null,
help:
'If provided, add a rel="canonical" prefixed with provided value. '
'Consider using if\nbuilding many versions of the docs for public '
'SEO; learn more at https://goo.gl/gktN6F.'),
];
}