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

EDIT: provide raw metadata to exporter #80

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
76 changes: 75 additions & 1 deletion lib/exporter/dojov1.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
define([ '../Module', '../Value', './util', '../console', '../node!fs' ], function (Module, Value, util, console, fs) {
define([
'dojo/string',
'../Module',
'../Value',
'./util',
'../console',
'dojo/node!fs',
], function (stringUtil, Module, Value, util, console, fs) {

/**
* Takes information from metadata stored alongside a Value and adds it to the output.
* @param node The node to add metadata to.
Expand Down Expand Up @@ -34,6 +42,66 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi
}
}

/**
* 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 = '<li>${key}${type}${summary}</li>',
annotationObject = metadata.type,
additionalDescription = '';

if (annotationObject.relatedModule) {
metadata.type = annotationObject.relatedModule.id;
return;
}

metadata.type = 'Object';
additionalDescription += '<p>' + (metadata.description ?
'The following properties are supported:' :
'An object with the following properties:') + '</p><ul>';

(function readProperties(object) {
var propertyMetadata,
properties = object.properties,
k;

// if the annotationObject is a function, we don't want to pick up any properties apart
// from what's on the prototype.
if (object.type === 'function') {
if (_hasOwnProperty.call(properties, 'prototype')) {
readProperties(properties.prototype);
}
return;
}

for (k in properties) {
if (_hasOwnProperty.call(properties, k)) {
// Type descriptor could be a plain JS object, or could be a constructor. It is often the
// latter.
if (k === 'prototype') {
readProperties(properties[k]);
}
// Filter out built-ins and constructor properties which come from dojo/_base/declare
else if (k !== 'constructor' && properties[k].file) {
propertyMetadata = 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 || '') + additionalDescription + '</ul>';
}

/**
* 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 +135,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 Down Expand Up @@ -105,6 +176,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
122 changes: 63 additions & 59 deletions lib/processor/dojodoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,22 @@ define([
* avoid accidentally overwriting data that already exists with empty data.
*/
function mixinMetadata(destination, source) {
for (var k in source) {
var raw,
k;

for (k in source) {
if (_hasOwnProperty.call(source, k) && source[k]) {
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 (k === 'raw') {
// this keeps the mixin/override semantics of raw the same as the metadata itself
raw = destination[k];
raw ? mixinMetadata(raw, source[k]) : destination[k] = source[k];
}
else if (typeof source[k] !== 'string' || trim(source[k])) {
destination[k] = source[k];
}
Expand Down Expand Up @@ -113,60 +121,24 @@ 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;
}

if (annotationObject.relatedModule) {
metadata.type = annotationObject.relatedModule.id;
return;
}

// TODO: The fact that evaluate exists on annotation objects seems to indicate that we’re failing to
// 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);
// defer resolving this to do it in the exporter. annotationObject might be the return
// value of the module currently being processed and in that case it won't have been tagged
// with a relatedModule yet.
metadata.type = annotationObject;
}

/**
Expand Down Expand Up @@ -194,6 +166,47 @@ define([
}
}

/**
* Processes metadata to parse the markdown of certain properties
* @param metadata Metadata object to parse for markdown
*/
function processMetadata(/**Object*/ metadata) {
var example,
i,
k,
raw = metadata.raw = {
summary: metadata.summary,
description: metadata.description,
// handle examples in the loop below to avoid redundant iteration. also, by
// recursing for properties and returns, they will get their own raw objects.
};

// 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 || '');

if (metadata.examples) {
raw.examples = [];
for (i = 0; (example = metadata.examples[i]); ++i) {
raw.examples[i] = example;
metadata.examples[i] = parseMarkdown(example);
}
}

if (metadata.properties) {
for (k in metadata.properties) {
if (_hasOwnProperty.call(metadata.properties, k)) {
processMetadata(metadata.properties[k]);
}
}
}

if (metadata.returns) {
processMetadata(metadata.returns);
}
}

/**
* Processes a dojodoc multi-line comment block, which consists of key lines that identify the metadata and
* subsequent indented lines containing the actual metadata.
Expand Down Expand Up @@ -306,19 +319,7 @@ 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);
}
}
processMetadata(metadata);

return metadata;
}
Expand Down Expand Up @@ -369,8 +370,11 @@ define([
candidate = /^[^\n]*\/\/(.*?)\n/.exec(util.getSourceForRange(value.raw.range));

if (candidate) {
metadata = { type: trim(candidate[1]) };
value.evaluated.metadata.type = trim(candidate[1]);
processTypeAnnotation(value.evaluated.metadata);
}
// if we get here, we have no need to mixin any metadata
return;
}

// Function or object body
Expand Down