Skip to content

Commit 6baa5e6

Browse files
authored
Search index checker (#1442)
* Add checking of the search index * Rearrangement makes broken link errors nicer.
1 parent 52bb306 commit 6baa5e6

File tree

2 files changed

+99
-26
lines changed

2 files changed

+99
-26
lines changed

lib/dartdoc.dart

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
library dartdoc;
77

88
import 'dart:async';
9+
import 'dart:convert';
910
import 'dart:io';
1011

1112
import 'package:analyzer/dart/element/element.dart' show LibraryElement;
@@ -214,45 +215,61 @@ class DartDoc {
214215
return new DartDocResults(packageMeta, package, outputDir);
215216
}
216217

217-
void _warn(Package package, PackageWarning kind, String p, String origin,
218-
{String source}) {
218+
/// Warn on file paths.
219+
void _warn(Package package, PackageWarning kind, String warnOn, String origin,
220+
{String referredFrom}) {
219221
// Ordinarily this would go in [Package.warn], but we don't actually know what
220222
// ModelElement to warn on yet.
221-
Locatable referenceElement;
222-
Set<Locatable> referenceElements;
223+
Locatable referredFromElement;
224+
Locatable warnOnElement;
225+
Set<Locatable> referredFromElements;
226+
Set<Locatable> warnOnElements;
223227

224228
// Make all paths relative to origin.
225-
if (path.isWithin(origin, p)) {
226-
p = path.relative(p, from: origin);
229+
if (path.isWithin(origin, warnOn)) {
230+
warnOn = path.relative(warnOn, from: origin);
227231
}
228-
if (source != null) {
229-
if (path.isWithin(origin, source)) {
230-
source = path.relative(source, from: origin);
232+
if (referredFrom != null) {
233+
if (path.isWithin(origin, referredFrom)) {
234+
referredFrom = path.relative(referredFrom, from: origin);
231235
}
232236
// Source paths are always relative.
233-
referenceElements = package.allHrefs[source];
234-
} else {
235-
referenceElements = package.allHrefs[p];
237+
referredFromElements = _hrefs[referredFrom];
238+
}
239+
warnOnElements = _hrefs[warnOn];
240+
241+
if (referredFromElements != null) {
242+
if (referredFromElements.any((e) => e.isCanonical)) {
243+
referredFromElement =
244+
referredFromElements.firstWhere((e) => e.isCanonical);
245+
} else {
246+
// If we don't have a canonical element, just pick one.
247+
referredFromElement =
248+
referredFromElements.isEmpty ? null : referredFromElements.first;
249+
}
236250
}
237-
if (referenceElements != null) {
238-
if (referenceElements.any((e) => e.isCanonical)) {
239-
referenceElement = referenceElements.firstWhere((e) => e.isCanonical);
251+
if (warnOnElements != null) {
252+
if (warnOnElements.any((e) => e.isCanonical)) {
253+
warnOnElement = warnOnElements.firstWhere((e) => e.isCanonical);
240254
} else {
241255
// If we don't have a canonical element, just pick one.
242-
referenceElement =
243-
referenceElements.isEmpty ? null : referenceElements.first;
256+
warnOnElement = warnOnElements.isEmpty ? null : warnOnElements.first;
244257
}
245258
}
246-
if (referenceElement == null && source == 'index.html')
247-
referenceElement = package;
248-
package.warnOnElement(referenceElement, kind, message: p);
259+
260+
if (referredFromElement == null && referredFrom == 'index.html')
261+
referredFromElement = package;
262+
String message = warnOn;
263+
if (referredFrom == 'index.json') message = '$warnOn (from index.json)';
264+
package.warnOnElement(warnOnElement, kind,
265+
message: message, referredFrom: referredFromElement);
249266
}
250267

251268
void _doOrphanCheck(Package package, String origin, Set<String> visited) {
252269
String normalOrigin = path.normalize(origin);
253270
String staticAssets = path.joinAll([normalOrigin, 'static-assets', '']);
254271
String indexJson = path.joinAll([normalOrigin, 'index.json']);
255-
bool foundIndex = false;
272+
bool foundIndexJson = false;
256273
for (FileSystemEntity f
257274
in new Directory(normalOrigin).listSync(recursive: true)) {
258275
var fullPath = path.normalize(f.path);
@@ -263,7 +280,7 @@ class DartDoc {
263280
continue;
264281
}
265282
if (fullPath == indexJson) {
266-
foundIndex = true;
283+
foundIndexJson = true;
267284
_onCheckProgress.add(fullPath);
268285
continue;
269286
}
@@ -277,7 +294,7 @@ class DartDoc {
277294
_onCheckProgress.add(fullPath);
278295
}
279296

280-
if (!foundIndex) {
297+
if (!foundIndexJson) {
281298
_warn(package, PackageWarning.brokenLink, indexJson, normalOrigin);
282299
_onCheckProgress.add(indexJson);
283300
}
@@ -305,6 +322,42 @@ class DartDoc {
305322
return new Tuple2(stringLinks, baseHref);
306323
}
307324

325+
void _doSearchIndexCheck(
326+
Package package, String origin, Set<String> visited) {
327+
String fullPath = path.joinAll([origin, 'index.json']);
328+
String indexPath = path.joinAll([origin, 'index.html']);
329+
File file = new File("$fullPath");
330+
if (!file.existsSync()) {
331+
return null;
332+
}
333+
JsonDecoder decoder = new JsonDecoder();
334+
List jsonData = decoder.convert(file.readAsStringSync());
335+
336+
Set<String> found = new Set();
337+
found.add(fullPath);
338+
// The package index isn't supposed to be in the search, so suppress the
339+
// warning.
340+
found.add(indexPath);
341+
for (Map<String, String> entry in jsonData) {
342+
if (entry.containsKey('href')) {
343+
String entryPath = path.joinAll([origin, entry['href']]);
344+
if (!visited.contains(entryPath)) {
345+
_warn(package, PackageWarning.brokenLink, entryPath,
346+
path.normalize(origin),
347+
referredFrom: fullPath);
348+
}
349+
found.add(entryPath);
350+
}
351+
}
352+
// Missing from search index
353+
Set<String> missing_from_search = visited.difference(found);
354+
for (String s in missing_from_search) {
355+
_warn(package, PackageWarning.missingFromSearchIndex, s,
356+
path.normalize(origin),
357+
referredFrom: fullPath);
358+
}
359+
}
360+
308361
void _doCheck(
309362
Package package, String origin, Set<String> visited, String pathToCheck,
310363
[String source, String fullPath]) {
@@ -313,15 +366,15 @@ class DartDoc {
313366
fullPath = path.normalize(fullPath);
314367
}
315368

316-
visited.add(fullPath);
317369
Tuple2 stringLinksAndHref = _getStringLinksAndHref(fullPath);
318370
if (stringLinksAndHref == null) {
319371
_warn(package, PackageWarning.brokenLink, pathToCheck,
320372
path.normalize(origin),
321-
source: source);
373+
referredFrom: source);
322374
_onCheckProgress.add(pathToCheck);
323375
return null;
324376
}
377+
visited.add(fullPath);
325378
Iterable<String> stringLinks = stringLinksAndHref.item1;
326379
String baseHref = stringLinksAndHref.item2;
327380

@@ -355,10 +408,10 @@ class DartDoc {
355408

356409
final Set<String> visited = new Set();
357410
final String start = 'index.html';
358-
visited.add(start);
359411
stdout.write('\nvalidating docs...');
360412
_doCheck(package, origin, visited, start);
361413
_doOrphanCheck(package, origin, visited);
414+
_doSearchIndexCheck(package, origin, visited);
362415
}
363416

364417
List<LibraryElement> _parseLibraries(

lib/src/model.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2747,6 +2747,7 @@ enum PackageWarning {
27472747
brokenLink,
27482748
orphanedFile,
27492749
unknownFile,
2750+
missingFromSearchIndex,
27502751
typeAsHtml,
27512752
}
27522753

@@ -2789,6 +2790,10 @@ Map<PackageWarning, List<String>> packageWarningText = {
27892790
"unknownFile",
27902791
"A leftover file exists in the tree that dartdoc did not write in this pass"
27912792
],
2793+
PackageWarning.missingFromSearchIndex: [
2794+
"missingFromSearchIndex",
2795+
"A file generated by dartdoc is not present in the generated index.json"
2796+
],
27922797
PackageWarning.typeAsHtml: [
27932798
"typeAsHtml",
27942799
"Use of <> in a comment for type parameters is being treated as HTML by markdown"
@@ -3094,6 +3099,8 @@ class Package implements Nameable, Documentable {
30943099
break;
30953100
case PackageWarning.brokenLink:
30963101
warningMessage = 'dartdoc generated a broken link to: ${message}';
3102+
warnablePrefix = 'to element';
3103+
referredFromPrefix = 'linked to from';
30973104
break;
30983105
case PackageWarning.orphanedFile:
30993106
warningMessage = 'dartdoc generated a file orphan: ${message}';
@@ -3102,6 +3109,10 @@ class Package implements Nameable, Documentable {
31023109
warningMessage =
31033110
'dartdoc detected an unknown file in the doc tree: ${message}';
31043111
break;
3112+
case PackageWarning.missingFromSearchIndex:
3113+
warningMessage =
3114+
'dartdoc generated a file not in the search index: ${message}';
3115+
break;
31053116
case PackageWarning.typeAsHtml:
31063117
// The message for this warning can contain many punctuation and other symbols,
31073118
// so bracket with a triple quote for defense.
@@ -3233,10 +3244,19 @@ class Package implements Nameable, Documentable {
32333244
// than toList().
32343245
for (ModelElement modelElement
32353246
in _allConstructedModelElements.values.toList()) {
3247+
// Technically speaking we should be able to use canonical model elements
3248+
// only here, but since the warnings that depend on this debug
3249+
// canonicalization problems, don't limit ourselves in case an href is
3250+
// generated for something non-canonical.
32363251
if (modelElement.href == null) continue;
32373252
hrefMap.putIfAbsent(modelElement.href, () => new Set());
32383253
hrefMap[modelElement.href].add(modelElement);
32393254
}
3255+
for (Library library in _libraries) {
3256+
if (library.href == null) continue;
3257+
hrefMap.putIfAbsent(library.href, () => new Set());
3258+
hrefMap[library.href].add(library);
3259+
}
32403260
return hrefMap;
32413261
}
32423262

0 commit comments

Comments
 (0)