Skip to content

Commit

Permalink
Merge pull request #68 from cloudfour/fix-page_writing_issue
Browse files Browse the repository at this point in the history
Fix: Page writing issue
  • Loading branch information
lyzadanger committed Apr 14, 2016
2 parents c5ca08b + d88f53e commit 8efb998
Show file tree
Hide file tree
Showing 34 changed files with 311 additions and 101 deletions.
12 changes: 12 additions & 0 deletions src/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,16 @@ const defaults = {
}
};

/*
* The following are undocumented because it is not anticipated they'll
* change in common usage.
*/
defaults.keys = {
pages : 'pages',
patterns : 'patterns',
collections: 'collections',
data : 'data',
templates : 'templates'
};

export default defaults;
2 changes: 1 addition & 1 deletion src/helpers/pattern.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function renderPatternPartial (patternId, drizzleData, Handlebars) {
const patternObj = deepPattern(patternId, drizzleData.patterns);
const localContext = patternContext(patternObj, drizzleData);
let template = Handlebars.partials[patternId];
if (template) {
if (typeof template !== 'undefined') {
if (typeof template !== 'function') {
template = Handlebars.compile(template);
}
Expand Down
2 changes: 1 addition & 1 deletion src/parse/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { readFileTree } from '../utils/parse';
* @return {Promise} resolving to parsed file data
*/
function parseData (options) {
return readFileTree(options.src.data, options);
return readFileTree(options.src.data, options.keys.data, options);
}

export default parseData;
2 changes: 1 addition & 1 deletion src/parse/pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { readFileTree } from '../utils/parse';
* @return {Promise} resolving to page data
*/
function parsePages (options) {
return readFileTree(options.src.pages, options);
return readFileTree(options.src.pages, options.keys.pages, options);
}

export default parsePages;
13 changes: 6 additions & 7 deletions src/parse/patterns.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,7 @@ const hasPatternOrdering = itms => {
*/
function buildPattern (patternObj, options) {
const patternFile = { path: patternObj.path };
const patternId = resourceId(patternFile,
options.src.patterns.basedir, 'patterns');
checkNamespaceCollision('id', patternObj.data,
`Pattern ${patternId}`, options);
return Object.assign(patternObj, {
id: patternId,
name: (patternObj.data && patternObj.data.name) ||
titleCase(resourceKey(patternFile))
});
Expand Down Expand Up @@ -162,10 +157,13 @@ function buildOrderedPatterns (collection) {
*/
function buildCollection (collectionObj, options) {
const items = buildPatterns (collectionObj, options);
const pseudoFile = { path: collectionPath(items) };
return readFiles(collectionGlob(items), options).then(collData => {
const collectionMeta = (collData.length) ? collData[0].contents : {};
collectionObj.collection = Object.assign ({
name: titleCase(collectionKey(items))
name: titleCase(collectionKey(items)),
id: resourceId(pseudoFile, options.src.patterns.basedir,
options.keys.collections)
}, collectionMeta);
checkNamespaceCollision(['items', 'patterns'], collectionObj.collection,
`Collection ${collectionObj.collection.name}`, options);
Expand Down Expand Up @@ -206,7 +204,8 @@ function buildCollections (patternObj, options, collectionPromises = []) {
* @return {Promise} resolving to pattern/collection data
*/
function parsePatterns (options) {
return readFileTree(options.src.patterns, options).then(patternObj => {
return readFileTree(options.src.patterns, options.keys.patterns, options)
.then(patternObj => {
return Promise.all(buildCollections(patternObj, options))
.then(() => patternObj,
error => DrizzleError.error(error, options.debug));
Expand Down
2 changes: 1 addition & 1 deletion src/parse/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { readFileTree } from '../utils/parse';
* @return {Promise} resolving to object of parsed template contents
*/
function parseTemplates (options) {
return readFileTree(options.src.templates, options);
return readFileTree(options.src.templates, options.keys.templates, options);
}

export default parseTemplates;
3 changes: 2 additions & 1 deletion src/render/collections.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { resourceContext } from '../utils/context';
import { applyTemplate } from '../utils/render';
import { deepObj } from '../utils/object';
import { idKeys } from '../utils/shared';
import DrizzleError from '../utils/error';

/**
Expand All @@ -20,7 +21,7 @@ function renderCollection (patterns, drizzleData, collectionKey) {
let layoutObj;
try {
// deepObj will throw if it fails, which is good and fine...
layoutObj = deepObj(layoutKey.split('.'), drizzleData.templates, false);
layoutObj = deepObj(idKeys(layoutKey), drizzleData.templates, false);
} catch (e) {
// But Make this error more friendly and specific
DrizzleError.error(new DrizzleError(
Expand Down
36 changes: 33 additions & 3 deletions src/utils/object.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { keyname, relativePathArray } from './shared';
import { idKeys, keyname, relativePathArray } from './shared';

import DrizzleError from './error';

Expand Down Expand Up @@ -39,7 +39,7 @@ function deepObj (pathKeys, obj, createEntries = true) {
* @return {Object} Object reference to patterns
*/
function deepPattern (patternId, obj) {
const pathBits = patternId.split('.'); // TODO pattern separator elsewhere?
const pathBits = idKeys(patternId);
pathBits.shift();
pathBits.splice(-1, 0, 'collection', 'items');
return deepObj(pathBits, obj, false);
Expand All @@ -51,13 +51,42 @@ function deepPattern (patternId, obj) {
* @see deepPattern
*/
function deepCollection (collectionId, obj) {
const pathBits = collectionId.split('.'); // TODO pattern separator elsewhere?
const pathBits = idKeys(collectionId);
pathBits.pop();
pathBits.push('collection');
pathBits.shift();
return deepObj(pathBits, obj, false);
}

/**
* isObject function opinionated against Arrays
* @param {Obj}
* @return {Boolean}
*/
function isObject (obj) {
const objType = typeof obj;
return (objType === 'object' && !!obj && !Array.isArray(obj));
}

/**
* Take a deeply-nested object and return a single-level object keyed
* by the `id` property of original object entries.
*
* @param {Obj} obj
* @param {Obj} keyedObj For recursion; not strictly necessary but...
* @return {Obj} keyed by ids
*/
function flattenById (obj, keyedObj = {}) {
if (obj.hasOwnProperty('id')) {
keyedObj[obj.id] = obj;
}
for (var key in obj) {
if (isObject(obj[key])) {
flattenById(obj[key], keyedObj);
}
}
return keyedObj;
}
/**
* Generate a resourceId for a file. Use file.path and base the
* ID elements on the path elements between relativeTo and file. Path
Expand Down Expand Up @@ -99,6 +128,7 @@ function resourceKey (resourceFile) {
export { deepCollection, // object
deepObj, // object
deepPattern, // object
flattenById,
keyname, // object
resourceId, //object
resourceKey // object
Expand Down
7 changes: 5 additions & 2 deletions src/utils/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Promise from 'bluebird';
import {readFile as readFileCB} from 'fs';
var readFile = Promise.promisify(readFileCB);
import { relativePathArray } from './shared';
import { deepObj, resourceKey } from './object'; // TODO NO NO NO NO NO
import { deepObj, resourceKey, resourceId } from './object'; // TODO NO NO NO NO
import DrizzleError from './error';

/**
Expand Down Expand Up @@ -165,14 +165,17 @@ function readFiles (glob, {
*
* @param {Object} src Object with properties `glob` and `basedir`
* @see defaults
* @param {String} prefix Key to prefix items in this object with when creating
* ids. e.g. 'patterns', 'pages'
* @param {Object} options
* @return {Promise} resolving to {Object} of keyed file contents
*/
function readFileTree (src, options) {
function readFileTree (src, prefix, options) {
const fileTree = {};
return readFiles(src.glob, options).then(fileData => {
fileData.forEach(itemFile => {
const fileKeys = relativePathArray(itemFile.path, src.basedir);
itemFile.id = resourceId(itemFile, src.basedir, prefix);
deepObj(fileKeys, fileTree)[resourceKey(itemFile)] = parseLocalData(
itemFile, options);
});
Expand Down
13 changes: 11 additions & 2 deletions src/utils/shared.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import path from 'path';

/**
* Split a string on a common separator.
* @param {String} idString
* @return {Array}
*/
function idKeys (idString) {
return idString.split('.');
}
/**
* Utility function to process strings to make them key-like (for properties).
* Previously this stripped prefixed numbers, etc., but for now it is
Expand All @@ -21,7 +29,7 @@ function keyname (str) {
* @return {Array}
*/
function relativePathArray (filePath, fromPath) {
if (filePath.indexOf(fromPath) === -1) {
if (filePath.indexOf(fromPath) === -1 || filePath === fromPath) {
// TODO Error handling: this should cause a warn
return [];
}
Expand All @@ -44,7 +52,8 @@ function titleCase (str) {
.replace(/\w\S*/g, word => word.charAt(0).toUpperCase() + word.substr(1));
}

export { keyname,
export { idKeys,
keyname,
relativePathArray,
titleCase
};
46 changes: 27 additions & 19 deletions src/utils/write.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import path from 'path';
import Promise from 'bluebird';
import {writeFile as writeFileCB} from 'fs';
import {mkdirp as mkdirpCB} from 'mkdirp';
import { idKeys } from './shared';
var writeFile = Promise.promisify(writeFileCB);
var mkdirp = Promise.promisify(mkdirpCB);

Expand All @@ -18,27 +19,34 @@ function write (filepath, contents) {
}

/**
* Write a single resource (e.g. page or pattern-collection) to
* the filesystem. Update the resourceObj with the outputPath.
*
* @param {Array} entryKeys path elements leading to resource
* @param {Object} resourceObj
* @param {String} destPath path prefix for dest output.
* @see defaults.dest
* Take an object's contents and write them to an HTML file on the filesystem.
* @param {String} resourceId e.g. pages.follow-me.down `.`-separated ID
* representing the hierarchical position of the
* resource in its object structure. Will be used
* to derive output path.
* @param {Object} resourceObj The object to output. Must have `contents` prop
* @param {String} pathPrefix The output path prefix (as defined in
* options.dest—@see defaults).
* @param {String} idPrefix Expected resource-type prefix on ID string. This
* string will be removed from the front of the
* path. E.g. a resourceId of `pages.one.two`
* and `idPrefix` of `pages` will remove the
* 'pages' entry from the path.
* @return {Promise}
*/
function writeResource (entryKeys, resourceObj, destPath) {
const filename = entryKeys.pop();
const outputPath = path.join(
entryKeys.join(path.sep),
`${filename}.html`
);
const fullPath = path.normalize(path.join(
destPath,
outputPath
function writePage (resourceId, resourceObj, pathPrefix, idPrefix = '') {
const subPath = idKeys(resourceId);
if (subPath.length && idPrefix && subPath[0] === idPrefix) {
subPath.shift();
}
const filename = subPath.pop() + '.html';
const outputPath = path.normalize(path.join(
pathPrefix,
subPath.join(path.sep),
filename
));
resourceObj.outputPath = fullPath;
return write(fullPath, resourceObj.contents);
resourceObj.outputPath = outputPath;
return write(outputPath, resourceObj.contents);
}

export { write, writeResource };
export { write, writePage };
20 changes: 7 additions & 13 deletions src/write/collections.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { writeResource } from '../utils/write';
import { writePage } from '../utils/write';
import DrizzleError from '../utils/error';
import path from 'path';

const hasCollection = patterns => patterns.hasOwnProperty('collection');
const isCollection = patterns => patterns.hasOwnProperty('items');
Expand All @@ -10,23 +9,19 @@ const isCollection = patterns => patterns.hasOwnProperty('items');
*
* @param {Object} pages current level of pages tree
* @param {Object} drizzleData
* @param {Array} entryKeys Array of path elements leading to this collection
* @param {Array} writePromises All write promises so far
* @return {Array} of Promises
*/
function walkCollections (patterns, drizzleData,
entryKeys = [], writePromises = []) {
function walkCollections (patterns, drizzleData, writePromises = []) {
if (hasCollection(patterns)) {
writePromises.push(
writeResource(entryKeys, patterns.collection,
drizzleData.options.dest.patterns));
writePromises.push(writePage(patterns.collection.id, patterns.collection,
drizzleData.options.dest.patterns,
drizzleData.options.keys.collections));
}
for (const patternKey in patterns) {
if (!isCollection(patterns[patternKey])) {
entryKeys.push(patternKey);
walkCollections(patterns[patternKey],
drizzleData, entryKeys, writePromises);
entryKeys.pop();
drizzleData, writePromises);
}
}
return writePromises;
Expand All @@ -41,8 +36,7 @@ function walkCollections (patterns, drizzleData,
function writeCollections (drizzleData) {
return Promise.all(walkCollections(
drizzleData.patterns,
drizzleData,
[path.basename(drizzleData.options.src.patterns.basedir)])
drizzleData)
).then(writePromises => drizzleData,
error => DrizzleError.error(error, drizzleData.options.debug));
}
Expand Down
15 changes: 6 additions & 9 deletions src/write/pages.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { writeResource } from '../utils/write';
import { writePage } from '../utils/write';
import DrizzleError from '../utils/error';

const isPage = page => page.hasOwnProperty('contents');
Expand All @@ -9,21 +9,18 @@ const isPage = page => page.hasOwnProperty('contents');
*
* @param {Object} pages current level of pages tree
* @param {Object} drizzleData
* @param {String} currentKey This is a bit inelegant, but we need to keep
* track of the current object's key in the owning
* object because it will be part of the filename(s)
* @param {Array} writePromises All write promises so far
* @return {Array} of Promises
*/
function walkPages (pages, drizzleData, currentKeys = [], writePromises = []) {
function walkPages (pages, drizzleData, writePromises = []) {
if (isPage(pages)) {
return writeResource(currentKeys, pages, drizzleData.options.dest.pages);
return writePage(pages.id, pages,
drizzleData.options.dest.pages,
drizzleData.options.keys.pages);
}
for (var pageKey in pages) {
currentKeys.push(pageKey);
writePromises = writePromises.concat(
walkPages(pages[pageKey], drizzleData, currentKeys, writePromises));
currentKeys.pop();
walkPages(pages[pageKey], drizzleData, writePromises));
}
return writePromises;
}
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/morePages/demos/demo-example-1.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
expectedOutputPath: demos/demo-example-1.html
---

Foo/bar
5 changes: 5 additions & 0 deletions test/fixtures/morePages/demos/demo-example-2.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
expectedOutputPath: demos/demo-example-2.<!DOCTYPE html>
---

Another page
4 changes: 4 additions & 0 deletions test/fixtures/morePages/demos/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
expectedOutputPath: demos/index.html
---
This should get output to `pagePrefix`/demos/index.html
Loading

0 comments on commit 8efb998

Please sign in to comment.