From 0bad23d2db48e1fc6d4acd1c8eb8961fcf4d6e39 Mon Sep 17 00:00:00 2001
From: Sebastien Barre
Date: Mon, 12 Dec 2016 19:07:17 -0500
Subject: [PATCH] factorize component frontmatter defaults
---
build.js | 13 +++-
src/components-build-defaults.js | 61 ++++++++++++-------
src/components-build-index.js | 40 ++++++------
src/components-build-pages.js | 21 +++----
src/components-build-screenshots.js | 38 ++++++------
.../{components.html => component.html} | 6 +-
src/templates/components-index.html | 19 +++---
src/templates/components-nav-template.html | 4 +-
8 files changed, 117 insertions(+), 85 deletions(-)
rename src/templates/{components.html => component.html} (92%)
diff --git a/build.js b/build.js
index 38350f9ef..a5b5802f0 100644
--- a/build.js
+++ b/build.js
@@ -21,7 +21,18 @@ const componentsBuildScreenshots = require('./src/components-build-screenshots')
// See src/components-build-defaults for list of options that can be overriden
const options = {
- // componentsGlobPattern: 'src/components/buttons/*.html',
+ components: {
+ globPattern: 'src/components/banners/*.html',
+ // frontMatter: {
+ // bodyClass: 'bg-red',
+ // screenshot: {
+ // autocrop: false,
+ // },
+ // },
+ },
+ // screenshot: {
+ // aspectRatio: '16x9',
+ // },
};
// Note that componentsBuildIndex() generates the index *and* the JSON
diff --git a/src/components-build-defaults.js b/src/components-build-defaults.js
index 56c4d167a..bbceca91e 100644
--- a/src/components-build-defaults.js
+++ b/src/components-build-defaults.js
@@ -1,29 +1,46 @@
module.exports = {
// Components
- componentsForNavPath: 'tmp/componentsForNav.json', // temporary file built by the index
- componentsGlobPattern: 'src/components/**/*.html', // source components to process
- componentsIndexPath: 'components/index.html', // target location of components index
- componentsBuildPages: true, // false to skip building pages
- componentsBuildScreenshots: true, // false to skip building screenshots
- // Screenshots
- screenshotName: 'screenshot.jpg', // name JPEG screenshot in each component dir
- screenshotAspectRatio: '4x3', // Tachyon aspect ratio of screenshot in index
- screenshotViewportWidth: 1024, // viewport width used for capture
- screenshotViewportHeight: 768, // viewport height used for capture
- screenshotTargetMinWidth: 400, // min width of target, resized screenshot
- screenshotTargetMinHeight: 160, // min height of target, resized screenshot
- mozjpegQuality: 90, // mozjpeg optimizer quality (default 75)
- screenshotSelector: '[data-name="component-container"]', // DOM element to capture
+ components: {
+ globPattern: 'src/components/**/*.html', // source components to process
+ forNavPath: 'tmp/componentsForNav.json', // temporary file built by the index
+ indexPath: 'components/index.html', // target location of components index
+ buildPages: true, // false to skip building pages
+ buildScreenshots: true, // false to skip building screenshots
+ frontMatter: { // font matter defaults (i.e. optional)
+ name: undefined, // undefined to infer component name
+ title: undefined, // undefined to infer component title
+ bodyClass: 'bg-white', // class to apply on
+ screenshot: { // per-component screenshot options
+ selector: '[data-name="component"]', // DOM element to capture
+ autocrop: true, // autocrop the capture
+ 'background-position': 'center center', // CSS background position
+ 'background-size': 'cover', // CSS background size ('cover', 'contain', 'auto')
+ },
+ },
+ },
+ // Screenshot options
+ // (components only for now, but could apply to other areas for consistency)
+ screenshot: {
+ basename: 'screenshot.jpg', // name of *JPEG* screenshot
+ aspectRatio: '4x3', // Tachyons aspect ratio of screenshots
+ viewportWidth: 1024, // viewport width used for capture
+ viewportHeight: 768, // viewport height used for capture
+ targetMinWidth: 400, // min width of target (final) screenshot
+ targetMinHeight: 160, // min height of target (final) screenshot
+ mozjpegQuality: 90, // mozjpeg optimizer quality (default 75)
+ },
// Misc
tachyonsCssPath: 'src/css/tachyons.css',
serverPort: 3333,
// Templates
- analyticsTemplatePath: 'src/templates/ga.html',
- componentsIndexTemplatePath: 'src/templates/components-index.html',
- componentsTemplatePath: 'src/templates/components.html',
- footerTemplatePath: 'src/templates/footer.html',
- headerTemplatePath: 'src/templates/header.html',
- headTemplatePath: 'src/templates/head.html',
- highlightTemplatePath: 'src/templates/highlight.html',
- lazysizesTemplate: 'src/templates/lazysizes.html',
+ templates: {
+ analyticsPath: 'src/templates/ga.html',
+ componentsIndexPath: 'src/templates/components-index.html',
+ componentPath: 'src/templates/component.html',
+ footerPath: 'src/templates/footer.html',
+ headerPath: 'src/templates/header.html',
+ headPath: 'src/templates/head.html',
+ highlightPath: 'src/templates/highlight.html',
+ lazysizesPath: 'src/templates/lazysizes.html',
+ },
};
diff --git a/src/components-build-index.js b/src/components-build-index.js
index 92215ddf6..002fc79fd 100644
--- a/src/components-build-index.js
+++ b/src/components-build-index.js
@@ -20,9 +20,9 @@ const getTitle = (component) => {
const getName = component => titleize(getTitle(component.split('/')[3]));
module.exports = _options => new Promise((resolve, reject) => {
- const options = _.assign({}, defaults, _options);
+ const options = _.merge({}, defaults, _options);
const startTime = process.hrtime();
- glob(options.componentsGlobPattern, {}, (err, components) => {
+ glob(options.components.globPattern, {}, (err, components) => {
console.log(chalk.magenta('Working on components index...'));
if (err) {
reject(err);
@@ -38,7 +38,8 @@ module.exports = _options => new Promise((resolve, reject) => {
const componentHtml = fs.readFileSync(component, 'utf8');
const fmParsed = fm.parse(componentHtml);
- const frontMatter = fmParsed.attributes || {};
+ const srcFrontMatter = fmParsed.attributes || {};
+ const frontMatter = _.merge({}, options.components.frontMatter, srcFrontMatter);
const dir = component.replace('src/', '').replace('.html', '');
// Compute component signature based on the Tachyons version and the contents of the
@@ -56,11 +57,14 @@ module.exports = _options => new Promise((resolve, reject) => {
path: `${dir}/index.html`,
href: `/${dir}/index.html`,
screenshot: {
- path: `${dir}/${options.screenshotName}`,
- href: `/${dir}/${options.screenshotName}?version=${signature}`,
+ path: `${dir}/${options.screenshot.basename}`,
+ href: `/${dir}/${options.screenshot.basename}?version=${signature}`,
},
signature,
- frontMatter,
+ // This is the raw front matter, as found in the component source, NOT merged
+ // with any defaults, so that it is easier to spot the overrides.
+ // It is up to build scripts to merge with options.components.frontMatter down the road.
+ frontMatter: srcFrontMatter,
});
});
@@ -70,16 +74,16 @@ module.exports = _options => new Promise((resolve, reject) => {
'in', categories.length, categories.length > 1 ? 'categories' : 'category'
);
- mkdirp.sync(path.dirname(options.componentsForNavPath));
- fs.writeFileSync(options.componentsForNavPath, JSON.stringify(componentsForNav, undefined, 2));
- console.log('- Created navigation JSON:', options.componentsForNavPath);
+ mkdirp.sync(path.dirname(options.components.forNavPath));
+ fs.writeFileSync(options.components.forNavPath, JSON.stringify(componentsForNav, undefined, 2));
+ console.log('- Created navigation JSON:', options.components.forNavPath);
- const analytics = fs.readFileSync(options.analyticsTemplatePath, 'utf8');
- const footer = fs.readFileSync(options.footerTemplatePath, 'utf8');
- const head = fs.readFileSync(options.headTemplatePath, 'utf8');
- const header = fs.readFileSync(options.headerTemplatePath, 'utf8');
- const componentsIndexTemplate = fs.readFileSync(options.componentsIndexTemplatePath, 'utf8');
- const lazysizesTemplate = fs.readFileSync(options.lazysizesTemplate, 'utf8');
+ const analytics = fs.readFileSync(options.templates.analyticsPath, 'utf8');
+ const footer = fs.readFileSync(options.templates.footerPath, 'utf8');
+ const head = fs.readFileSync(options.templates.headPath, 'utf8');
+ const header = fs.readFileSync(options.templates.headerPath, 'utf8');
+ const componentsIndexTemplate = fs.readFileSync(options.templates.componentsIndexPath, 'utf8');
+ const lazysizesTemplate = fs.readFileSync(options.templates.lazysizesPath, 'utf8');
const compiledPage = _.template(componentsIndexTemplate)({
componentsForNav,
@@ -91,9 +95,9 @@ module.exports = _options => new Promise((resolve, reject) => {
lazysizesTemplate,
options,
});
- mkdirp.sync(path.dirname(options.componentsIndexPath));
- fs.writeFileSync(options.componentsIndexPath, compiledPage);
- console.log('- Created index:', options.componentsIndexPath);
+ mkdirp.sync(path.dirname(options.components.indexPath));
+ fs.writeFileSync(options.components.indexPath, compiledPage);
+ console.log('- Created index:', options.components.indexPath);
const elapsed = process.hrtime(startTime);
console.log(chalk.magenta('Done with components index!'), chalk.dim(prettyHrtime(elapsed)));
diff --git a/src/components-build-pages.js b/src/components-build-pages.js
index a8aff16c3..2c0269184 100644
--- a/src/components-build-pages.js
+++ b/src/components-build-pages.js
@@ -23,25 +23,25 @@ const select = require('postcss-select');
const defaults = require('./components-build-defaults');
module.exports = _options => new Promise((resolve, reject) => {
- const options = _.assign({}, defaults, _options);
+ const options = _.merge({}, defaults, _options);
const startTime = process.hrtime();
console.log(chalk.magenta('Working on components pages...'));
- if (options.componentsForNavPath === undefined || !fs.existsSync(options.componentsForNavPath)) {
+ if (options.components.forNavPath === undefined || !fs.existsSync(options.components.forNavPath)) {
reject('Can not find components nav JSON file');
return;
}
- if (!options.componentsBuildPages) {
+ if (!options.components.buildPages) {
console.log(chalk.dim('Skipped by request.'));
resolve();
return;
}
- const componentsForNav = JSON.parse(fs.readFileSync(options.componentsForNavPath, 'utf8'));
+ const componentsForNav = JSON.parse(fs.readFileSync(options.components.forNavPath, 'utf8'));
- const componentTemplate = fs.readFileSync(options.componentsTemplatePath, 'utf8');
- const analytics = fs.readFileSync(options.analyticsTemplatePath, 'utf8');
- const footer = fs.readFileSync(options.footerTemplatePath, 'utf8');
- const head = fs.readFileSync(options.headTemplatePath, 'utf8');
- const highlight = fs.readFileSync(options.highlightTemplatePath, 'utf8');
+ const componentTemplate = fs.readFileSync(options.templates.componentPath, 'utf8');
+ const analytics = fs.readFileSync(options.templates.analyticsPath, 'utf8');
+ const footer = fs.readFileSync(options.templates.footerPath, 'utf8');
+ const head = fs.readFileSync(options.templates.headPath, 'utf8');
+ const highlight = fs.readFileSync(options.templates.highlightPath, 'utf8');
const tachyonsCss = fs.readFileSync(options.tachyonsCssPath, 'utf8');
@@ -57,10 +57,9 @@ module.exports = _options => new Promise((resolve, reject) => {
const componentHtml = fs.readFileSync(component.src, 'utf8');
const fmParsed = fm.parse(componentHtml);
- const frontMatter = _.assign({}, component.frontMatter);
+ const frontMatter = _.merge({}, options.components.frontMatter, component.frontMatter);
frontMatter.title = component.title;
frontMatter.name = component.name;
- frontMatter.bodyClass = frontMatter.bodyClass || '';
frontMatter.classes = getClasses(fmParsed.body).map(klass => `.${klass}`);
frontMatter.componentHtml = componentHtml;
frontMatter.content = fmParsed.body;
diff --git a/src/components-build-screenshots.js b/src/components-build-screenshots.js
index 556519f81..b1f83434e 100644
--- a/src/components-build-screenshots.js
+++ b/src/components-build-screenshots.js
@@ -40,19 +40,19 @@ const initNightmare = () => Nightmare({ // eslint-disable-line
const formatFromSizeToSize = (from, to) => `(${filesize(from)} => ${filesize(to)})`;
module.exports = _options => new Promise((resolve, reject) => {
- const options = _.assign({}, defaults, _options);
+ const options = _.merge({}, defaults, _options);
const startTime = process.hrtime();
console.log(chalk.magenta('Working on components screenshots...'));
- if (options.componentsForNavPath === undefined || !fs.existsSync(options.componentsForNavPath)) {
+ if (options.components.forNavPath === undefined || !fs.existsSync(options.components.forNavPath)) {
reject('Can not find components nav JSON file');
return;
}
- if (!options.componentsBuildScreenshots) {
+ if (!options.components.buildScreenshots) {
console.log(chalk.dim('Skipped by request.'));
resolve();
return;
}
- const componentsForNav = JSON.parse(fs.readFileSync(options.componentsForNavPath, 'utf8'));
+ const componentsForNav = JSON.parse(fs.readFileSync(options.components.forNavPath, 'utf8'));
const nightmare = initNightmare();
@@ -70,18 +70,15 @@ module.exports = _options => new Promise((resolve, reject) => {
for (let comp_idx = 0; comp_idx < componentsForNav[category].length; comp_idx += 1) {
const component = componentsForNav[category][comp_idx];
- let selector = options.screenshotSelector;
- if (component.frontMatter.screenshot && component.frontMatter.screenshot.selector) {
- selector = `${selector} ${component.frontMatter.screenshot.selector}`;
- }
+ const frontMatter = _.merge({}, options.components.frontMatter, component.frontMatter);
// Grab the size of the component enclosing rectangle
// https://github.com/segmentio/nightmare/issues/498#issuecomment-189156529
const componentRect = yield nightmare
- .viewport(options.screenshotViewportWidth, options.screenshotViewportHeight)
+ .viewport(options.screenshot.viewportWidth, options.screenshot.viewportHeight)
// .wait(1000)
.goto(`http://localhost:${options.serverPort}${component.href}`)
- .wait(selector)
+ .wait(frontMatter.screenshot.selector)
.evaluate((_selector) => {
// Hide scrollbar that could pop up due to .scrollTo
const sheet = document.styleSheets[0];
@@ -97,7 +94,7 @@ module.exports = _options => new Promise((resolve, reject) => {
};
}
return false;
- }, selector);
+ }, frontMatter.screenshot.selector);
// Capture the component
if (componentRect === false) {
@@ -107,12 +104,12 @@ module.exports = _options => new Promise((resolve, reject) => {
const screenshotDir = path.dirname(component.screenshot.path);
const tmpPngObj = tmp.fileSync({ dir: screenshotDir });
- componentRect.height = Math.min(componentRect.height, options.screenshotViewportHeight);
+ componentRect.height = Math.min(componentRect.height, options.screenshot.viewportHeight);
yield nightmare
// we can not use .screenshot() with componentRect, so constrain the viewport instead
.viewport(
- componentRect.width || options.screenshotViewportWidth,
- componentRect.height || options.screenshotViewportHeight
+ componentRect.width || options.screenshot.viewportWidth,
+ componentRect.height || options.screenshot.viewportHeight
).scrollTo(componentRect.y, componentRect.x)
// .wait(1000)
.screenshot(tmpPngObj.name); // do *not* use componentRect here or risk distortions
@@ -122,15 +119,14 @@ module.exports = _options => new Promise((resolve, reject) => {
const tmpJpegPath = path.join(tmpJpegDirObj.name, path.basename(component.screenshot.path));
const screenshot = yield Jimp.read(tmpPngObj.name);
yield new Promise((write_resolve, write_reject) => {
- if (component.frontMatter.screenshot === undefined ||
- component.frontMatter.screenshot.autocrop !== false) {
+ if (frontMatter.screenshot.autocrop) {
screenshot.autocrop(false);
}
// Allow shrinking, up to a point
- const scaleHeight = screenshot.bitmap.height <= options.screenshotTargetMinHeight
- ? 0.0 : options.screenshotTargetMinHeight / screenshot.bitmap.height;
- const scaleWidth = screenshot.bitmap.width <= options.screenshotTargetMinWidth
- ? 0.0 : options.screenshotTargetMinWidth / screenshot.bitmap.width;
+ const scaleHeight = screenshot.bitmap.height <= options.screenshot.targetMinHeight
+ ? 0.0 : options.screenshot.targetMinHeight / screenshot.bitmap.height;
+ const scaleWidth = screenshot.bitmap.width <= options.screenshot.targetMinWidth
+ ? 0.0 : options.screenshot.targetMinWidth / screenshot.bitmap.width;
const scale = Math.max(scaleHeight, scaleWidth);
screenshot
.scale(scale > 0 ? scale : 1.0)
@@ -142,7 +138,7 @@ module.exports = _options => new Promise((resolve, reject) => {
// Optimize
imagemin([tmpJpegPath], screenshotDir, {
plugins: [
- imageminMozjpeg({ quality: options.mozjpegQuality }),
+ imageminMozjpeg({ quality: options.screenshot.mozjpegQuality }),
// imageminJpegRecompress(), // this guy is useless
// imageminJpegtran(), // this guy is useless
],
diff --git a/src/templates/components.html b/src/templates/component.html
similarity index 92%
rename from src/templates/components.html
rename to src/templates/component.html
index 9e1ae8f0b..330fa0dc0 100644
--- a/src/templates/components.html
+++ b/src/templates/component.html
@@ -7,7 +7,7 @@
-
+
<%= content %>
@@ -64,8 +64,8 @@ Other Components
<% Object.keys(componentsForNav).map(function(category) { %>
<% }) %>
diff --git a/src/templates/components-index.html b/src/templates/components-index.html
index 55fe6d0ba..a98ee5a8b 100644
--- a/src/templates/components-index.html
+++ b/src/templates/components-index.html
@@ -30,17 +30,22 @@ <%= category %>