Skip to content

Commit 6695c2c

Browse files
author
Brian Muenzenmeyer
committed
Annotations Support (#337)
* annotations exporter - built with TDD * annotations.md parsing with tests * merge both annotations formats. covers the majority of pattern-lab/edition-node-gulp#12
1 parent ea1b680 commit 6695c2c

File tree

5 files changed

+175
-5
lines changed

5 files changed

+175
-5
lines changed

core/lib/annotation_exporter.js

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@ var annotations_exporter = function (pl) {
44
var path = require('path'),
55
fs = require('fs-extra'),
66
JSON5 = require('json5'),
7+
_ = require('lodash'),
8+
md = require('markdown-it')(),
79
paths = pl.config.paths;
810

9-
// HELPER FUNCTIONS
11+
/*
12+
Returns the array of comments that used to be wrapped in raw JS.
13+
*/
1014
function parseAnnotationsJS() {
1115
//attempt to read the file
1216
try {
1317
var oldAnnotations = fs.readFileSync(path.resolve(paths.source.annotations, 'annotations.js'), 'utf8');
1418
} catch (ex) {
15-
console.log(ex, 'This may be expected.');
19+
console.log(ex, 'annotations.js file missing from ' + paths.source.annotations + '. This may be expected.');
1620
}
1721

1822
//parse as JSON by removing the old wrapping js syntax. comments and the trailing semi-colon
@@ -23,17 +27,81 @@ var annotations_exporter = function (pl) {
2327
console.log('There was an error parsing JSON for ' + paths.source.annotations + 'annotations.js');
2428
console.log(ex);
2529
}
26-
return oldAnnotationsJSON;
30+
return oldAnnotationsJSON.comments;
31+
}
32+
33+
/*
34+
Converts the annotations.md file yaml list into an array of annotations
35+
*/
36+
function parseAnnotationsMD() {
37+
var annotations = [];
38+
39+
//attempt to read the file
40+
var annotationsMD = '';
41+
try {
42+
annotationsMD = fs.readFileSync(path.resolve(paths.source.annotations, 'annotations.md'), 'utf8');
43+
} catch (ex) {
44+
console.log(ex, 'annotations.md file missing from ' + paths.source.annotations + '. This may be expected.');
45+
}
46+
47+
//take the annotation snippets and split them on our custom delimiter
48+
var annotationsYAML = annotationsMD.split('~*~');
49+
for (var i = 0; i < annotationsYAML.length; i++) {
50+
var annotation = {};
51+
52+
//for each annotation process the yaml frontmatter and markdown
53+
var annotationSnippet = annotationsYAML[i];
54+
var annotationsRE = /---\n{1}([\s\S]*)---\n{1}([\s\S]*)+/gm;
55+
var chunks = annotationsRE.exec(annotationSnippet);
56+
if (chunks && chunks[1] && chunks[2]) {
57+
58+
//convert each yaml frontmatter key into an object key
59+
var frontmatter = chunks[1];
60+
var frontmatterLines = frontmatter.split(/\n/gm);
61+
for (var j = 0; j < frontmatterLines.length; j++) {
62+
var frontmatterLine = frontmatterLines[j];
63+
if (frontmatterLine.length > 0) {
64+
var frontmatterLineChunks = frontmatterLine.split(':'); //test this
65+
var frontmatterKey = frontmatterLineChunks[0].toLowerCase().trim();
66+
var frontmatterValueString = frontmatterLineChunks[1].trim();
67+
var frontmatterValue = frontmatterValueString.substring(1, frontmatterValueString.length - 1);
68+
if (frontmatterKey === 'el' || frontmatterKey === 'selector') {
69+
annotation.el = frontmatterValue;
70+
}
71+
if (frontmatterKey === 'title') {
72+
annotation.title = frontmatterValue;
73+
}
74+
}
75+
}
76+
77+
//set the comment to the parsed markdown
78+
var annotationMarkdown = chunks[2];
79+
annotation.comment = md.render(annotationMarkdown);
80+
81+
annotations.push(annotation);
82+
} else {
83+
console.log('annotations.md file not formatted as expected. Error parsing frontmatter and markdown out of ' + annotationSnippet);
84+
}
85+
}
86+
return annotations;
2787
}
2888

2989
function gatherAnnotations() {
30-
//todo: merge markdown too https://github.com/pattern-lab/patternlab-php-core/blob/c2c4bc6a8bda2b2f9c08b197669ebc94c025e7c6/src/PatternLab/Annotations.php
31-
return parseAnnotationsJS();
90+
var annotationsJS = parseAnnotationsJS();
91+
var annotationsMD = parseAnnotationsMD();
92+
var mergedAnnotations = _.unionBy(annotationsJS, annotationsMD, 'el');
93+
return mergedAnnotations;
3294
}
3395

3496
return {
3597
gather: function () {
3698
return gatherAnnotations();
99+
},
100+
gatherJS: function () {
101+
return parseAnnotationsJS();
102+
},
103+
gatherMD: function () {
104+
return parseAnnotationsMD();
37105
}
38106
};
39107

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"handlebars": "^4.0.5",
1111
"html-entities": "^1.2.0",
1212
"json5": "^0.5.0",
13+
"lodash": "^4.12.0",
1314
"markdown-it": "^6.0.1",
1415
"matchdep": "^1.0.0",
1516
"mustache": "^2.2.0",

test/annotation_exporter_tests.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"use strict";
2+
3+
var eol = require('os').EOL;
4+
var Pattern = require('../core/lib/object_factory').Pattern;
5+
var extend = require('util')._extend;
6+
7+
function createFakePatternLab(customProps) {
8+
var pl = {
9+
"config": {
10+
"paths": {
11+
"source": {
12+
"annotations": './test/files/'
13+
}
14+
}
15+
}
16+
};
17+
18+
return extend(pl, customProps);
19+
}
20+
21+
var patternlab = createFakePatternLab();
22+
var ae = require('../core/lib/annotation_exporter')(patternlab);
23+
24+
exports['annotaton_exporter'] = {
25+
26+
'converts old JS annotations into new format': function (test) {
27+
//arrange
28+
//act
29+
var annotations = ae.gatherJS();
30+
31+
//assert
32+
test.equals(annotations.length, 2);
33+
test.equals(annotations[1].el, '.logo');
34+
test.equals(annotations[1].title, 'Logo');
35+
test.equals(annotations[1].comment, "The logo image is an SVG file, which ensures that the logo displays crisply even on high resolution displays. A PNG fallback is provided for browsers that don't support SVG images.</p><p>Further reading: <a href=\"http://bradfrostweb.com/blog/mobile/hi-res-optimization/\">Optimizing Web Experiences for High Resolution Screens</a></p>");
36+
37+
test.done();
38+
},
39+
40+
'converts new markdown annotations into an array': function (test) {
41+
//arrange
42+
//act
43+
var annotations = ae.gatherMD();
44+
45+
//assert
46+
test.equals(annotations.length, 3);
47+
test.equals(annotations[1].el, '.logo');
48+
test.equals(annotations[1].title, 'Logo');
49+
test.equals(annotations[1].comment.replace(/\r?\n|\r/gm, ""), '<p>The <em>logo image</em> is an SVG file.</p>');
50+
51+
test.done();
52+
},
53+
54+
'merges both annotation methods into one array' : function (test) {
55+
//arrange
56+
57+
//act
58+
var annotations = ae.gather();
59+
60+
//assert
61+
test.equals(annotations.length, 3);
62+
test.equals(annotations[2].el, '#nav');
63+
test.equals(annotations[2].title, 'Navigation');
64+
test.equals(annotations[2].comment.replace(/\r?\n|\r/gm, ""), '<p>Navigation for adaptive web experiences can be tricky. Refer to <a href="https://bradfrost.github.io/this-is-responsive/patterns.html#navigation">these repsonsive patterns</a> when evaluating solutions.</p>');
65+
66+
test.done();
67+
68+
}
69+
};

test/files/annotations.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
var comments = {
2+
"comments" : [
3+
{
4+
"el": "header[role=banner]",
5+
"title" : "Masthead",
6+
"comment": "The main header of the site doesn't take up too much screen real estate in order to keep the focus on the core content. It's using a linear CSS gradient instead of a background image to give greater design flexibility and reduce HTTP requests."
7+
},
8+
{
9+
"el": ".logo",
10+
"title" : "Logo",
11+
"comment": "The logo image is an SVG file, which ensures that the logo displays crisply even on high resolution displays. A PNG fallback is provided for browsers that don't support SVG images.</p><p>Further reading: <a href=\"http://bradfrostweb.com/blog/mobile/hi-res-optimization/\">Optimizing Web Experiences for High Resolution Screens</a></p>"
12+
}
13+
]
14+
};

test/files/annotations.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
el: "header[role=banner]"
3+
title: "Masthead"
4+
---
5+
The main header of the site doesn't take up *too much screen real estate* in order to keep the focus on the core content.
6+
It's using a linear CSS gradient instead of a background image to give greater design flexibility and reduce HTTP requests.
7+
~*~
8+
---
9+
selector: ".logo"
10+
title: "Logo"
11+
---
12+
The _logo image_ is an SVG file.
13+
~*~
14+
---
15+
el: "#nav"
16+
title : "Navigation"
17+
---
18+
Navigation for adaptive web experiences can be tricky. Refer to [these repsonsive patterns](https://bradfrost.github.io/this-is-responsive/patterns.html#navigation) when evaluating solutions.

0 commit comments

Comments
 (0)