Skip to content
This repository was archived by the owner on Dec 31, 2017. It is now read-only.

move markdown parsing to exporter #78

Closed
wants to merge 1 commit into from
Closed
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
101 changes: 96 additions & 5 deletions lib/exporter/dojov1.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,46 @@
define([ '../Module', '../Value', './util', '../console', '../node!fs' ], function (Module, Value, util, console, fs) {
define([
'dojo/string',
'../Module',
'../Value',
'./util',
'../console',
'dojo/node!fs',
'dojo/node!../marked/lib/marked.js'
], function (stringUtil, Module, Value, util, console, fs, marked) {
/**
* Parses a markdown-enabled block of text into HTML, including the custom block extension for code samples.
* @param text Markdown text.
* @returns {string} HTML.
*/
function parseMarkdown(/**string*/ text) {
var tokens = marked.lexer(text);

for (var index = 0, token; (token = tokens[index]); ++index) {
if (token.type === 'paragraph' && /^\s*\|\s*/m.test(token.text)) {
var newTokens = [],
lastToken = {};

token.text.split('\n').forEach(function (line) {
var lineType = /^\s*\|/.test(line) ? 'code' : 'paragraph';

line = line.replace(/^\s*\|/, '');

if (lineType === lastToken.type) {
lastToken.text += (lastToken.text.length ? '\n' : '') + line;
}
else {
newTokens.push((lastToken = { type: lineType, text: line }));
}
});

tokens.splice.apply(tokens, [ index, 1 ].concat(newTokens));
index += newTokens.length - 1;
}
}

return marked.parser(tokens);
}

/**
* Takes information from metadata stored alongside a Value and adds it to the output.
* @param node The node to add metadata to.
Expand All @@ -11,7 +53,7 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi

for (var metadataName in { summary: 1, description: 1 }) {
if (metadata.hasOwnProperty(metadataName) && metadata[metadataName]) {
node.createNode(metadataName).childNodes.push(metadata[metadataName]);
node.createNode(metadataName).childNodes.push(parseMarkdown(metadata[metadataName]));
}
}

Expand All @@ -29,11 +71,54 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi
var examplesNode = node.createNode('examples');

for (var i = 0, j = metadata.examples.length; i < j; ++i) {
examplesNode.createNode('example').childNodes.push(metadata.examples[i]);
examplesNode.createNode('example').childNodes.push(parseMarkdown(metadata.examples[i]));
}
}
}

/**
* Given metadata with a type annotation, attempt to resolve the annotated type as an object and (hackily) apply
* information about the object’s default properties to the metadata description property.
*/
function processTypeAnnotation(/**Object*/ metadata) {
if (!metadata.type || typeof metadata.type === 'string') {
return;
}

var propertyTemplate = '\n* ${key}${type}${summary}',
annotationObject = metadata.type,
additionalDescription = '';

metadata.type = 'Object';
additionalDescription += metadata.description ?
'\n\nThe following properties are supported:\n' :
'An object with the following properties:\n';

(function readProperties(object) {
var propertyMetadata;
for (var k in object.properties) {
if (_hasOwnProperty.call(object.properties, k)) {
// Type descriptor could be a plain JS object, or could be a constructor. It is often the
// latter.
if (k === 'prototype') {
readProperties(object.properties[k]);
}
// Filter out built-ins and constructor properties which come from dojo/_base/declare
else if (k !== 'constructor' && object.properties[k].file) {
propertyMetadata = object.properties[k].metadata;
additionalDescription += stringUtil.substitute(propertyTemplate, {
key: k,
type: propertyMetadata.type ? ' (' + propertyMetadata.type + (propertyMetadata.isOptional ? ', optional' : '') + ')' : '',
summary: propertyMetadata.summary ? ': ' + parseMarkdown(propertyMetadata.summary) : ''
});
}
}
}
}(annotationObject));

metadata.description = (metadata.description || '') + additionalDescription;
}

/**
* Takes an array of return Values and processes it for return types, discarding all
* duplicates, and applies the resulting list of properties to the node given in returnsNode.
Expand Down Expand Up @@ -67,6 +152,9 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi
i;

for (i = 0; (parameter = property.parameters[i]); ++i) {
if (typeof parameter.metadata.type !== 'string') {
processTypeAnnotation(parameter.metadata);
}
parameterType = parameter.metadata.type || parameter.type || 'unknown';
parameterNode = parametersNode.createNode('parameter', {
name: parameter.name,
Expand All @@ -76,7 +164,7 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi

for (var metadataName in { summary: 1, description: 1 }) {
if (parameter.metadata.hasOwnProperty(metadataName) && parameter.metadata[metadataName]) {
parameterNode.createNode(metadataName).childNodes.push(parameter.metadata[metadataName]);
parameterNode.createNode(metadataName).childNodes.push(parseMarkdown(parameter.metadata[metadataName]));
}
}
}
Expand All @@ -88,7 +176,7 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi

for (i = 0; (returnValue = property.returns[i]); ++i) {
if (returnValue.metadata.summary) {
propertyNode.createNode('return-description').childNodes.push(returnValue.metadata.summary);
propertyNode.createNode('return-description').childNodes.push(parseMarkdown(returnValue.metadata.summary));
break;
}
}
Expand All @@ -105,6 +193,9 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi
propertyNode;

function makePropertyObject(name, value) {
if (typeof value.metadata.type !== 'string') {
processTypeAnnotation(value.metadata);
}
var object = {
name: name,
scope: scope,
Expand Down
97 changes: 9 additions & 88 deletions lib/processor/dojodoc.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,8 @@
define([
'dojo/_base/lang',
'dojo/string',
'../Value',
'../env',
'./util',
'dojo/node!../marked/lib/marked.js'
], function (lang, stringUtil, Value, env, util, marked) {
/**
* Parses a markdown-enabled block of text into HTML, including the custom block extension for code samples.
* @param text Markdown text.
* @returns {string} HTML.
*/
function parseMarkdown(/**string*/ text) {
var tokens = marked.lexer(text);

for (var index = 0, token; (token = tokens[index]); ++index) {
if (token.type === 'paragraph' && /^\s*\|\s*/m.test(token.text)) {
var newTokens = [],
lastToken = {};

token.text.split('\n').forEach(function (line) {
var lineType = /^\s*\|/.test(line) ? 'code' : 'paragraph';

line = line.replace(/^\s*\|/, '');

if (lineType === lastToken.type) {
lastToken.text += (lastToken.text.length ? '\n' : '') + line;
}
else {
newTokens.push((lastToken = { type: lineType, text: line }));
}
});

tokens.splice.apply(tokens, [ index, 1 ].concat(newTokens));
index += newTokens.length - 1;
}
}

return marked.parser(tokens);
}
'../Value',
'./util'
], function (env, Value, util) {

/**
* Mixes in metadata safely, ignoring empty keys and concatenating instead of overriding arrays in order to
Expand All @@ -50,7 +14,7 @@ define([
if (source[k] instanceof Array && destination[k] instanceof Array) {
destination[k] = destination[k].concat(source[k]);
}
else if (k === 'type') {
else if (k === 'type' && typeof source[k] === 'string') {
destination[k] = source[k].replace(optionalTypeRe, '');
}
else if (typeof source[k] !== 'string' || trim(source[k])) {
Expand Down Expand Up @@ -113,18 +77,15 @@ define([
}

/**
* Given metadata with a type annotation, attempt to resolve the annotated type as an object and (hackily) apply
* information about the object’s default properties to the metadata description property.
* TODO: This should really end up happening in the dojov1 exporter instead.
* Given metadata with a type annotation, attempt to resolve the annotated type as an object and
* provide that object to the exporter as the type property of the metadata.
*/
function processTypeAnnotation(/**Object*/ metadata) {
if (!metadata.type) {
return;
}

var propertyTemplate = '\n* ${key}${type}${summary}',
annotationObject = env.scope.getVariable(metadata.type.replace(/[^\w$\.]+$/g, '').split('.')),
additionalDescription = '';
var annotationObject = env.scope.getVariable(metadata.type.replace(/[^\w$\.]+$/g, '').split('.'));

if (!annotationObject || annotationObject.type === Value.TYPE_UNDEFINED || /* not a built-in */ !annotationObject.file) {
return;
Expand All @@ -139,34 +100,8 @@ define([
// evaluate all function expressions; this might be an issue
annotationObject.evaluate && annotationObject.evaluate();

metadata.type = 'Object';
additionalDescription += metadata.description ?
'\n\nThe following properties are supported:\n' :
'An object with the following properties:\n';

(function readProperties(object) {
var propertyMetadata;
for (var k in object.properties) {
if (_hasOwnProperty.call(object.properties, k)) {
// Type descriptor could be a plain JS object, or could be a constructor. It is often the
// latter.
if (k === 'prototype') {
readProperties(object.properties[k]);
}
// Filter out built-ins and constructor properties which come from dojo/_base/declare
else if (k !== 'constructor' && object.properties[k].file) {
propertyMetadata = object.properties[k].metadata;
additionalDescription += stringUtil.substitute(propertyTemplate, {
key: k,
type: propertyMetadata.type ? ' (' + propertyMetadata.type + (propertyMetadata.isOptional ? ', optional' : '') + ')' : '',
summary: propertyMetadata.summary ? ': ' + propertyMetadata.summary : ''
});
}
}
}
}(annotationObject));

metadata.description = (metadata.description || '') + parseMarkdown(additionalDescription);
// attach annotation to metadata as the type so the exporter can process it
metadata.type = annotationObject;
}

/**
Expand Down Expand Up @@ -306,20 +241,6 @@ define([
}
}

// The style guide says `summary` isn’t markdown, only `description` is markdown, but everyone sticks markdown
// in the summary anyway, so handle it as markdown too
metadata.summary = parseMarkdown(metadata.summary);
metadata.description = parseMarkdown(metadata.description);
for (var i = 0, example; (example = metadata.examples[i]); ++i) {
metadata.examples[i] = parseMarkdown(example);
}

for (var k in metadata.properties) {
if (_hasOwnProperty.call(metadata.properties, k)) {
metadata.properties[k].summary = parseMarkdown(metadata.properties[k].summary);
}
}

return metadata;
}

Expand Down