fabricator-assemble
Advanced tools
Comparing version 1.0.6 to 1.1.0
192
index.js
// modules | ||
var _ = require('lodash'); | ||
var beautifyHtml = require('js-beautify').html; | ||
var changeCase = require('change-case'); | ||
var chalk = require('chalk'); | ||
var fs = require('fs'); | ||
var globby = require('globby'); | ||
var Handlebars = require('handlebars'); | ||
var inflect = require('i')(); | ||
var matter = require('gray-matter'); | ||
@@ -64,2 +65,12 @@ var md = require('markdown-it')({ html: true, linkify: true }); | ||
/** | ||
* Keywords used to access items in views | ||
* @type {Object} | ||
*/ | ||
keys: { | ||
materials: 'materials', | ||
views: 'views', | ||
docs: 'docs' | ||
}, | ||
/** | ||
* Location to write files | ||
@@ -69,2 +80,3 @@ * @type {String} | ||
dest: 'dist', | ||
/** | ||
@@ -78,3 +90,15 @@ * beautifier options | ||
indent_with_tabs: true | ||
} | ||
}, | ||
/** | ||
* Function to call when an error occurs | ||
* @type {Function} | ||
*/ | ||
onError: null, | ||
/** | ||
* Whether or not to log errors to console | ||
* @type {Boolean} | ||
*/ | ||
logErrors: false | ||
}; | ||
@@ -136,6 +160,10 @@ | ||
* @param {String} filePath | ||
* @example | ||
* './src/materials/structures/foo.html' -> 'foo' | ||
* './src/materials/structures/02-bar.html' -> 'bar' | ||
* @return {String} | ||
*/ | ||
var getFileName = function (filePath) { | ||
return path.basename(filePath).replace(/\.[^\/.]+$/, ''); | ||
var getName = function (filePath, preserveNumbers) { | ||
var name = path.basename(filePath, path.extname(filePath)); | ||
return (preserveNumbers) ? name : name.replace(/^[0-9|\.\-]+/, ''); | ||
}; | ||
@@ -145,2 +173,51 @@ | ||
/** | ||
* Attempt to read front matter, handle errors | ||
* @param {String} file Path to file | ||
* @return {Object} | ||
*/ | ||
var getMatter = function (file) { | ||
return matter.read(file, { | ||
parser: require('js-yaml').safeLoad | ||
}); | ||
}; | ||
/** | ||
* Handle errors | ||
* @param {Object} e Error object | ||
*/ | ||
var handleError = function (e) { | ||
// default to exiting process on error | ||
var exit = true; | ||
// construct error object by combining argument with defaults | ||
var error = _.assign({}, { | ||
name: 'Error', | ||
reason: '', | ||
message: 'An error occurred', | ||
}, e); | ||
// call onError | ||
if (_.isFunction(options.onError)) { | ||
options.onError(error); | ||
exit = false; | ||
} | ||
// log errors | ||
if (options.logErrors) { | ||
console.error(chalk.bold.red('Error (fabricator-assemble): ' + e.message)); | ||
exit = false; | ||
} | ||
// break the build if desired | ||
if (exit) { | ||
console.error(chalk.bold.red('Error (fabricator-assemble): ' + e.message)); | ||
process.exit(1); | ||
} | ||
}; | ||
/** | ||
* Build the template context by merging context-specific data with assembly data | ||
@@ -151,3 +228,15 @@ * @param {Object} data | ||
var buildContext = function (data) { | ||
return _.assign({}, data, assembly.data, assembly.materialData, { materials: assembly.materials }, { views: assembly.views }, { docs: assembly.docs }); | ||
// set keys to whatever is defined | ||
var materials = {}; | ||
materials[options.keys.materials] = assembly.materials; | ||
var views = {}; | ||
views[options.keys.views] = assembly.views; | ||
var docs = {}; | ||
docs[options.keys.docs] = assembly.docs; | ||
return _.assign({}, data, assembly.data, assembly.materialData, materials, views, docs); | ||
}; | ||
@@ -157,2 +246,14 @@ | ||
/** | ||
* Convert a file name to title case | ||
* @param {String} str | ||
* @return {String} | ||
*/ | ||
var toTitleCase = function(str) { | ||
return str.replace(/(\-|_)/g, ' ').replace(/\w\S*/g, function(word) { | ||
return word.charAt(0).toUpperCase() + word.substr(1).toLowerCase(); | ||
}); | ||
}; | ||
/** | ||
* Insert the page into a layout | ||
@@ -188,4 +289,4 @@ * @param {String} page | ||
var parent = path.normalize(path.dirname(file)).split(path.sep).slice(-2, -1)[0]; | ||
var collection = path.normalize(path.dirname(file)).split(path.sep).pop(); | ||
var parent = getName(path.normalize(path.dirname(file)).split(path.sep).slice(-2, -1)[0]); | ||
var collection = getName(path.normalize(path.dirname(file)).split(path.sep).pop()); | ||
var isSubCollection = (dirs.indexOf(parent) > -1); | ||
@@ -195,3 +296,3 @@ | ||
assembly.materials[collection] = assembly.materials[collection] || { | ||
name: changeCase.titleCase(collection), | ||
name: toTitleCase(collection), | ||
items: {} | ||
@@ -201,3 +302,3 @@ }; | ||
assembly.materials[parent].items[collection] = assembly.materials[parent].items[collection] || { | ||
name: changeCase.titleCase(collection), | ||
name: toTitleCase(collection), | ||
items: {} | ||
@@ -214,7 +315,8 @@ }; | ||
// get info | ||
var fileMatter = matter.read(file); | ||
var collection = path.normalize(path.dirname(file)).split(path.sep).pop(); | ||
var fileMatter = getMatter(file); | ||
var collection = getName(path.normalize(path.dirname(file)).split(path.sep).pop()); | ||
var parent = path.normalize(path.dirname(file)).split(path.sep).slice(-2, -1)[0]; | ||
var isSubCollection = (dirs.indexOf(parent) > -1); | ||
var id = (isSubCollection) ? collection + '.' + getFileName(file) : getFileName(file); | ||
var id = (isSubCollection) ? collection + '.' + getName(file) : getName(file); | ||
var key = (isSubCollection) ? collection + '.' + getName(file, true) : getName(file, true); | ||
@@ -230,9 +332,9 @@ // get material front-matter, omit `notes` | ||
if (!isSubCollection) { | ||
assembly.materials[collection].items[id] = { | ||
name: changeCase.titleCase(id), | ||
assembly.materials[collection].items[key] = { | ||
name: toTitleCase(id), | ||
notes: (fileMatter.data.notes) ? md.render(fileMatter.data.notes) : '' | ||
}; | ||
} else { | ||
assembly.materials[parent].items[collection].items[id] = { | ||
name: changeCase.titleCase(id.split('.')[1]), | ||
assembly.materials[parent].items[collection].items[key] = { | ||
name: toTitleCase(id.split('.')[1]), | ||
notes: (fileMatter.data.notes) ? md.render(fileMatter.data.notes) : '' | ||
@@ -260,3 +362,2 @@ }; | ||
// register the partial | ||
@@ -269,6 +370,6 @@ Handlebars.registerPartial(id, content); | ||
// sort materials object alphabetically | ||
assembly.materials = sortObj(assembly.materials); | ||
assembly.materials = sortObj(assembly.materials, 'order'); | ||
for (var collection in assembly.materials) { | ||
assembly.materials[collection].items = sortObj(assembly.materials[collection].items); | ||
assembly.materials[collection].items = sortObj(assembly.materials[collection].items, 'order'); | ||
} | ||
@@ -293,7 +394,7 @@ | ||
var id = getFileName(file); | ||
var id = getName(file); | ||
// save each as unique prop | ||
assembly.docs[id] = { | ||
name: changeCase.titleCase(id), | ||
name: toTitleCase(id), | ||
content: md.render(fs.readFileSync(file, 'utf-8')) | ||
@@ -320,3 +421,3 @@ }; | ||
files.forEach(function (file) { | ||
var id = getFileName(file); | ||
var id = getName(file); | ||
var content = fs.readFileSync(file, 'utf-8'); | ||
@@ -339,3 +440,3 @@ assembly.layouts[id] = content; | ||
files.forEach(function (file) { | ||
var id = getFileName(file); | ||
var id = getName(file); | ||
var content = fs.readFileSync(file, 'utf-8'); | ||
@@ -361,3 +462,3 @@ Handlebars.registerPartial(id, content); | ||
files.forEach(function (file) { | ||
var id = getFileName(file); | ||
var id = getName(file); | ||
var content = yaml.safeLoad(fs.readFileSync(file, 'utf-8')); | ||
@@ -383,3 +484,3 @@ assembly.data[id] = content; | ||
var id = getFileName(file); | ||
var id = getName(file); | ||
@@ -390,3 +491,3 @@ // determine if view is part of a collection (subdir) | ||
var fileMatter = matter.read(file), | ||
var fileMatter = getMatter(file), | ||
fileData = _.omit(fileMatter.data, 'notes'); | ||
@@ -399,3 +500,3 @@ | ||
assembly.views[collection] = assembly.views[collection] || { | ||
name: changeCase.titleCase(collection), | ||
name: toTitleCase(collection), | ||
items: {} | ||
@@ -406,3 +507,3 @@ }; | ||
assembly.views[collection].items[id] = { | ||
name: changeCase.titleCase(id), | ||
name: toTitleCase(id), | ||
data: fileData | ||
@@ -451,12 +552,18 @@ }; | ||
* @description Like a normal partial include (`{{> partialName }}`), | ||
* but with some additional templating logic to help with nested block iterations | ||
* but with some additional templating logic to help with nested block iterations. | ||
* The name of the helper is the singular form of whatever is defined as the `options.keys.materials` | ||
* @example | ||
* {{material name context}} | ||
*/ | ||
Handlebars.registerHelper('material', function (name, context) { | ||
Handlebars.registerHelper(inflect.singularize(options.keys.materials), function (name, context) { | ||
var template = Handlebars.partials[name], | ||
// remove leading numbers from name keyword | ||
// partials are always registered with the leading numbers removed | ||
var key = name.replace(/^([a-z][a-z0-9]*\.)?([0-9\.-]+)(.*)$/i, '$1$3'); | ||
// attempt to find pre-compiled partial | ||
var template = Handlebars.partials[key], | ||
fn; | ||
// check to see if template is already compiled | ||
// compile partial if not already compiled | ||
if (!_.isFunction(template)) { | ||
@@ -468,2 +575,3 @@ fn = Handlebars.compile(template); | ||
// return beautified html with trailing whitespace removed | ||
return beautifyHtml(fn(buildContext(context)).replace(/^\s+/, ''), options.beautifier); | ||
@@ -483,3 +591,3 @@ | ||
// merge user options with defaults | ||
options = _.assign({}, defaults, userOptions); | ||
options = _.merge({}, defaults, userOptions); | ||
@@ -512,3 +620,3 @@ // setup steps | ||
var id = getFileName(file); | ||
var id = getName(file); | ||
@@ -521,3 +629,3 @@ // build filePath | ||
// get page gray matter and content | ||
var pageMatter = matter(fs.readFileSync(file, 'utf-8')), | ||
var pageMatter = getMatter(file), | ||
pageContent = pageMatter.content; | ||
@@ -549,8 +657,14 @@ | ||
// setup assembly | ||
setup(options); | ||
try { | ||
// assemble | ||
assemble(); | ||
// setup assembly | ||
setup(options); | ||
// assemble | ||
assemble(); | ||
} catch(e) { | ||
handleError(e); | ||
} | ||
}; |
{ | ||
"name": "fabricator-assemble", | ||
"version": "1.0.6", | ||
"version": "1.1.0", | ||
"description": "The assembly engine behind Fabricator", | ||
@@ -20,11 +20,11 @@ "main": "index.js", | ||
"dependencies": { | ||
"change-case": "^2.2.0", | ||
"globby": "^1.2.0", | ||
"gray-matter": "^1.2.6", | ||
"handlebars": "^2.0.0", | ||
"html-minifier": "^0.6.9", | ||
"chalk": "^1.0.0", | ||
"globby": "^2.0.0", | ||
"gray-matter": "^2.0.0", | ||
"handlebars": "^3.0.3", | ||
"i": "^0.3.3", | ||
"js-beautify": "^1.5.5", | ||
"js-yaml": "^3.2.7", | ||
"lodash": "^2.4.1", | ||
"markdown-it": "^3.1.0", | ||
"lodash": "^3.8.0", | ||
"markdown-it": "^4.2.1", | ||
"mkdirp": "^0.5.0", | ||
@@ -36,4 +36,5 @@ "sort-object": "^1.0.0" | ||
"helper-markdown": "^0.1.1", | ||
"html-minifier": "^0.7.2", | ||
"mocha": "^2.1.0" | ||
} | ||
} |
@@ -81,3 +81,10 @@ # Fabricator Assemble | ||
docs: 'src/docs/**/*.md', | ||
keys: { | ||
materials: 'materials', | ||
views: 'views', | ||
docs: 'docs' | ||
} | ||
helpers: {}, | ||
logErrors: false, | ||
onError: function(error) {}, | ||
dest: 'dist' | ||
@@ -136,5 +143,37 @@ } | ||
### options.keys | ||
Type: `Objects` | ||
Default: `materials/views/docs` | ||
Object keywords for accessing "materials", "views", and "docs" in a view templating context. Fabricator uses some specific terms like "materials" to describe what are really "partials" in Handelbars. This option give you the flexibility to define your own terms for `materials`, `views`, and `docs`. | ||
For example: | ||
``` | ||
assemble({ | ||
keys: { | ||
materials: 'patterns' | ||
} | ||
}); | ||
``` | ||
``` | ||
<!-- formerly `{{#each materials}}` --> | ||
{{#each patterns}} | ||
<h1>{{name}}</h1> | ||
{{#each items}} | ||
<h2>{{name}}</h2> | ||
{{/each}} | ||
{{/each}} | ||
``` | ||
**Note**: this will also change the built-in `{{material <foo>}}` helper to use the **singular** form of whatever is defined for the `materials` key. e.g. `materialKey: 'patterns'` -> `{{pattern <foo>}}`. If you set a new key for `materials`, you will also need to update the `f-item-content.html` include to use the new helper name. | ||
### options.helpers | ||
Type: `Object` | ||
Type: `Object` | ||
Default: `{}` | ||
@@ -153,5 +192,19 @@ | ||
### options.logErrors | ||
Type: `Boolean` | ||
Default: `false` | ||
Whether or not to log errors to console. If set to false, the app will exit on error. | ||
### options.onError | ||
Type: `Function` | ||
Default: `null` | ||
Error handler function. Receives an `error` object param. | ||
### options.dest | ||
Type: `String` | ||
Type: `String` | ||
Default: `dist` | ||
@@ -161,3 +214,3 @@ | ||
## API | ||
## Usage | ||
@@ -260,2 +313,30 @@ ### Definitions | ||
#### Ordering | ||
You can manually order materials by prefixing the file name with numbers: | ||
``` | ||
01-foo.html | ||
01.01-bar.html | ||
02-qux.html | ||
``` | ||
This defines the order in which materials will appear in the side menu or other places the `materials.items` context is used. | ||
**Note**: The number prefixes are ignored when registering partials, so you'll still be able to access them using the material name per usual. e.g.: | ||
``` | ||
{{> foo}} | ||
{{> bar}} | ||
{{> qux}} | ||
``` | ||
The numbers are also ignored in the `.name` property. The materials above would list as: | ||
``` | ||
Foo | ||
Bar | ||
Qux | ||
``` | ||
#### Data | ||
@@ -262,0 +343,0 @@ |
@@ -0,1 +1,2 @@ | ||
var _ = require('lodash'); | ||
var assert = require('assert'); | ||
@@ -21,3 +22,4 @@ var assemble = require('../'); | ||
markdown: require('helper-markdown') | ||
} | ||
}, | ||
logErrors: true | ||
}; | ||
@@ -95,2 +97,18 @@ | ||
it('should use a custom material key', function (done) { | ||
assemble(_.assign({}, options, { | ||
keys: { | ||
materials: 'patterns' | ||
} | ||
})); | ||
var output = minify(fs.readFileSync('./test/output/material-key.html', 'utf-8'), { collapseWhitespace: true }); | ||
var expected = minify(fs.readFileSync('./test/expected/material-key.html', 'utf-8'), { collapseWhitespace: true }); | ||
assert.equal(output, expected); | ||
done(); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
32765
32
588
369
4
+ Addedchalk@^1.0.0
+ Addedi@^0.3.3
+ Addedalign-text@0.1.4(transitive)
+ Addedansi-red@0.1.1(transitive)
+ Addedansi-wrap@0.1.0(transitive)
+ Addedasync@1.5.2(transitive)
+ Addedcenter-align@0.1.3(transitive)
+ Addedcliui@2.1.0(transitive)
+ Addedcoffee-script@1.12.7(transitive)
+ Addedentities@1.1.2(transitive)
+ Addedextend-shallow@2.0.1(transitive)
+ Addedglob@5.0.15(transitive)
+ Addedglobby@2.1.0(transitive)
+ Addedgray-matter@2.1.1(transitive)
+ Addedhandlebars@3.0.8(transitive)
+ Addedi@0.3.7(transitive)
+ Addedis-buffer@1.1.6(transitive)
+ Addedis-extendable@0.1.1(transitive)
+ Addedkind-of@3.2.2(transitive)
+ Addedlazy-cache@1.0.4(transitive)
+ Addedlinkify-it@1.2.4(transitive)
+ Addedlodash@3.10.1(transitive)
+ Addedlongest@1.0.1(transitive)
+ Addedmarkdown-it@4.4.0(transitive)
+ Addedminimatch@3.1.2(transitive)
+ Addedminimist@0.0.10(transitive)
+ Addedobject-assign@3.0.0(transitive)
+ Addedoptimist@0.6.1(transitive)
+ Addedpath-is-absolute@1.0.1(transitive)
+ Addedrepeat-string@1.6.1(transitive)
+ Addedright-align@0.1.3(transitive)
+ Addedsource-map@0.1.430.5.7(transitive)
+ Addedtoml@2.3.6(transitive)
+ Addeduglify-js@2.8.29(transitive)
+ Addedyargs@3.10.0(transitive)
- Removedchange-case@^2.2.0
- Removedhtml-minifier@^0.6.9
- Removedasync@0.2.100.9.2(transitive)
- Removedcamel-case@1.2.2(transitive)
- Removedchange-case@2.1.62.3.1(transitive)
- Removedclean-css@2.2.23(transitive)
- Removedcli@0.6.6(transitive)
- Removedcommander@2.2.0(transitive)
- Removedconstant-case@1.1.2(transitive)
- Removeddot-case@1.1.2(transitive)
- Removedexit@0.1.2(transitive)
- Removedextend-shallow@1.1.4(transitive)
- Removedglob@3.2.114.5.3(transitive)
- Removedglobby@1.2.0(transitive)
- Removedgray-matter@1.3.0(transitive)
- Removedhandlebars@2.0.0(transitive)
- Removedhtml-minifier@0.6.9(transitive)
- Removedis-lower-case@1.1.3(transitive)
- Removedis-upper-case@1.1.2(transitive)
- Removedkind-of@1.1.0(transitive)
- Removedlinkify-it@0.1.5(transitive)
- Removedlodash@2.4.2(transitive)
- Removedlower-case@1.1.4(transitive)
- Removedlower-case-first@1.0.2(transitive)
- Removedlru-cache@2.7.3(transitive)
- Removedmarkdown-it@3.1.0(transitive)
- Removedminimatch@0.3.02.0.10(transitive)
- Removedobject-assign@2.1.1(transitive)
- Removedoptimist@0.3.7(transitive)
- Removedparam-case@1.1.2(transitive)
- Removedpascal-case@1.1.2(transitive)
- Removedpath-case@1.1.2(transitive)
- Removedrelateurl@0.2.7(transitive)
- Removedsentence-case@1.1.3(transitive)
- Removedsigmund@1.0.1(transitive)
- Removedsnake-case@1.1.2(transitive)
- Removedsource-map@0.1.34(transitive)
- Removedswap-case@1.1.2(transitive)
- Removedtitle-case@1.1.2(transitive)
- Removeduc.micro@0.1.0(transitive)
- Removeduglify-js@2.3.62.4.24(transitive)
- Removedupper-case@1.1.3(transitive)
- Removedupper-case-first@1.1.2(transitive)
- Removedyargs@3.5.4(transitive)
Updatedglobby@^2.0.0
Updatedgray-matter@^2.0.0
Updatedhandlebars@^3.0.3
Updatedlodash@^3.8.0
Updatedmarkdown-it@^4.2.1