diff options
author | Soreine <nicolas@gitbook.com> | 2017-03-09 14:02:02 +0100 |
---|---|---|
committer | Soreine <nicolas@gitbook.com> | 2017-03-09 14:02:02 +0100 |
commit | b3c7152f94c5ff2736ff302bdd9a132f7f7569ed (patch) | |
tree | b09d356043b70a745c0af81b2c63e8966a5d60da | |
parent | 4e3e3e515de5da4a9926ccae38256381f2a32714 (diff) | |
download | gitbook-b3c7152f94c5ff2736ff302bdd9a132f7f7569ed.zip gitbook-b3c7152f94c5ff2736ff302bdd9a132f7f7569ed.tar.gz gitbook-b3c7152f94c5ff2736ff302bdd9a132f7f7569ed.tar.bz2 |
gitbook: Add summary modifier `deleteByPath`
3 files changed, 237 insertions, 0 deletions
diff --git a/packages/gitbook/src/modifiers/summary/__tests__/deleteByPath.js b/packages/gitbook/src/modifiers/summary/__tests__/deleteByPath.js new file mode 100644 index 0000000..2557210 --- /dev/null +++ b/packages/gitbook/src/modifiers/summary/__tests__/deleteByPath.js @@ -0,0 +1,170 @@ +const expect = require('expect'); +const Summary = require('../../../models/summary'); + +describe('deleteByPath', () => { + const deleteByPath = require('../deleteByPath'); + + const summary = Summary.createFromParts([ + { + articles: [ + { + title: 'Intro', + path: 'README.md' + } + ] + }, + { + title: 'Part', + articles: [ + { + title: '1', + path: '' + }, + { + title: '2', + path: '2/README.md', + articles: [ + { + title: '2a', + path: '2/1.md' + }, + { + title: '2b', + path: '2/1.md#anchor' + } + ] + } + ] + } + ]); + + it('should remove a single article', () => { + const newSummary = deleteByPath(summary, 'README.md'); + expectParts(newSummary, [ + { + articles: [] + }, + { + title: 'Part', + articles: [ + { + title: '1', + path: '' + }, + { + title: '2', + path: '2/README.md', + articles: [ + { + title: '2a', + path: '2/1.md' + }, + { + title: '2b', + path: '2/1.md#anchor' + } + ] + } + ] + } + ]); + }); + + it('should handle anchors', () => { + const newSummary = deleteByPath(summary, '2/1.md'); + expectParts(newSummary, [ + { + articles: [ + { + title: 'Intro', + path: 'README.md' + } + ] + }, + { + title: 'Part', + articles: [ + { + title: '1', + path: '' + }, + { + title: '2', + path: '2/README.md', + articles: [] + } + ] + } + ]); + }); + + it('should handle dirs', () => { + const newSummary = deleteByPath(summary, '2/'); + expectParts(newSummary, [ + { + articles: [ + { + title: 'Intro', + path: 'README.md' + } + ] + }, + { + title: 'Part', + articles: [ + { + title: '1', + path: '' + } + ] + } + ]); + }); + + it('should not delete descendants that don\'t match', () => { + const newSummary = deleteByPath(summary, '2/README.md'); + expectParts(newSummary, [ + { + articles: [ + { + title: 'Intro', + path: 'README.md' + } + ] + }, + { + title: 'Part', + articles: [ + { + title: '1', + path: '' + }, + { + title: '2', + // Unlink + articles: [ + { + title: '2a', + path: '2/1.md' + }, + { + title: '2b', + path: '2/1.md#anchor' + } + ] + } + ] + } + ]); + }); +}); + +function expectParts(summary, expectedParts) { + const expectedSummary = Summary.createFromParts(expectedParts); + expect( + summary.toJS().parts + ).toEqual( + expectedSummary.toJS().parts + ); +} + diff --git a/packages/gitbook/src/modifiers/summary/deleteByPath.js b/packages/gitbook/src/modifiers/summary/deleteByPath.js new file mode 100644 index 0000000..3ab6c92 --- /dev/null +++ b/packages/gitbook/src/modifiers/summary/deleteByPath.js @@ -0,0 +1,66 @@ +const Path = require('path'); + +/** + Delete all articles that point to a file under the given path, unless + some of their children do not share that same path. + + @param {Summary} summary + @param {String} path Can be a file path or directory path + @return {Summary} +*/ +function deleteByPath(summary, path) { + const parts = summary.getParts() + .map((part) => { + const articles = deleteArticlesByPath(part.getArticles(), path); + return part.merge({ articles }); + }); + + return summary.merge({ parts }); +} + +/** + Same as `deleteByPath` but for a list of articles. + + @param {List<Article>} articles + @param {String} path + @return {List<Article} + */ +function deleteArticlesByPath(articles, path) { + return articles + // Delete leaf articles first + .map(article => article.merge({ + articles: deleteArticlesByPath(article.articles, path) + })) + // Then delete top level articles if they don't have any descendant left. + .filterNot( + article => article.getArticles().isEmpty() && isInside(article, path) + ) + // Unlink those left + .map(article => isInside(article, path) ? article.merge({ ref: '' }) : article); +} + +/** + @param {Article} article + @param {String} potentialParent + @return {Boolean} True if path match the parent path, or if path is inside parent path + */ +function isInside(article, potentialParent) { + // For inside-directory checking, we want to allow trailing slashes, so normalize. + const path = stripTrailingSep(article.getPath() || ''); + potentialParent = stripTrailingSep(potentialParent); + + return path.lastIndexOf(potentialParent, 0) === 0 && + ( + path[potentialParent.length] === Path.sep || + path[potentialParent.length] === undefined + ); +} + +function stripTrailingSep(path) { + if (path[path.length - 1] === Path.sep) { + return path.slice(0, -1); + } + return path; +} + +module.exports = deleteByPath; diff --git a/packages/gitbook/src/modifiers/summary/index.js b/packages/gitbook/src/modifiers/summary/index.js index cb8e8f9..28ec625 100644 --- a/packages/gitbook/src/modifiers/summary/index.js +++ b/packages/gitbook/src/modifiers/summary/index.js @@ -9,6 +9,7 @@ module.exports = { unshiftArticle: require('./unshiftArticle'), editArticleTitle: require('./editArticleTitle'), editArticleRef: require('./editArticleRef'), + deleteByPath: require('./deleteByPath'), // Parts insertPart: require('./insertPart'), removePart: require('./removePart'), |