Fix #40 #47 render examples recursively

Fix #17 paths ordered as per swagger file
Remove json-stable-stringify dependency
This commit is contained in:
Kam Low 2017-04-01 13:53:02 +02:00
parent db9ce6d982
commit 55b6e2c655
24 changed files with 406 additions and 2477 deletions

View File

@ -27,29 +27,29 @@ var Handlebars = require('handlebars');
* @api public
*/
module.exports = function(context, options) {
var ret = "";
var data;
if (typeof context !== "object") {
return ret;
var ret = "";
var data;
if (typeof context !== "object") {
return ret;
}
var keys = Object.keys(context);
keys.sort(function(a,b) {
// http://stackoverflow.com/questions/8996963/how-to-perform-case-insensitive-sorting-in-javascript
a = String(a).toLowerCase();
b = String(b).toLowerCase();
if (a == b) return 0;
if (a > b) return 1;
return -1;
}).forEach(function(key, index) {
if (options.data) {
data = Handlebars.createFrame(options.data || {});
data.index = index;
data.key = key;
data.length = keys.length;
data.first = index === 0;
data.last = index === keys.length - 1;
}
var keys = Object.keys(context);
keys.sort(function(a,b) {
// http://stackoverflow.com/questions/8996963/how-to-perform-case-insensitive-sorting-in-javascript
a = String(a).toLowerCase();
b = String(b).toLowerCase();
if( a == b) return 0;
if( a > b) return 1;
return -1;
}).forEach(function(key, index) {
if (options.data) {
data = Handlebars.createFrame(options.data || {});
data.index = index;
data.key = key;
data.length = keys.length;
data.first = index === 0;
data.last = index === keys.length - 1;
}
ret = ret + options.fn(context[key], {data: data})
});
return ret
ret = ret + options.fn(context[key], {data: data})
});
return ret;
};

View File

@ -5,5 +5,5 @@
* @returns {boolean}
*/
module.exports = function(value1, value2) {
return value1 == value2;
return value1 == value2;
};

View File

@ -4,5 +4,5 @@
* is not addressed by this helper.
*/
module.exports = function(value) {
return value.replace(/[^A-Za-z0-9-_:.]/g, "-");
return value.replace(/[^A-Za-z0-9-_:.]/g, "-");
};

View File

@ -1,6 +1,6 @@
module.exports = function(array, object, options) {
if (array && array.indexOf(object) >= 0) {
return options.fn(this);
}
return options.inverse(this);
if (array && array.indexOf(object) >= 0) {
return options.fn(this);
}
return options.inverse(this);
};

View File

@ -14,9 +14,9 @@
* @param {object} `v2` the second value
*/
module.exports = function(v1, v2, options) {
// http://stackoverflow.com/questions/8853396/logical-operator-in-a-handlebars-js-if-conditional
if (v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
// http://stackoverflow.com/questions/8853396/logical-operator-in-a-handlebars-js-if-conditional
if (v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
};

View File

@ -9,7 +9,7 @@ var common = require('../lib/common');
* @returns {Handlebars.SafeString} a Handlebars-SafeString containing the provieded
* markdown, rendered as HTML.
*/
module.exports = function(value, options) {
var html = common.markdown(value, options.hash ? options.hash.stripParagraph : false);
return new Handlebars.SafeString(html);
module.exports = function(value, options) {
var html = common.markdown(value, options.hash ? options.hash.stripParagraph : false);
return new Handlebars.SafeString(html);
};

View File

@ -3,6 +3,8 @@ var common = require('../lib/common');
module.exports = function(value, options) {
var cloned = common.formatExample(value, options.data.root);
if (!cloned)
return '';
if (options.hash.type == 'array')
cloned = [cloned];
var html = common.printSchema(cloned);

View File

@ -1,10 +0,0 @@
var Handlebars = require('handlebars');
var common = require('../lib/common');
module.exports = function(value, options) {
var cloned = common.formatSchema(value);
if (options.hash.type == 'array')
cloned = [cloned];
var html = common.printSchema(cloned);
return new Handlebars.SafeString(html)
};

View File

@ -1,15 +0,0 @@
var Handlebars = require('handlebars');
var common = require('../lib/common');
module.exports = function(reference, options) {
if (!reference) {
console.error("Cannot print null reference.");
return '';
}
var model = common.resolveSchemaReference(reference, options.data.root);
var cloned = common.formatSchema(model);
if (options.hash.type == 'array')
cloned = [cloned];
var html = common.printSchema(cloned);
return new Handlebars.SafeString(html);
};

View File

@ -6,5 +6,5 @@
* @api public
*/
module.exports = function(value, options) {
return value ? value.toUpperCase() : '';
return value ? value.toUpperCase() : '';
};

View File

@ -37,82 +37,99 @@ var common = {
return html;
},
formatSchema: function(value) {
var cloned;
if (typeof value === 'object' && typeof value.properties === 'object') {
if (value.example) {
// Use the supplied example
value = value.example;
cloned = _.cloneDeep(value);
} else {
// Create json object of keys : type info string
value = value.properties;
cloned = _.cloneDeep(value);
Object.keys(cloned).forEach(function(propName) {
var prop = cloned[propName];
if (prop.type) {
if (prop.example) {
cloned[propName] = prop.example;
}
else {
cloned[propName] = prop.type;
if (prop.format) {
cloned[propName] += ('(' + prop.format + ')');
}
}
}
})
}
}
return cloned;
},
// formatSchema: function(value) {
// var cloned;
// if (typeof value === 'object' && typeof value.properties === 'object') {
// if (value.example) {
// // Use the supplied example
// value = value.example;
// cloned = _.cloneDeep(value);
// } else {
// // Create json object of keys : type info string
// value = value.properties;
// cloned = _.cloneDeep(value);
// Object.keys(cloned).forEach(function(propName) {
// var prop = cloned[propName];
// if (prop.type) {
// if (prop.example) {
// cloned[propName] = prop.example;
// }
// else {
// cloned[propName] = prop.type;
// if (prop.format) {
// cloned[propName] += ('(' + prop.format + ')');
// }
// }
// }
// })
// }
// }
// return cloned;
// },
formatExample: function(value, root) {
if (value.example) {
return value.example;
}
else if (value.schema && value.schema.$ref) {
return this.formatExampleProp(value.schema, root);
}
else if (value.schema && value.schema.items) {
return this.formatExampleProp(value.schema.items.$ref, root);
}
if (!value) {
return;
}
if (value.example) {
return value.example;
}
else if (value.schema && (value.schema.$ref || value.schema.items )) {
return this.formatExampleProp(value.schema, root);
}
else if (value.type) {
return this.formatExampleProp(value, root);
}
console.warn('Cannot format object ', value)
},
formatExampleProp: function(ref, root) {
var obj = {};
var that = this;
if (!ref) {
return;
}
if (ref.$ref) {
ref = this.resolveSchemaReference(ref.$ref, root);
return this.formatExampleProp(ref, root);
}
else if (ref.type == 'object') {
var obj = {};
var that = this;
if (ref.$ref) {
ref = this.resolveSchemaReference(ref.$ref, root);
return this.formatExampleProp(ref, root);
}
else if (ref.type == 'object') {
Object.keys(ref.properties).forEach(function(k) {
obj[k] = that.formatExampleProp(ref.properties[k], root);
});
}
else if (ref.type == 'array' && ref.items) {
obj = [
this.formatExampleProp(ref.items, root),
this.formatExampleProp(ref.items, root),
];
}
else if (ref.example) {
return ref.example;
}
else {
return ref.type + (ref.format ? ' (' + ref.format + ')' : '');
}
if (ref.properties) {
Object.keys(ref.properties).forEach(function(k) {
obj[k] = that.formatExampleProp(ref.properties[k], root);
});
}
else if (ref.allOf) {
ref.allOf.forEach(function(parent) {
obj = Object.assign(that.formatExampleProp(parent, root), obj);
});
}
return obj;
return obj;
}
else if (ref.type == 'array' && ref.items) {
return [ this.formatExampleProp(ref.items, root) ];
}
else if (ref.example) {
return ref.example;
}
else {
return ref.type + (ref.format ? ' (' + ref.format + ')' : '');
}
console.warn('Cannot format property ', ref)
},
printSchema: function(value) {
if (!value) {
return '';
}
var schemaString = require('json-stable-stringify')(value, { space: 2 });
var schemaString = JSON.stringify(value, null, 2);
// Add an extra CRLR before the code so the postprocessor can determine
// the correct line indent for the <pre> tag.
@ -167,8 +184,8 @@ highlight.configure({
});
marked.setOptions({
highlight: common.highlight,
//langPrefix: 'hljs '
highlight: common.highlight
// langPrefix: 'hljs'
});
module.exports = common;

View File

@ -408,8 +408,19 @@ article {
word-break: break-all;
}
}
//
// Definition panel
.definition {
.doc-examples h5 {
margin-top: -1rem;
}
}
}
//
// Code highlighting

View File

@ -2,19 +2,17 @@
This partial renders the documentation content
@api public
--}}
<article>
{{>swagger/introduction}}
{{>swagger/securityDefinitions}}
{{#if showTagSummary}}
{{>swagger/tags}}
{{else}}
{{>swagger/paths}}
{{/if}}
{{!printSchema .}}
{{!swagger/paths paths=paths}}
{{!swagger/parameterDefinitions parameters=parameters}}
{{!swagger/responseDefinitions responses=responses}}
{{>swagger/definitions definitions=definitions}}
{{! Powered by link }}

View File

@ -34,7 +34,7 @@
{{/eachSorted}}
{{else}}
<h5>Paths</h5>
{{#eachSorted paths}}
{{#each paths}}
<!-- <section>
<a href="#path-{{htmlId @key}}">{{@key}}</a>
<ul> -->
@ -51,16 +51,16 @@
{{/each}}
<!-- </ul>
</section> -->
{{#eachSorted .}}
{{!swagger/operation . operation=. method=@key path=../path}}
{{/eachSorted}}
{{/eachSorted}}
{{#each .}}
{{!swagger/operation . operation=. method=@key path=../path}}
{{/each}}
{{/each}}
{{/if}}
<h5>Schema Definitions</h5>
{{#eachSorted definitions}}
{{#each definitions}}
<a href="#definition-{{htmlId @key}}">
{{@key}}
</a>
{{/eachSorted}}
{{/each}}
</nav>

View File

@ -1,9 +1,9 @@
{{!
Renders a json.schema inside a bootstrap-panel.
Renders a json.schema inside a panel.
@public
@readonly
}}
<div id="definition-{{htmlId @key}}" class="panel panel-definition"
<div id="definition-{{htmlId @key}}" class="definition panel"
data-traverse-target="definition-{{htmlId @key}}">
{{#if title}}
{{#if anchor}}
@ -16,25 +16,6 @@
{{/if}}
{{/if}}
<!-- <div class="doc-row">
<div class="doc-copy">
{{! The _request_body variable is filled with the parameter `body` by the preprocessor. }}
{{#if _show_requst_body_section}}
{{>swagger/request-body consumes=consumes body=_request_body}}
{{/if}}
{{>swagger/parameters parameters=parameters}}
</div>
{{! Print examples without whitespace }}
<div class="doc-examples">{{#if _show_requst_body_section}}
<section>
<h5>Request Example</h5>
{{>swagger/print-schema _request_body.schema}}
</section>
{{/if}}</div>
</div> -->
<div class="doc-row">
<div class="doc-copy">
{{#if $ref}}
@ -45,13 +26,11 @@
</div>
{{! Print examples without whitespace }}
{{#if example}}
<div class="doc-examples">
<section>
<h5>Example</h5>
{{>swagger/print-schema example}}
</section>
</div>
{{/if}}
<div class="doc-examples">
<section>
<h5>Example</h5>
{{>swagger/print-example .}}
</section>
</div>
</div>
</div>

View File

@ -5,10 +5,9 @@
--}}
{{#if definitions}}
<!-- <h1>Schema definitions</h1> -->
<h1>Models</h1>
<h1>Schema Definitions</h1>
{{#eachSorted definitions}}
{{#each definitions}}
{{>swagger/definition title=@key anchor="/definitions" }}
{{/eachSorted}}
{{/each}}
{{/if}}

View File

@ -66,11 +66,11 @@
{{! Print examples without whitespace }}
<div class="doc-examples">{{#if _show_requst_body_section}}
<section>
<h5>Request Example</h5>
{{>swagger/print-example _request_body}}
</section>
{{/if}}</div>
<section>
<h5>Request Example</h5>
{{>swagger/print-example _request_body}}
</section>
{{/if}}</div>
</div>
{{>swagger/responses}}

View File

@ -1,11 +1,11 @@
{{!--
Renders a single path definition with all its methods (GET, POST).
@param {string} path the request path
@param {object<Operation>} pathItems a swagger [path-item-object](http://swagger.io/specification/#pathItemObject)
@api public
Renders a single path definition with all its methods (GET, POST).
@param {string} path the request path
@param {object<Operation>} pathItems a swagger [path-item-object](http://swagger.io/specification/#pathItemObject)
@api public
--}}
<span id="path-{{htmlId path}}"></span>
{{#eachSorted pathItems}}
{{>swagger/operation . operation=. method=@key path=../path}}
{{/eachSorted}}
{{#each pathItems}}
{{>swagger/operation . operation=. method=@key path=../path}}
{{/each}}

View File

@ -3,8 +3,14 @@
@params {Path[]} paths a list of Swagger Path-Objects
@api public
--}}
<h1>Paths</h1>
{{#eachSorted paths}}
{{>swagger/path pathItems=. path=@key}}
{{/eachSorted}}
<h1>Paths</h1>
{{#if showTagSummary}}
{{#eachSorted paths}}
{{>swagger/path pathItems=. path=@key}}
{{/eachSorted}}
{{else}}
{{#each paths}}
{{>swagger/path pathItems=. path=@key}}
{{/each}}
{{/if}}

View File

@ -1,7 +0,0 @@
{{#if $ref}}
<div class="hljs">{{printSchemaReference $ref type=type}}</div>
{{else if items.$ref}}
<div class="hljs">{{printSchemaReference items.$ref type=type}}</div>
{{else}}
<div class="hljs">{{printSchema .}}</div>
{{/if}}

View File

@ -1,6 +1,6 @@
{
"name": "spectacle-docs",
"version": "0.7.5",
"version": "0.8.0",
"description": "Generate beautiful static API documentation from OpenAPI/Swagger 2.0 specifications",
"preferGlobal": true,
"bin": {
@ -49,10 +49,12 @@
"grunt-sass": "^2.0.0",
"handlebars": "^4.0.5",
"highlight.js": "^9.1.0",
"json-stable-stringify": "^1.0.1",
"lodash": "^4.2.1",
"marked": "^0.3.5",
"tmp": "0.0.31",
"trace": "^1.1.0"
},
"devDependencies": {
"grunt-contrib-jshint": "^1.1.0"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1117,6 +1117,8 @@ Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-lic
right: 50%; } }
#spectacle article .operation .operation-path {
word-break: break-all; }
#spectacle article .definition .doc-examples h5 {
margin-top: -1rem; }
#spectacle .hljs {
padding: 0 !important;
margin-bottom: 1.5rem; }

File diff suppressed because one or more lines are too long