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) { %>

<%= category %>

- <% componentsForNav[category].map(function(componentForNav) { %> - <%= componentForNav.name %> + <% componentsForNav[category].map(function(component) { %> + <%= component.name %> <% }) %>
<% }) %> 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 %>

- <% componentsForNav[category].map(function(componentForNav) { %> + <% componentsForNav[category].map(function(component) { + const frontMatter = _.merge({}, options.components.frontMatter, component.frontMatter); + %> -
+ href="<%= component.href %>" + title="<%= component.name %>"> +
+ data-bg="<%= component.screenshot.href %>" + style="background-repeat: no-repeat; + background-position: <%= frontMatter.screenshot['background-position'] %>; + background-size: <%= frontMatter.screenshot['background-size'] %>;"> +

- <%= componentForNav.name %> + <%= component.name %>

<% }) %> diff --git a/src/templates/components-nav-template.html b/src/templates/components-nav-template.html index f8b667250..01eeea5fb 100644 --- a/src/templates/components-nav-template.html +++ b/src/templates/components-nav-template.html @@ -3,8 +3,8 @@

Other Components

<% Object.keys(componentsForNav).map(function(category) { %>

<%= category %>

- <% componentsForNav[category].map(function(componentForNav) { %> - <%= componentForNav.name %> + <% componentsForNav[category].map(function(component) { %> + <%= component.name %> <% }) %>
<% }) %>