diff --git a/example/index.html b/example/index.html index b311e0fd..240266cc 100644 --- a/example/index.html +++ b/example/index.html @@ -18,6 +18,9 @@ }, "2015-02-16-this-is-a-toml-frontmatter-post.md": { content: "+++\ntitle = \"This is a TOML front matter post\"\nimage = \"/nf-logo.png\"\n\"date\" = \"2015-02-16T00:00:00.000Z\"\n+++\n\n# I Am a Title in Markdown\n\nHello, world\n\n* One Thing\n* Another Thing\n* A Third Thing\n" + }, + "2015-02-14-this-is-a-post-with-a-different-extension.other": { + content: "---\ntitle: This post should not appear because the extension is different\nimage: /nf-logo.png\ndate: 2015-02-14T00:00:00.000Z\n---\n\n# I Am a Title in Markdown\n\nHello, world\n\n* One Thing\n* Another Thing\n* A Third Thing\n" } }, _faqs: { diff --git a/src/backends/backend.js b/src/backends/backend.js index 0001eaba..cddeab18 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -2,7 +2,7 @@ import TestRepoBackend from "./test-repo/implementation"; import GitHubBackend from "./github/implementation"; import NetlifyAuthBackend from "./netlify-auth/implementation"; import { resolveFormat } from "../formats/formats"; -import { selectListMethod, selectEntrySlug, selectEntryPath, selectAllowNewEntries } from "../reducers/collections"; +import { selectListMethod, selectEntrySlug, selectEntryPath, selectAllowNewEntries, selectFolderEntryExtension } from "../reducers/collections"; import { createEntry } from "../valueObjects/Entry"; class LocalStorageAuthStore { @@ -82,7 +82,8 @@ class Backend { listEntries(collection) { const listMethod = this.implementation[selectListMethod(collection)]; - return listMethod.call(this.implementation, collection) + const extension = selectFolderEntryExtension(collection); + return listMethod.call(this.implementation, collection, extension) .then(loadedEntries => ( loadedEntries.map(loadedEntry => createEntry( collection.get("name"), diff --git a/src/backends/github/API.js b/src/backends/github/API.js index b856ea3e..ef7765bf 100644 --- a/src/backends/github/API.js +++ b/src/backends/github/API.js @@ -155,7 +155,14 @@ export default class API { listFiles(path) { return this.request(`${ this.repoURL }/contents/${ path }`, { params: { ref: this.branch }, - }); + }) + .then(files => { + if (!Array.isArray(files)) { + throw new Error(`Cannot list files, path ${path} is not a directory but a ${files.type}`); + } + return files; + }) + .then(files => files.filter(file => file.type === "file")); } readUnpublishedBranchFile(contentKey) { diff --git a/src/backends/github/implementation.js b/src/backends/github/implementation.js index 0c1d982f..c3c77731 100644 --- a/src/backends/github/implementation.js +++ b/src/backends/github/implementation.js @@ -1,6 +1,7 @@ import semaphore from "semaphore"; import AuthenticationPage from "./AuthenticationPage"; import API from "./API"; +import { fileExtension } from '../../lib/pathHelper' const MAX_CONCURRENT_DOWNLOADS = 10; @@ -39,8 +40,9 @@ export default class GitHub { return Promise.resolve(this.token); } - entriesByFolder(collection) { + entriesByFolder(collection, extension) { return this.api.listFiles(collection.get("folder")) + .then(files => files.filter(fileExtension(file.name) === extension)) .then(this.fetchFiles); } diff --git a/src/backends/test-repo/implementation.js b/src/backends/test-repo/implementation.js index f00d9916..0c3ab1a5 100644 --- a/src/backends/test-repo/implementation.js +++ b/src/backends/test-repo/implementation.js @@ -1,4 +1,5 @@ import AuthenticationPage from './AuthenticationPage'; +import { fileExtension } from '../../lib/pathHelper' function getFile(path) { const segments = path.split('/'); @@ -40,11 +41,15 @@ export default class TestRepo { return Promise.resolve(''); } - entriesByFolder(collection) { + entriesByFolder(collection, extension) { const entries = []; const folder = collection.get('folder'); if (folder) { for (const path in window.repoFiles[folder]) { + if (fileExtension(path) !== extension) { + continue; + } + const file = { path: `${ folder }/${ path }` }; entries.push( { diff --git a/src/formats/__tests__/toml.md b/src/formats/__tests__/toml.md deleted file mode 100644 index e69de29b..00000000 diff --git a/src/lib/__tests__/pathHelper.spec.js b/src/lib/__tests__/pathHelper.spec.js new file mode 100644 index 00000000..5a947057 --- /dev/null +++ b/src/lib/__tests__/pathHelper.spec.js @@ -0,0 +1,101 @@ +import { fileExtensionWithSeparator, fileExtension } from '../pathHelper'; + +describe('fileExtensionWithSeparator', () => { + it('should return the extension of a file', () => { + expect( + fileExtensionWithSeparator('index.html') + ).toEqual( + '.html' + ); + }); + + it('should return the extension of a file path', () => { + expect( + fileExtensionWithSeparator('/src/main/index.html') + ).toEqual( + '.html' + ); + }); + + it('should return the extension of a file path with trailing slash', () => { + expect( + fileExtensionWithSeparator('/src/main/index.html/') + ).toEqual( + '.html' + ); + }); + + it('should return the extension for an extension with two ..', () => { + expect( + fileExtensionWithSeparator('/src/main/index..html') + ).toEqual( + '.html' + ); + }); + + it('should return an empty string for the parent path ..', () => { + expect( + fileExtensionWithSeparator('..') + ).toEqual( + '' + ); + }); + + it('should return an empty string if the file has no extension', () => { + expect( + fileExtensionWithSeparator('/src/main/index') + ).toEqual( + '' + ); + }); +}); + +describe('fileExtension', () => { + it('should return the extension of a file', () => { + expect( + fileExtension('index.html') + ).toEqual( + 'html' + ); + }); + + it('should return the extension of a file path', () => { + expect( + fileExtension('/src/main/index.html') + ).toEqual( + 'html' + ); + }); + + it('should return the extension of a file path with trailing slash', () => { + expect( + fileExtension('/src/main/index.html/') + ).toEqual( + 'html' + ); + }); + + it('should return the extension for an extension with two ..', () => { + expect( + fileExtension('/src/main/index..html') + ).toEqual( + 'html' + ); + }); + + it('should return an empty string for the parent path ..', () => { + expect( + fileExtension('..') + ).toEqual( + '' + ); + }); + + it('should return an empty string if the file has no extension', () => { + expect( + fileExtension('/src/main/index') + ).toEqual( + '' + ); + }); +}); diff --git a/src/lib/pathHelper.js b/src/lib/pathHelper.js index 57bb0031..4748122e 100644 --- a/src/lib/pathHelper.js +++ b/src/lib/pathHelper.js @@ -58,11 +58,11 @@ export function basename(p, ext = "") { * last portion of the path. If there is no '.' in the last portion of the path * or the first character of it is '.', then it returns an empty string. * @example Usage example - * path.extname('index.html') + * path.fileExtensionWithSeparator('index.html') * // returns * '.html' */ -export function extname(p) { +export function fileExtensionWithSeparator(p) { p = normalizePath(p); const sections = p.split('/'); p = sections.pop(); @@ -79,3 +79,17 @@ export function extname(p) { } return p.substr(i); } + +/** + * Return the extension of the path, from after the last '.' to end of string in the + * last portion of the path. If there is no '.' in the last portion of the path + * or the first character of it is '.', then it returns an empty string. + * @example Usage example + * path.fileExtension('index.html') + * // returns + * 'html' + */ +export function fileExtension(p) { + const ext = fileExtensionWithSeparator(p); + return ext === '' ? ext : ext.substr(1); +} diff --git a/src/reducers/collections.js b/src/reducers/collections.js index 35a83064..4cf076c8 100644 --- a/src/reducers/collections.js +++ b/src/reducers/collections.js @@ -88,6 +88,7 @@ const selectors = { }; export const selectFields = (collection, slug) => selectors[collection.get('type')].fields(collection, slug); +export const selectFolderEntryExtension = (collection) => selectors[FOLDER].entryExtension(collection); export const selectEntryPath = (collection, slug) => selectors[collection.get('type')].entryPath(collection, slug); export const selectEntrySlug = (collection, path) => selectors[collection.get('type')].entrySlug(collection, path); export const selectListMethod = collection => selectors[collection.get('type')].listMethod();