git.iko.soy/typeable/remark-validate-links
on main help sign in
Remove superfluous comments
commit27f46cc34c7444d3f21ba4c3482e02d9df1ed147 authorTitus Wormer <tituswormer@gmail.com> dateSat 26 Nov 2016, 12:28 (10 years ago) parent7731474
2 files changed+186−281
index.js+186−270 test/index.js−11
index.js+186−270
@@ -1,14 +1,5 @@
1−/**
2− * @author Titus Wormer
3− * @copyright 2015 Titus Wormer
4− * @license MIT
5− * @module remark:validate-links
6− * @fileoverview Validate links to headings and files in markdown.
7− */
8−
91'use strict';
102
11−/* Dependencies. */
123var url = require('url');
134var fs = require('fs');
145var path = require('path');
@@ -20,96 +11,179 @@ var urljoin = require('urljoin');
2011var slug = require('remark-slug');
2112var xtend = require('xtend');
2213
23−/* Expose. */
2414module.exports = attacher;
2515
26−/* Methods. */
16+completer.pluginId = 'remark-validate-links';
17+
2718var exists = fs.existsSync;
2819var parse = url.parse;
2920
30−/**
31− * Get the `pathname` of `uri`, if applicable.
32− *
33− * @todo Externalise.
34− * @param {string} uri - Reference.
35− * @return {string?} - Pathname.
36− */
37−function getPathname(uri) {
38− return parse(uri).pathname;
21+function attacher(remark, options, fileSet) {
22+ var repo = (options || {}).repository;
23+ var pack;
24+
25+ /* Throw when not on the CLI. */
26+ if (!fileSet) {
27+ throw new Error('remark-validate-links only works on the CLI');
28+ }
29+
30+ /* Try to get the repo from `package.json` when not
31+ * given. */
32+ if (!repo) {
33+ try {
34+ pack = fileSet.files[0].cwd;
35+ // eslint-disable-next-line import/no-dynamic-require
36+ pack = require(path.resolve(pack, 'package.json'));
37+ } catch (err) {
38+ pack = {};
39+ }
40+
41+ repo = pack.repository ? pack.repository.url || pack.repository : '';
42+ }
43+
44+ repo = repo ? gh(repo) : {};
45+
46+ /* Attach a `completer`. */
47+ fileSet.use(completer);
48+
49+ /* Attach `slug`. */
50+ remark.use(slug);
51+
52+ /* Expose transformer. */
53+ return transformerFactory({user: repo.user, repo: repo.repo}, fileSet);
3954}
4055
41−/**
42− * Get the `hash` of `uri`, if applicable.
43− *
44− * @todo Externalise.
45− * @param {string} uri - Reference.
46− * @return {string?} - Hash.
47− */
48−function getHash(uri) {
49− var hash = parse(uri).hash;
56+/* Completer. */
57+function completer(set, done) {
58+ var exposed = {};
5059
51− return hash ? hash.slice(1) : null;
60+ set.valueOf().forEach(function (file) {
61+ var landmarks = file.data.remarkValidateLinksLandmarks;
62+
63+ if (landmarks) {
64+ exposed = xtend(exposed, landmarks);
65+ }
66+ });
67+
68+ set.valueOf().forEach(function (file) {
69+ /* istanbul ignore else - stdin */
70+ if (file.path) {
71+ validate(exposed, file);
72+ }
73+ });
74+
75+ done();
5276}
5377
54−/**
55− * Suggest a possible similar reference.
56− *
57− * @param {string} pathname - Unfound reference.
58− * @param {Object<string, boolean>} references - All
59− * references.
60− * @return {string?} - Suggested reference.
61− */
62−function getClosest(pathname, references) {
63− var hash = getHash(pathname);
64− var base = getPathname(pathname);
65− var dictionary = [];
66− var reference;
67− var subhash;
68− var subbase;
78+/* Factory to create a transformer based on the given
79+ * project and set. */
80+function transformerFactory(project, fileSet) {
81+ return transformer;
6982
70− for (reference in references) {
71− subbase = getPathname(reference);
72− subhash = getHash(reference);
83+ /* Transformer. Adds references files to the set. */
84+ function transformer(ast, file) {
85+ var filePath = file.path;
86+ var space = file.data;
87+ var links = [];
88+ var landmarks = {};
89+ var references;
90+ var current;
91+ var link;
92+ var pathname;
7393
74− if (getPathname(reference) === base) {
75− if (subhash && hash) {
76− dictionary.push(subhash);
94+ /* istanbul ignore if - stdin */
95+ if (!filePath) {
96+ return;
97+ }
98+
99+ references = gatherReferences(file, ast, project);
100+ current = getPathname(filePath);
101+
102+ for (link in references) {
103+ pathname = getPathname(link);
104+
105+ if (
106+ pathname !== current &&
107+ getHash(link) &&
108+ links.indexOf(pathname) === -1
109+ ) {
110+ links.push(pathname);
111+ fileSet.add(pathname);
77112 }
78− } else if (!subhash && !hash) {
79− dictionary.push(subbase);
80113 }
81− }
82114
83− return propose(hash ? hash : base, dictionary, {threshold: 0.7});
115+ landmarks[filePath] = true;
116+
117+ visit(ast, function (node) {
118+ var data = node.data || {};
119+ var attrs = data.htmlAttributes || {};
120+ var id = attrs.name || attrs.id || data.id;
121+
122+ if (id) {
123+ landmarks[filePath + '#' + id] = true;
124+ }
125+ });
126+
127+ space.remarkValidateLinksReferences = references;
128+ space.remarkValidateLinksLandmarks = landmarks;
129+ }
84130}
85131
86−/**
87− * Utilitity to warn `reason` for each node in `nodes`,
88− * on `file`.
89− *
90− * @param {File} file - Virtual file.
91− * @param {Array.<Node>} nodes - Offending nodes.
92− * @param {string} reason - Message.
93− */
94−function warnAll(file, nodes, reason) {
95− nodes.forEach(function (node) {
96− var message = file.message(reason, node);
97− message.source = message.ruleId = 'remark-validate-links';
98− });
132+/* Check if `file` references headings or files not in
133+ * `exposed`. If `project` is given, normalizes GitHub blob
134+ * URLs. */
135+function validate(exposed, file) {
136+ var references = file.data.remarkValidateLinksReferences;
137+ var filePath = file.path;
138+ var reference;
139+ var nodes;
140+ var real;
141+ var hash;
142+ var pathname;
143+ var warning;
144+ var suggestion;
145+
146+ for (reference in references) {
147+ nodes = references[reference];
148+ real = exposed[reference];
149+ hash = getHash(reference);
150+
151+ /* Check if files without `hash` can be linked to.
152+ * Because there’s no need to inspect those files
153+ * for headings they are not added to remark. This
154+ * is especially useful because they might be
155+ * non-markdown files. Here we check if they exist. */
156+ if ((real === undefined || real === null) && !hash) {
157+ real = references[reference] = exists(reference);
158+ }
159+
160+ if (!real) {
161+ if (hash) {
162+ pathname = getPathname(reference);
163+ warning = 'Link to unknown heading';
164+
165+ if (pathname !== filePath) {
166+ warning += ' in `' + pathname + '`';
167+ }
168+
169+ warning += ': `' + hash + '`';
170+ } else {
171+ warning = 'Link to unknown file: `' + reference + '`';
172+ }
173+
174+ suggestion = getClosest(reference, exposed);
175+
176+ if (suggestion) {
177+ warning += '. Did you mean `' + suggestion + '`';
178+ }
179+
180+ warnAll(file, nodes, warning);
181+ }
182+ }
99183}
100184
101−/**
102− * Gather references: a map of file-paths references
103− * to be one or more nodes.
104− *
105− * @param {File} file - Set of virtual files.
106− * @param {Node} tree - Syntax tree.
107− * @param {Object.<string, string>} project - GitHub
108− * project, with a `user` and `repo` property
109− * (optional).
110− * @return {Object.<string, Array.<Node>>} exposed - Map of
111− * file-paths (and anchors) which are referenced.
112− */
185+/* Gather references: a map of file-paths references
186+ * to be one or more nodes. */
113187function gatherReferences(file, tree, project) {
114188 var cache = {};
115189 var filePath = file.path;
@@ -123,9 +197,7 @@ function gatherReferences(file, tree, project) {
123197 prefix = '/' + project.user + '/' + project.repo + '/blob/';
124198 }
125199
126− /**
127− * Handle new links.
128− */
200+ /* Handle new links. */
129201 function onlink(node) {
130202 var link = node.url;
131203 var definition;
@@ -223,204 +295,48 @@ function gatherReferences(file, tree, project) {
223295 return cache;
224296}
225297
226−/**
227− * Check if `file` references headings or files not in
228− * `exposed`. If `project` is given, normalizes GitHub blob
229− * URLs.
230− *
231− * @param {Object.<string, boolean?>} exposed - Map of
232− * file-paths (and anchors) which can be references to.
233− * @param {File} file - Set of virtual files.
234− */
235−function validate(exposed, file) {
236− var references = file.data.remarkValidateLinksReferences;
237− var filePath = file.path;
238− var reference;
239− var nodes;
240− var real;
241− var hash;
242− var pathname;
243− var warning;
244− var suggestion;
245−
246− for (reference in references) {
247− nodes = references[reference];
248− real = exposed[reference];
249− hash = getHash(reference);
250−
251− /* Check if files without `hash` can be linked to.
252− * Because there’s no need to inspect those files
253− * for headings they are not added to remark. This
254− * is especially useful because they might be
255− * non-markdown files. Here we check if they exist. */
256− if ((real === undefined || real === null) && !hash) {
257− real = references[reference] = exists(reference);
258− }
259−
260− if (!real) {
261− if (hash) {
262− pathname = getPathname(reference);
263− warning = 'Link to unknown heading';
264−
265− if (pathname !== filePath) {
266− warning += ' in `' + pathname + '`';
267− }
268−
269− warning += ': `' + hash + '`';
270− } else {
271− warning = 'Link to unknown file: `' + reference + '`';
272− }
273−
274− suggestion = getClosest(reference, exposed);
275−
276− if (suggestion) {
277− warning += '. Did you mean `' + suggestion + '`';
278− }
279−
280− warnAll(file, nodes, warning);
281− }
282− }
283−}
284−
285−/**
286− * Completer.
287− *
288− * @property {*} pluginId - Unique ID so completers by
289− * this plug-in are not added multiple times to a single
290− * file-set pipeline.
291− * @param {FileSet} set - Virtual file-set.
292− * @param {function(err?)} done - Callback.
293− */
294−function completer(set, done) {
295− var exposed = {};
296−
297− set.valueOf().forEach(function (file) {
298− var landmarks = file.data.remarkValidateLinksLandmarks;
299−
300− if (landmarks) {
301− exposed = xtend(exposed, landmarks);
302− }
303− });
304−
305− set.valueOf().forEach(function (file) {
306− /* istanbul ignore else - stdin */
307− if (file.path) {
308− validate(exposed, file);
309− }
298+/* Utilitity to warn `reason` for each node in `nodes`,
299+ * on `file`. */
300+function warnAll(file, nodes, reason) {
301+ nodes.forEach(function (node) {
302+ var message = file.message(reason, node);
303+ message.source = message.ruleId = 'remark-validate-links';
310304 });
311−
312− done();
313305}
314306
315−completer.pluginId = 'remark-validate-links';
316−
317−/**
318−* Factory to create a transformer based on the given
319−* project and set.
320−*
321−* @param {Object.<string, string>} project - GitHub
322−* project, with a `user` and `repo` property
323−* (optional).
324−* @param {FileSet} fileSet - Set of virtual files.
325−* @return {function(ast, file)} - Transformer.
326−*/
327−function transformerFactory(project, fileSet) {
328− /**
329− * Transformer. Adds references files to the set.
330− *
331− * @param {*} ast - Node.
332− * @param {File} file - Virtual file.
333− */
334− function transformer(ast, file) {
335− var filePath = file.path;
336− var space = file.data;
337− var links = [];
338− var landmarks = {};
339− var references;
340− var current;
341− var link;
342− var pathname;
343−
344− /* istanbul ignore if - stdin */
345− if (!filePath) {
346− return;
347− }
348−
349− references = gatherReferences(file, ast, project);
350− current = getPathname(filePath);
307+/* Suggest a possible similar reference. */
308+function getClosest(pathname, references) {
309+ var hash = getHash(pathname);
310+ var base = getPathname(pathname);
311+ var dictionary = [];
312+ var reference;
313+ var subhash;
314+ var subbase;
351315
352− for (link in references) {
353− pathname = getPathname(link);
316+ for (reference in references) {
317+ subbase = getPathname(reference);
318+ subhash = getHash(reference);
354319
355− if (
356− pathname !== current &&
357− getHash(link) &&
358− links.indexOf(pathname) === -1
359− ) {
360− links.push(pathname);
361− fileSet.add(pathname);
320+ if (getPathname(reference) === base) {
321+ if (subhash && hash) {
322+ dictionary.push(subhash);
362323 }
324+ } else if (!subhash && !hash) {
325+ dictionary.push(subbase);
363326 }
364−
365− landmarks[filePath] = true;
366−
367− visit(ast, function (node) {
368− var data = node.data || {};
369− var attrs = data.htmlAttributes || {};
370− var id = attrs.name || attrs.id || data.id;
371−
372− if (id) {
373− landmarks[filePath + '#' + id] = true;
374− }
375− });
376−
377− space.remarkValidateLinksReferences = references;
378− space.remarkValidateLinksLandmarks = landmarks;
379327 }
380328
381− return transformer;
329+ return propose(hash ? hash : base, dictionary, {threshold: 0.7});
382330}
383331
384−/**
385−* Attacher.
386−*
387−* @param {Remark} remark - Processor.
388−* @param {Object?} options - Settings.
389−* @param {FileSet?} fileSet - Virtual file-set.
390−* @throws {Error} - When `fileSet` is not given (when not on
391−* the CLI).
392−*/
393−function attacher(remark, options, fileSet) {
394− var repo = (options || {}).repository;
395− var pack;
396−
397− /* Throw when not on the CLI. */
398− if (!fileSet) {
399− throw new Error('remark-validate-links only works on the CLI');
400− }
401−
402− /* Try to get the repo from `package.json` when not
403− * given. */
404− if (!repo) {
405− try {
406− pack = fileSet.files[0].cwd;
407− // eslint-disable-next-line import/no-dynamic-require
408− pack = require(path.resolve(pack, 'package.json'));
409− } catch (err) {
410− pack = {};
411− }
412−
413− repo = pack.repository ? pack.repository.url || pack.repository : '';
414− }
415−
416− repo = repo ? gh(repo) : {};
417−
418− /* Attach a `completer`. */
419− fileSet.use(completer);
332+/* Get the `hash` of `uri`, if applicable. */
333+function getHash(uri) {
334+ var hash = parse(uri).hash;
420335
421− /* Attach `slug`. */
422− remark.use(slug);
336+ return hash ? hash.slice(1) : null;
337+}
423338
424− /* Expose transformer. */
425− return transformerFactory({user: repo.user, repo: repo.repo}, fileSet);
339+/* Get the `pathname` of `uri`, if applicable. */
340+function getPathname(uri) {
341+ return parse(uri).pathname;
426342}
test/index.js−11
@@ -1,14 +1,5 @@
1−/**
2− * @author Titus Wormer
3− * @copyright 2015 Titus Wormer
4− * @license MIT
5− * @module remark:validate-links:test
6− * @fileoverview Test suite for `remark-validate-links`.
7− */
8−
91'use strict';
102
11−/* Dependencies. */
123var fs = require('fs');
134var path = require('path');
145var test = require('tape');
@@ -17,7 +8,6 @@ var remark = require('remark');
178var strip = require('strip-ansi');
189var links = require('..');
1910
20−/* Constants. */
2111process.chdir(path.resolve(process.cwd(), 'test', 'fixtures'));
2212
2313test.onFinish(function () {
@@ -28,7 +18,6 @@ var bin = path.join('..', '..', 'node_modules', '.bin', 'remark');
2818
2919var source = ' remark-validate-links remark-validate-links';
3020
31−/* Tests. */
3221test('remark-validate-links', function (t) {
3322 t.plan(8);
3423