Skip to content

Commit 48f5259

Browse files
authored
Move templates.dart to use ResourceProvider. (#2481)
Move templates.dart to use ResourceProvider. * **Breaking Change**: Templates.fromDirectory is made private and now requires a ResourceLoader. * Templates.createDefault is made `@visibleForTesting` and now requires a ResourceLoader. This includes a few minor cleanups as well.
1 parent 9e61a4f commit 48f5259

File tree

7 files changed

+179
-71
lines changed

7 files changed

+179
-71
lines changed

lib/dartdoc.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ class DartdocGeneratorOptionContext extends DartdocOptionContext
5050

5151
class DartdocFileWriter implements FileWriter {
5252
final String outputDir;
53-
ResourceProvider resourceProvider;
53+
@override
54+
final ResourceProvider resourceProvider;
5455
final Map<String, Warnable> _fileElementMap = {};
5556
@override
5657
final Set<String> writtenFiles = {};

lib/src/generator/generator.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
/// A library containing an abstract documentation generator.
66
library dartdoc.generator;
77

8+
import 'package:analyzer/file_system/file_system.dart';
89
import 'package:dartdoc/src/dartdoc_options.dart';
910
import 'package:dartdoc/src/model/model.dart' show PackageGraph;
1011
import 'package:dartdoc/src/package_meta.dart';
1112
import 'package:dartdoc/src/warnings.dart';
1213

1314
abstract class FileWriter {
15+
ResourceProvider get resourceProvider;
16+
1417
/// All filenames written by this generator.
1518
Set<String> get writtenFiles;
1619

lib/src/generator/html_generator.dart

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ import 'package:dartdoc/src/generator/dartdoc_generator_backend.dart';
99
import 'package:dartdoc/src/generator/generator.dart';
1010
import 'package:dartdoc/src/generator/generator_frontend.dart';
1111
import 'package:dartdoc/src/generator/html_resources.g.dart' as resources;
12-
import 'package:dartdoc/src/generator/resource_loader.dart' as resource_loader;
12+
import 'package:dartdoc/src/generator/resource_loader.dart';
1313
import 'package:dartdoc/src/generator/template_data.dart';
1414
import 'package:dartdoc/src/generator/templates.dart';
15-
import 'package:path/path.dart' as path;
1615

1716
Future<Generator> initHtmlGenerator(
1817
DartdocGeneratorOptionContext context) async {
@@ -43,21 +42,28 @@ class HtmlGeneratorBackend extends DartdocGeneratorBackend {
4342
// Allow overwrite of favicon.
4443
var bytes =
4544
graph.resourceProvider.getFile(options.favicon).readAsBytesSync();
46-
writer.write(path.join('static-assets', 'favicon.png'), bytes,
45+
writer.write(
46+
graph.resourceProvider.pathContext
47+
.join('static-assets', 'favicon.png'),
48+
bytes,
4749
allowOverwrite: true);
4850
}
4951
}
5052

5153
Future<void> _copyResources(FileWriter writer) async {
52-
final prefix = 'package:dartdoc/resources/';
54+
var resourceLoader = ResourceLoader(writer.resourceProvider);
5355
for (var resourcePath in resources.resource_names) {
54-
if (!resourcePath.startsWith(prefix)) {
55-
throw StateError('Resource paths must start with $prefix, '
56-
'encountered $resourcePath');
56+
if (!resourcePath.startsWith(_dartdocResourcePrefix)) {
57+
throw StateError('Resource paths must start with '
58+
'$_dartdocResourcePrefix, encountered $resourcePath');
5759
}
58-
var destFileName = resourcePath.substring(prefix.length);
59-
writer.write(path.join('static-assets', destFileName),
60-
await resource_loader.loadAsBytes(resourcePath));
60+
var destFileName = resourcePath.substring(_dartdocResourcePrefix.length);
61+
var destFilePath = writer.resourceProvider.pathContext
62+
.join('static-assets', destFileName);
63+
writer.write(
64+
destFilePath, await resourceLoader.loadAsBytes(resourcePath));
6165
}
6266
}
67+
68+
static const _dartdocResourcePrefix = 'package:dartdoc/resources/';
6369
}

lib/src/generator/resource_loader.dart

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,43 @@
66
library dartdoc.resource_loader;
77

88
import 'dart:convert' show utf8;
9-
import 'dart:io' show File;
109
import 'dart:isolate' show Isolate;
10+
import 'package:analyzer/file_system/file_system.dart';
11+
import 'package:meta/meta.dart';
1112

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

16-
return utf8.decode(bytes);
17-
}
16+
ResourceLoader(this.provider);
17+
18+
/// Loads a `package:` resource as a String.
19+
Future<String> loadAsString(String path) async {
20+
var bytes = await loadAsBytes(path);
1821

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

25-
var uri = await _resolveUri(Uri.parse(path));
26-
return File.fromUri(uri).readAsBytes();
27-
}
25+
/// Loads a `package:` resource as an [List<int>].
26+
Future<List<int>> loadAsBytes(String path) async {
27+
if (!path.startsWith('package:')) {
28+
throw ArgumentError('path must begin with package:');
29+
}
30+
31+
var uri = await resolveUri(Uri.parse(path));
32+
return provider.getFile(uri.toFilePath()).readAsBytesSync();
33+
}
2834

29-
/// Helper function for resolving to a non-relative, non-package URI.
30-
Future<Uri> _resolveUri(Uri uri) {
31-
if (uri.scheme == 'package') {
32-
return Isolate.resolvePackageUri(uri).then((resolvedUri) {
33-
if (resolvedUri == null) {
34-
throw ArgumentError.value(uri, 'uri', 'Unknown package');
35-
}
36-
return resolvedUri;
37-
});
35+
/// Helper function for resolving to a non-relative, non-package URI.
36+
@visibleForTesting
37+
Future<Uri> resolveUri(Uri uri) {
38+
if (uri.scheme == 'package') {
39+
return Isolate.resolvePackageUri(uri).then((resolvedUri) {
40+
if (resolvedUri == null) {
41+
throw ArgumentError.value(uri, 'uri', 'Unknown package');
42+
}
43+
return resolvedUri;
44+
});
45+
}
46+
return Future<Uri>.value(Uri.base.resolveUri(uri));
3847
}
39-
return Future<Uri>.value(Uri.base.resolveUri(uri));
4048
}

lib/src/generator/templates.dart

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
@Renderer(#renderIndex, Context<PackageTemplateData>())
88
library dartdoc.templates;
99

10-
import 'dart:io' show File, Directory;
11-
10+
import 'package:analyzer/file_system/file_system.dart';
1211
import 'package:dartdoc/dartdoc.dart';
13-
import 'package:dartdoc/src/generator/resource_loader.dart' as loader;
12+
import 'package:dartdoc/src/generator/resource_loader.dart';
1413
import 'package:dartdoc/src/generator/template_data.dart';
1514
import 'package:dartdoc/src/mustachio/annotations.dart';
15+
import 'package:meta/meta.dart';
1616
import 'package:mustache/mustache.dart';
1717
import 'package:path/path.dart' as path;
1818

@@ -58,7 +58,6 @@ const _partials_md = <String>[
5858
'features',
5959
'feature_set',
6060
'footer',
61-
'footer',
6261
'head',
6362
'library',
6463
'mixin',
@@ -77,16 +76,15 @@ Future<Map<String, String>> _loadPartials(
7776
List<String> headerPaths,
7877
List<String> footerPaths,
7978
List<String> footerTextPaths) async {
80-
headerPaths ??= [];
81-
footerPaths ??= [];
82-
footerTextPaths ??= [];
83-
8479
var partials = await templatesLoader.loadPartials();
8580

8681
void replacePlaceholder(String key, String placeholder, List<String> paths) {
8782
var template = partials[key];
8883
if (template != null && paths != null && paths.isNotEmpty) {
89-
var replacement = paths.map((p) => File(p).readAsStringSync()).join('\n');
84+
var replacement = paths
85+
.map((p) =>
86+
templatesLoader.loader.provider.getFile(p).readAsStringSync())
87+
.join('\n');
9088
template = template.replaceAll(placeholder, replacement);
9189
partials[key] = template;
9290
}
@@ -100,6 +98,8 @@ Future<Map<String, String>> _loadPartials(
10098
}
10199

102100
abstract class _TemplatesLoader {
101+
ResourceLoader get loader;
102+
103103
Future<Map<String, String>> loadPartials();
104104

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

113-
factory _DefaultTemplatesLoader.create(String format) {
113+
@override
114+
final ResourceLoader loader;
115+
116+
factory _DefaultTemplatesLoader.create(String format, ResourceLoader loader) {
114117
List<String> partials;
115118
switch (format) {
116119
case 'html':
@@ -122,10 +125,10 @@ class _DefaultTemplatesLoader extends _TemplatesLoader {
122125
default:
123126
partials = [];
124127
}
125-
return _DefaultTemplatesLoader(format, partials);
128+
return _DefaultTemplatesLoader(format, partials, loader);
126129
}
127130

128-
_DefaultTemplatesLoader(this._format, this._partials);
131+
_DefaultTemplatesLoader(this._format, this._partials, this.loader);
129132

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

145148
/// Loads templates from a specified Directory.
146149
class _DirectoryTemplatesLoader extends _TemplatesLoader {
147-
final Directory _directory;
150+
final Folder _directory;
148151
final String _format;
149152

150-
_DirectoryTemplatesLoader(this._directory, this._format);
153+
@override
154+
final ResourceLoader loader;
155+
156+
_DirectoryTemplatesLoader(this._directory, this._format, this.loader);
157+
158+
path.Context get pathContext => _directory.provider.pathContext;
151159

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

156-
for (var file in _directory.listSync().whereType<File>()) {
157-
var basename = path.basename(file.path);
164+
for (var file in _directory.getChildren().whereType<File>()) {
165+
var basename = pathContext.basename(file.path);
158166
if (basename.startsWith('_') && basename.endsWith('.$_format')) {
159-
var content = file.readAsString();
167+
var content = file.readAsStringSync();
160168
var partialName = basename.substring(1, basename.lastIndexOf('.'));
161-
partials[partialName] = await content;
169+
partials[partialName] = content;
162170
}
163171
}
164172
return partials;
165173
}
166174

167175
@override
168-
Future<String> loadTemplate(String name) {
169-
var file = File(path.join(_directory.path, '$name.$_format'));
170-
if (!file.existsSync()) {
176+
Future<String> loadTemplate(String name) async {
177+
var file = _directory.getChildAssumingFile('$name.$_format');
178+
if (!file.exists) {
171179
throw DartdocFailure('Missing required template file: $name.$_format');
172180
}
173-
return file.readAsString();
181+
return file.readAsStringSync();
174182
}
175183
}
176184

@@ -197,44 +205,51 @@ class Templates {
197205
var templatesDir = context.templatesDir;
198206
var format = context.format;
199207
var footerTextPaths = context.footerText;
208+
var resourceLoader = ResourceLoader(context.resourceProvider);
200209

201210
if (templatesDir != null) {
202-
return fromDirectory(Directory(templatesDir), format,
211+
return _fromDirectory(
212+
context.resourceProvider.getFolder(templatesDir), format,
213+
loader: resourceLoader,
203214
headerPaths: context.header,
204215
footerPaths: context.footer,
205216
footerTextPaths: footerTextPaths);
206217
} else {
207218
return createDefault(format,
219+
loader: resourceLoader,
208220
headerPaths: context.header,
209221
footerPaths: context.footer,
210222
footerTextPaths: footerTextPaths);
211223
}
212224
}
213225

226+
@visibleForTesting
214227
static Future<Templates> createDefault(String format,
215-
{List<String> headerPaths,
216-
List<String> footerPaths,
217-
List<String> footerTextPaths}) async {
218-
return _create(_DefaultTemplatesLoader.create(format),
228+
{@required ResourceLoader loader,
229+
List<String> headerPaths = const <String>[],
230+
List<String> footerPaths = const <String>[],
231+
List<String> footerTextPaths = const <String>[]}) async {
232+
return _create(_DefaultTemplatesLoader.create(format, loader),
219233
headerPaths: headerPaths,
220234
footerPaths: footerPaths,
221235
footerTextPaths: footerTextPaths);
222236
}
223237

224-
static Future<Templates> fromDirectory(Directory dir, String format,
225-
{List<String> headerPaths,
226-
List<String> footerPaths,
227-
List<String> footerTextPaths}) async {
228-
return _create(_DirectoryTemplatesLoader(dir, format),
238+
static Future<Templates> _fromDirectory(Folder dir, String format,
239+
{@required ResourceLoader loader,
240+
@required List<String> headerPaths,
241+
@required List<String> footerPaths,
242+
@required List<String> footerTextPaths}) async {
243+
return _create(_DirectoryTemplatesLoader(dir, format, loader),
229244
headerPaths: headerPaths,
230245
footerPaths: footerPaths,
231246
footerTextPaths: footerTextPaths);
232247
}
233248

234249
static Future<Templates> _create(_TemplatesLoader templatesLoader,
235-
{List<String> headerPaths,
236-
List<String> footerPaths,
237-
List<String> footerTextPaths}) async {
250+
{@required List<String> headerPaths,
251+
@required List<String> footerPaths,
252+
@required List<String> footerTextPaths}) async {
238253
var partials = await _loadPartials(
239254
templatesLoader, headerPaths, footerPaths, footerTextPaths);
240255

0 commit comments

Comments
 (0)