Skip to content
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
3 changes: 2 additions & 1 deletion lib/dartdoc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ class DartdocGeneratorOptionContext extends DartdocOptionContext

class DartdocFileWriter implements FileWriter {
final String outputDir;
ResourceProvider resourceProvider;
@override
final ResourceProvider resourceProvider;
final Map<String, Warnable> _fileElementMap = {};
@override
final Set<String> writtenFiles = {};
Expand Down
3 changes: 3 additions & 0 deletions lib/src/generator/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
/// A library containing an abstract documentation generator.
library dartdoc.generator;

import 'package:analyzer/file_system/file_system.dart';
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';

abstract class FileWriter {
ResourceProvider get resourceProvider;

/// All filenames written by this generator.
Set<String> get writtenFiles;

Expand Down
26 changes: 16 additions & 10 deletions lib/src/generator/html_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ import 'package:dartdoc/src/generator/dartdoc_generator_backend.dart';
import 'package:dartdoc/src/generator/generator.dart';
import 'package:dartdoc/src/generator/generator_frontend.dart';
import 'package:dartdoc/src/generator/html_resources.g.dart' as resources;
import 'package:dartdoc/src/generator/resource_loader.dart' as resource_loader;
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:path/path.dart' as path;

Future<Generator> initHtmlGenerator(
DartdocGeneratorOptionContext context) async {
Expand Down Expand Up @@ -43,21 +42,28 @@ class HtmlGeneratorBackend extends DartdocGeneratorBackend {
// Allow overwrite of favicon.
var bytes =
graph.resourceProvider.getFile(options.favicon).readAsBytesSync();
writer.write(path.join('static-assets', 'favicon.png'), bytes,
writer.write(
graph.resourceProvider.pathContext
.join('static-assets', 'favicon.png'),
bytes,
allowOverwrite: true);
}
}

Future<void> _copyResources(FileWriter writer) async {
final prefix = 'package:dartdoc/resources/';
var resourceLoader = ResourceLoader(writer.resourceProvider);
for (var resourcePath in resources.resource_names) {
if (!resourcePath.startsWith(prefix)) {
throw StateError('Resource paths must start with $prefix, '
'encountered $resourcePath');
if (!resourcePath.startsWith(_dartdocResourcePrefix)) {
throw StateError('Resource paths must start with '
'$_dartdocResourcePrefix, encountered $resourcePath');
}
var destFileName = resourcePath.substring(prefix.length);
writer.write(path.join('static-assets', destFileName),
await resource_loader.loadAsBytes(resourcePath));
var destFileName = resourcePath.substring(_dartdocResourcePrefix.length);
var destFilePath = writer.resourceProvider.pathContext
.join('static-assets', destFileName);
writer.write(
destFilePath, await resourceLoader.loadAsBytes(resourcePath));
}
}

static const _dartdocResourcePrefix = 'package:dartdoc/resources/';
}
54 changes: 31 additions & 23 deletions lib/src/generator/resource_loader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,43 @@
library dartdoc.resource_loader;

import 'dart:convert' show utf8;
import 'dart:io' show File;
import 'dart:isolate' show Isolate;
import 'package:analyzer/file_system/file_system.dart';
import 'package:meta/meta.dart';

/// Loads a `package:` resource as a String.
Future<String> loadAsString(String path) async {
var bytes = await loadAsBytes(path);
class ResourceLoader {
final ResourceProvider provider;

return utf8.decode(bytes);
}
ResourceLoader(this.provider);

/// Loads a `package:` resource as a String.
Future<String> loadAsString(String path) async {
var bytes = await loadAsBytes(path);

/// Loads a `package:` resource as an [List<int>].
Future<List<int>> loadAsBytes(String path) async {
if (!path.startsWith('package:')) {
throw ArgumentError('path must begin with package:');
return utf8.decode(bytes);
}

var uri = await _resolveUri(Uri.parse(path));
return File.fromUri(uri).readAsBytes();
}
/// Loads a `package:` resource as an [List<int>].
Future<List<int>> loadAsBytes(String path) async {
if (!path.startsWith('package:')) {
throw ArgumentError('path must begin with package:');
}

var uri = await resolveUri(Uri.parse(path));
return provider.getFile(uri.toFilePath()).readAsBytesSync();
}

/// Helper function for resolving to a non-relative, non-package URI.
Future<Uri> _resolveUri(Uri uri) {
if (uri.scheme == 'package') {
return Isolate.resolvePackageUri(uri).then((resolvedUri) {
if (resolvedUri == null) {
throw ArgumentError.value(uri, 'uri', 'Unknown package');
}
return resolvedUri;
});
/// Helper function for resolving to a non-relative, non-package URI.
@visibleForTesting
Future<Uri> resolveUri(Uri uri) {
if (uri.scheme == 'package') {
return Isolate.resolvePackageUri(uri).then((resolvedUri) {
if (resolvedUri == null) {
throw ArgumentError.value(uri, 'uri', 'Unknown package');
}
return resolvedUri;
});
}
return Future<Uri>.value(Uri.base.resolveUri(uri));
}
return Future<Uri>.value(Uri.base.resolveUri(uri));
}
85 changes: 50 additions & 35 deletions lib/src/generator/templates.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
@Renderer(#renderIndex, Context<PackageTemplateData>())
library dartdoc.templates;

import 'dart:io' show File, Directory;

import 'package:analyzer/file_system/file_system.dart';
import 'package:dartdoc/dartdoc.dart';
import 'package:dartdoc/src/generator/resource_loader.dart' as loader;
import 'package:dartdoc/src/generator/resource_loader.dart';
import 'package:dartdoc/src/generator/template_data.dart';
import 'package:dartdoc/src/mustachio/annotations.dart';
import 'package:meta/meta.dart';
import 'package:mustache/mustache.dart';
import 'package:path/path.dart' as path;

Expand Down Expand Up @@ -58,7 +58,6 @@ const _partials_md = <String>[
'features',
'feature_set',
'footer',
'footer',
'head',
'library',
'mixin',
Expand All @@ -77,16 +76,15 @@ Future<Map<String, String>> _loadPartials(
List<String> headerPaths,
List<String> footerPaths,
List<String> footerTextPaths) async {
headerPaths ??= [];
footerPaths ??= [];
footerTextPaths ??= [];

var partials = await templatesLoader.loadPartials();

void replacePlaceholder(String key, String placeholder, List<String> paths) {
var template = partials[key];
if (template != null && paths != null && paths.isNotEmpty) {
var replacement = paths.map((p) => File(p).readAsStringSync()).join('\n');
var replacement = paths
.map((p) =>
templatesLoader.loader.provider.getFile(p).readAsStringSync())
.join('\n');
template = template.replaceAll(placeholder, replacement);
partials[key] = template;
}
Expand All @@ -100,6 +98,8 @@ Future<Map<String, String>> _loadPartials(
}

abstract class _TemplatesLoader {
ResourceLoader get loader;

Future<Map<String, String>> loadPartials();

Future<String> loadTemplate(String name);
Expand All @@ -110,7 +110,10 @@ class _DefaultTemplatesLoader extends _TemplatesLoader {
final String _format;
final List<String> _partials;

factory _DefaultTemplatesLoader.create(String format) {
@override
final ResourceLoader loader;

factory _DefaultTemplatesLoader.create(String format, ResourceLoader loader) {
List<String> partials;
switch (format) {
case 'html':
Expand All @@ -122,10 +125,10 @@ class _DefaultTemplatesLoader extends _TemplatesLoader {
default:
partials = [];
}
return _DefaultTemplatesLoader(format, partials);
return _DefaultTemplatesLoader(format, partials, loader);
}

_DefaultTemplatesLoader(this._format, this._partials);
_DefaultTemplatesLoader(this._format, this._partials, this.loader);

@override
Future<Map<String, String>> loadPartials() async {
Expand All @@ -144,33 +147,38 @@ class _DefaultTemplatesLoader extends _TemplatesLoader {

/// Loads templates from a specified Directory.
class _DirectoryTemplatesLoader extends _TemplatesLoader {
final Directory _directory;
final Folder _directory;
final String _format;

_DirectoryTemplatesLoader(this._directory, this._format);
@override
final ResourceLoader loader;

_DirectoryTemplatesLoader(this._directory, this._format, this.loader);

path.Context get pathContext => _directory.provider.pathContext;

@override
Future<Map<String, String>> loadPartials() async {
var partials = <String, String>{};

for (var file in _directory.listSync().whereType<File>()) {
var basename = path.basename(file.path);
for (var file in _directory.getChildren().whereType<File>()) {
var basename = pathContext.basename(file.path);
if (basename.startsWith('_') && basename.endsWith('.$_format')) {
var content = file.readAsString();
var content = file.readAsStringSync();
var partialName = basename.substring(1, basename.lastIndexOf('.'));
partials[partialName] = await content;
partials[partialName] = content;
}
}
return partials;
}

@override
Future<String> loadTemplate(String name) {
var file = File(path.join(_directory.path, '$name.$_format'));
if (!file.existsSync()) {
Future<String> loadTemplate(String name) async {
var file = _directory.getChildAssumingFile('$name.$_format');
if (!file.exists) {
throw DartdocFailure('Missing required template file: $name.$_format');
}
return file.readAsString();
return file.readAsStringSync();
}
}

Expand All @@ -197,44 +205,51 @@ class Templates {
var templatesDir = context.templatesDir;
var format = context.format;
var footerTextPaths = context.footerText;
var resourceLoader = ResourceLoader(context.resourceProvider);

if (templatesDir != null) {
return fromDirectory(Directory(templatesDir), format,
return _fromDirectory(
context.resourceProvider.getFolder(templatesDir), format,
loader: resourceLoader,
headerPaths: context.header,
footerPaths: context.footer,
footerTextPaths: footerTextPaths);
} else {
return createDefault(format,
loader: resourceLoader,
headerPaths: context.header,
footerPaths: context.footer,
footerTextPaths: footerTextPaths);
}
}

@visibleForTesting
static Future<Templates> createDefault(String format,
{List<String> headerPaths,
List<String> footerPaths,
List<String> footerTextPaths}) async {
return _create(_DefaultTemplatesLoader.create(format),
{@required ResourceLoader loader,
List<String> headerPaths = const <String>[],
List<String> footerPaths = const <String>[],
List<String> footerTextPaths = const <String>[]}) async {
return _create(_DefaultTemplatesLoader.create(format, loader),
headerPaths: headerPaths,
footerPaths: footerPaths,
footerTextPaths: footerTextPaths);
}

static Future<Templates> fromDirectory(Directory dir, String format,
{List<String> headerPaths,
List<String> footerPaths,
List<String> footerTextPaths}) async {
return _create(_DirectoryTemplatesLoader(dir, format),
static Future<Templates> _fromDirectory(Folder dir, String format,
{@required ResourceLoader loader,
@required List<String> headerPaths,
@required List<String> footerPaths,
@required List<String> footerTextPaths}) async {
return _create(_DirectoryTemplatesLoader(dir, format, loader),
headerPaths: headerPaths,
footerPaths: footerPaths,
footerTextPaths: footerTextPaths);
}

static Future<Templates> _create(_TemplatesLoader templatesLoader,
{List<String> headerPaths,
List<String> footerPaths,
List<String> footerTextPaths}) async {
{@required List<String> headerPaths,
@required List<String> footerPaths,
@required List<String> footerTextPaths}) async {
var partials = await _loadPartials(
templatesLoader, headerPaths, footerPaths, footerTextPaths);

Expand Down
Loading