summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSoreine <nicolas@gitbook.com>2017-03-09 14:02:02 +0100
committerSoreine <nicolas@gitbook.com>2017-03-09 14:02:02 +0100
commitb3c7152f94c5ff2736ff302bdd9a132f7f7569ed (patch)
treeb09d356043b70a745c0af81b2c63e8966a5d60da
parent4e3e3e515de5da4a9926ccae38256381f2a32714 (diff)
downloadgitbook-b3c7152f94c5ff2736ff302bdd9a132f7f7569ed.zip
gitbook-b3c7152f94c5ff2736ff302bdd9a132f7f7569ed.tar.gz
gitbook-b3c7152f94c5ff2736ff302bdd9a132f7f7569ed.tar.bz2
gitbook: Add summary modifier `deleteByPath`
-rw-r--r--packages/gitbook/src/modifiers/summary/__tests__/deleteByPath.js170
-rw-r--r--packages/gitbook/src/modifiers/summary/deleteByPath.js66
-rw-r--r--packages/gitbook/src/modifiers/summary/index.js1
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'),