Skip to content

factorize component frontmatter defaults #129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion build.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
61 changes: 39 additions & 22 deletions src/components-build-defaults.js
Original file line number Diff line number Diff line change
@@ -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 <body>
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',
},
};
40 changes: 22 additions & 18 deletions src/components-build-index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -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,
});
});

Expand All @@ -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,
Expand All @@ -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)));
Expand Down
21 changes: 10 additions & 11 deletions src/components-build-pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -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;
Expand Down
38 changes: 17 additions & 21 deletions src/components-build-screenshots.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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];
Expand All @@ -97,7 +94,7 @@ module.exports = _options => new Promise((resolve, reject) => {
};
}
return false;
}, selector);
}, frontMatter.screenshot.selector);

// Capture the component
if (componentRect === false) {
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</head>
<body class="w-100 sans-serif <%= bodyClass %>">
<main>
<div data-name="component-container">
<div data-name="component">
<%= content %>
</div>
<section data-name="component-info" class="pa3 pa5-ns bt b--black-10 black-70 bg-white">
Expand Down Expand Up @@ -64,8 +64,8 @@ <h1 class="f6 b ttu">Other Components</h1>
<% Object.keys(componentsForNav).map(function(category) { %>
<div>
<h2 class="f6 fw6 mb2 ttc black-70"><%= category %></h2>
<% componentsForNav[category].map(function(componentForNav) { %>
<a class="f6 f5-ns fw4 dib mr3 mb2 black-70 link hover-blue" href="<%= componentForNav.href %>"><%= componentForNav.name %></a>
<% componentsForNav[category].map(function(component) { %>
<a class="f6 f5-ns fw4 dib mr3 mb2 black-70 link hover-blue" href="<%= component.href %>"><%= component.name %></a>
<% }) %>
</div>
<% }) %>
Expand Down
19 changes: 12 additions & 7 deletions src/templates/components-index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,22 @@ <h3 class="f5 fw6 pb2 bb b--black-10 black-70 ttc"><%= category %></h3>
</div>
<div class="ph3 ph5-ns mb5">
<div class="mw9 center cf">
<% componentsForNav[category].map(function(componentForNav) { %>
<% componentsForNav[category].map(function(component) {
const frontMatter = _.merge({}, options.components.frontMatter, component.frontMatter);
%>
<a class="db pointer link underline-hover black-70 fl w-50 w-third-m w-25-l pb2 pr2 border-box"
href="<%= componentForNav.href %>"
title="<%= componentForNav.name %>">
<div class="ba b--black-20 aspect-ratio aspect-ratio--<%= options.screenshotAspectRatio %>">
href="<%= component.href %>"
title="<%= component.name %>">
<div class="ba b--black-20 aspect-ratio aspect-ratio--<%= options.screenshot.aspectRatio %>">
<div class="aspect-ratio--object lazyload"
data-bg="<%= componentForNav.screenshot.href %>"
style="background-repeat: no-repeat; background-position: <%= componentForNav.frontMatter.screenshot && componentForNav.frontMatter.screenshot['background-position'] || 'center center' %>; background-size: <%= componentForNav.frontMatter.screenshot && componentForNav.frontMatter.screenshot['background-size'] || 'cover' %>;"></div>
data-bg="<%= component.screenshot.href %>"
style="background-repeat: no-repeat;
background-position: <%= frontMatter.screenshot['background-position'] %>;
background-size: <%= frontMatter.screenshot['background-size'] %>;">
</div>
</div>
<p class="f6 truncate">
<%= componentForNav.name %>
<%= component.name %>
</p>
</a>
<% }) %>
Expand Down
Loading