Skip to content
This repository has been archived by the owner on Sep 7, 2021. It is now read-only.

Simple packaging for HTML elements #14

Merged
merged 8 commits into from
May 13, 2019
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
18 changes: 5 additions & 13 deletions content/html/elements/video/prose.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
<!-- <short-description> -->
<!-- short-description -->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More and more I think comments or custom tags are not going to work for this. For example, it completely foils GitHub's rich Markdown diffs:

Screen Shot 2019-05-09 at 5 24 10 PM

I think sooner or later we'll want to do plain headings (## Short description) and aggressively enforce accepted text and hierarchy (as part of the recipe).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes... I'm not sure now why we opted for this plan rather than just using headings. Also I'm not happy that prose.md uses comments while attributes use headings.

So unless someone can remember why we decided to do this, I think you are right.

Again these are things we can tweak as we go along though.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to keep this for now but filed #19.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't signify that I've done any kind of thorough review 😃, but I was skimming this PR while waiting for some tests to finish and noticed this thread. I can't remember the details either, but I'm also not happy with the fact that prose.md uses hidden comments to enforce structure while the attributes use headings. It's probably best to choose some specific headings for signifying/enforcing structure, and get rid of the hidden comments. So, for what it's worth, just wanted to say that I agree with you both in where you're heading here! 😄

The **HTML Video element** (**`<video>`**) embeds a media player which
supports video playback into the document.
<!-- </short-description> -->

<!-- <overview> -->
<!-- overview -->
You can use `<video>` for audio content as well, but the [`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio)
element may provide a more appropriate user experience.

Expand Down Expand Up @@ -53,9 +51,7 @@ A good general source of information on using HTML `<video>` is the
[Video and audio
content](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content)
beginner's tutorial.
<!-- </overview> -->

<!-- <usage-notes> -->
<!-- usage-notes -->
Usage notes
-----------

Expand Down Expand Up @@ -103,9 +99,7 @@ AddType video/webm .webm

Your web host may provide an easy interface to MIME type configuration
changes for new technologies until a global update naturally occurs.
<!-- </usage-notes> -->

<!-- <accessibility-concerns> -->
<!-- accessibility-concerns -->
Videos should provide both captions and transcripts that accurately
describe its content (see [Adding captions and subtitles to HTML5
video](https://developer.mozilla.org/en-US/docs/Web/Apps/Fundamentals/Audio_and_video_delivery/Adding_captions_and_subtitles_to_HTML5_video)
Expand Down Expand Up @@ -156,9 +150,7 @@ setting](https://developer.mozilla.org/en-US/docs/Web/API/WebVTT_API#Cue_setting
2.0](https://www.w3.org/TR/UNDERSTANDING-WCAG20/media-equiv-av-only-alt.html)
- [Understanding Success Criterion 1.2.2 | W3C Understanding WCAG
2.0](https://www.w3.org/TR/UNDERSTANDING-WCAG20/media-equiv-captions.html)
<!-- </accessibility-concerns> -->

<!-- <see-also> -->
<!-- see-also -->
See also
--------

Expand Down
13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,27 @@
},
"scripts": {
"lint-md": "remark content",
"build-json": "node scripts/build-json/build-json.js",
"spell-md": "mdspell -a -n --en-us 'content/**/*.md'"
},
"dependencies": {
"gray-matter": "4.0.2",
"jsdom": "^12.2.0",
"js-yaml": "3.13.1",
"marked": "0.6.2",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not even a fully functioning prototype yet and we've got two Markdown parsers. Some day (but not today) I'm going to have a full on meltdown[1] about this. Choosing a Markdown and an idea of how to not-so-dangerously extend it is something I'd like to see happen sooner rather than later (maybe "propose possible Markdowns" would be a good task for me for a future sprint).

[1] This would be a great name for a Markdown implementation

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely, we need to do this, and before you have a Markdown meltdown. I have filed mdn/sprints#1505 for this, but feel free to edit it as you see fit. I have made it an epic because it looks like it might be bigger than a single user story. But maybe not.

I hope, again, that getting experience of migrating content will help us decide which features we need.

"markdown-spellcheck": "1.3.1",
"npm": "^6.4.1",
"mdn-browser-compat-data": "0.x",
"npm": "^6.9.0",
"remark-cli": "6.0.1",
"remark-preset-lint-recommended": "3.0.2"
},
"remarkConfig": {
"plugins": [
"remark-preset-lint-recommended",
["remark-lint-list-item-indent", "space"]
[
"remark-lint-list-item-indent",
"space"
]
]
}
}
60 changes: 60 additions & 0 deletions scripts/build-json/build-json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');

const bcd = require('./resolve-bcd');
const examples = require('./compose-examples');
const attributes = require('./compose-attributes');
const prose = require('./slice-prose');
const contributors = require('./resolve-contributors');

const htmlElements = '/content/html/elements';

const writeToFile = (propertyName, json) => {
const data = {
html: {
elements: {
[propertyName]: json,
}
}
};

const dest = path.join(process.cwd(),'packaged/html/elements', `${propertyName}.json`);
const destDir = path.dirname(dest);
if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir, { recursive: true });
}
fs.writeFileSync(dest, `${JSON.stringify(data, null, 2)}`);
};

function package(elementName) {
const elementPath = path.join(process.cwd(), htmlElements, elementName);

if (!fs.existsSync(elementPath)) {
console.error(`Could not find an element called "${elementName}"`);
return;
}

// open meta.yaml
const meta = yaml.safeLoad(fs.readFileSync(path.join(elementPath, 'meta.yaml'), 'utf8'));

// initialise some paths for more resources
const examplesPaths = meta.examples.map(relativePath => path.join(elementPath, relativePath));
const attributesPath = path.join(elementPath, meta.attributes['element-specific']);
const prosePath = path.join(elementPath, 'prose.md');
const contributorsPath = path.join(elementPath, 'contributors.md');

// make the package
const element = {};
element.title = meta.title;
element.interactive_example_url = meta['interactive-example'];
element.browser_compatibility = bcd.package(meta['browser-compatibility']);
element.examples = examples.package(examplesPaths);
element.attributes = attributes.package(attributesPath);
element.prose = prose.package(prosePath);
element.contributors = contributors.package(contributorsPath);

writeToFile(elementName, element);
}

package(process.argv[2]);
75 changes: 75 additions & 0 deletions scripts/build-json/compose-attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const fs = require('fs');
const path = require('path');

const yaml = require('js-yaml');
const matter = require('gray-matter');
const marked = require('marked');
const jsdom = require('jsdom');
const bcd = require('mdn-browser-compat-data');

const { JSDOM } = jsdom;

function extractFromSiblings(node, terminatorTag, contentType) {
let content = '';
let sib = node.nextSibling;
while (sib && sib.nodeName != terminatorTag) {
if (sib.outerHTML) {
if (contentType === 'html') {
content += sib.outerHTML;
} else if (contentType === 'text') {
content += sib.textContent;
}
}
sib = sib.nextSibling;
}
return content;
}

function packageValues(heading, dom) {
const values = [];
const valueHeadings = dom.querySelectorAll('h3');
for (let valueHeading of valueHeadings) {
let value = {
value: valueHeading.textContent,
description: extractFromSiblings(valueHeading, 'H3', 'html')
}
values.push(value);
}
return values;
}

function packageAttribute(attributePath) {
const attributeMD = fs.readFileSync(attributePath, 'utf8');
const {data, content} = matter(attributeMD);
const dom = JSDOM.fragment(marked(content));
const attribute = {};

// extract the name property
const name = dom.querySelector('h1');
attribute.name = name.textContent;

// extract the description property
attribute.description = extractFromSiblings(name, 'H2', 'html');

// extract the type property
const h2Headings = dom.querySelectorAll('h2');
const typeHeading = (h2Headings.length === 2)? h2Headings[1]: h2Headings[0];
attribute.type = extractFromSiblings(typeHeading, 'H2', 'text');

// extract the values property
if (h2Headings.length === 2) {
valuesHeading = h2Headings[0];
attribute.values = packageValues(valuesHeading, dom);
}

return attribute;
}

function package(root) {
const attributePaths = fs.readdirSync(root).map(relative => path.join(root, relative));
return attributePaths.map(packageAttribute);
}

module.exports = {
package
}
48 changes: 48 additions & 0 deletions scripts/build-json/compose-examples.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const fs = require('fs');
const path = require('path');

const matter = require('gray-matter');
const marked = require('marked');

function packageDescription(examplePath) {
const descriptionMD = fs.readFileSync(path.join(examplePath, 'description.md'), 'utf8');
const {data, content} = matter(descriptionMD);
data.content = marked(content);
return data;
}

function packageSources(examplePath) {
const exampleSource = {};

const jsPath = path.join(examplePath, 'example.js');
if (fs.existsSync(jsPath)) {
exampleSource.js = fs.readFileSync(jsPath, 'utf8');
}

const cssPath = path.join(examplePath, 'example.css');
if (fs.existsSync(cssPath)) {
exampleSource.css = fs.readFileSync(cssPath, 'utf8');
}

const htmlPath = path.join(examplePath, 'example.html');
if (fs.existsSync(htmlPath)) {
exampleSource.html = fs.readFileSync(htmlPath, 'utf8');
}

return exampleSource;
}

function packageExample(examplePath) {
return {
description: packageDescription(examplePath),
sources: packageSources(examplePath)
}
}

function package(paths) {
return paths.map(packageExample);
}

module.exports = {
package
}
12 changes: 12 additions & 0 deletions scripts/build-json/resolve-bcd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const bcd = require('mdn-browser-compat-data');

function package(query) {
let data = query.split('.').reduce(function(prev, curr) {
return prev ? prev[curr] : undefined
}, bcd);
return data;
}

module.exports = {
package
}
13 changes: 13 additions & 0 deletions scripts/build-json/resolve-contributors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const fs = require('fs');
const path = require('path');

const marked = require('marked');

function package(contributorsPath) {
const contributorsMD = fs.readFileSync(contributorsPath, 'utf8');
return marked(contributorsMD);
}

module.exports = {
package
}
84 changes: 84 additions & 0 deletions scripts/build-json/slice-prose.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const fs = require('fs');
const path = require('path');

const yaml = require('js-yaml');
const matter = require('gray-matter');
const marked = require('marked');
const jsdom = require('jsdom');
const bcd = require('mdn-browser-compat-data');

const { JSDOM } = jsdom;
const commentNode = 8;
const namedSections = [
'short-description',
'overview',
'attributes-text',
'usage-notes',
'accessibility-concerns',
'see-also'
];

function extractFromSiblings(node, terminatorTag, contentType) {
let content = '';
let sib = node.nextSibling;
while (sib && sib.nodeName != terminatorTag) {
if (sib.outerHTML) {
if (contentType === 'html') {
content += sib.outerHTML;
} else if (contentType === 'text') {
content += sib.textContent;
}
}
sib = sib.nextSibling;
}
return content;
}

function packageValues(heading, dom) {
const values = [];
const valueHeadings = dom.querySelectorAll('h3');
for (let valueHeading of valueHeadings) {
let value = {
value: valueHeading.textContent,
description: extractFromSiblings(valueHeading, 'H3', 'html')
}
values.push(value);
}
return values;
}

function getSection(node, sections) {
const sectionName = node.textContent.trim();
const sectionContent = extractFromSiblings(node, '#comment', 'html');
const extraSections = [];

if (namedSections.includes(sectionName)) {
sections[sectionName] = sectionContent;
} else {
const additionalSection = {
name: sectionName,
content: sectionContent
};
sections['additional-sections'].push(additionalSection);
}
}

function package(prosePath) {
const proseMD = fs.readFileSync(prosePath, 'utf8');
const dom = JSDOM.fragment(marked(proseMD));
const sections = {
'additional-sections': []
};
let node = dom.firstChild;
while (node) {
if (node.nodeType === commentNode) {
getSection(node, sections);
}
node = node.nextSibling;
}
return sections;
}

module.exports = {
package
}