translator.js (4721B)
1 /* A couple of utility methods */ 2 3 function each(obj, iter) { 4 for (var key in obj) { 5 if (obj.hasOwnProperty(key)) iter(key, obj[key]); 6 } 7 } 8 9 function nextString(str) { 10 return 'S' + (parseInt(str.substr(1), 36) + 1).toString(36); 11 } 12 13 /* End utility methods */ 14 15 function Translator(api, options) { 16 var origLength = JSON.stringify(api, null, 2).length; 17 var debugInfo = {flattened: {}, pruned: {}}; 18 var shapeName = 'S0'; 19 var shapeNameMap = {}; 20 var visitedShapes = {}; 21 22 function logResults() { 23 console.log('** Generated', api.metadata.endpointPrefix + '-' + 24 api.metadata.apiVersion +'.min.json' + 25 (process.env.DEBUG ? ':' : '')); 26 27 if (process.env.DEBUG) { 28 var pruned = Object.keys(debugInfo.pruned); 29 var flattened = Object.keys(debugInfo.flattened); 30 var newLength = JSON.stringify(api, null, 2).length; 31 console.log('- Pruned Shapes:', pruned.length); 32 console.log('- Flattened Shapes:', flattened.length); 33 console.log('- Remaining Shapes:', Object.keys(api.shapes).length); 34 console.log('- Original Size:', origLength / 1024.0, 'kb'); 35 console.log('- Minified Size:', newLength / 1024.0, 'kb'); 36 console.log('- Size Saving:', (origLength - newLength) / 1024.0, 'kb'); 37 console.log(''); 38 } 39 } 40 41 function deleteTraits(obj) { 42 if (!options.documentation) { 43 delete obj.documentation; 44 delete obj.documentationUrl; 45 delete obj.errors; 46 delete obj.min; 47 delete obj.max; 48 delete obj.pattern; 49 delete obj['enum']; 50 delete obj.box; 51 } 52 } 53 54 function trackShapeDeclaration(ref) { 55 if (ref.shape && !shapeNameMap[ref.shape]) { 56 // found a shape declaration we have not yet visited. 57 // assign a new generated name in the shapeNameMap & visit it 58 var oldShapeName = ref.shape; 59 ref.shape = shapeName = nextString(shapeName); 60 61 visitedShapes[shapeName] = api.shapes[oldShapeName]; 62 shapeNameMap[oldShapeName] = {name: shapeName, refs: [ref]}; 63 64 traverseShapeRef(api.shapes[oldShapeName]); 65 } else if (ref.shape && shapeNameMap[ref.shape]) { 66 // we visited this shape before. keep track of this ref and rename 67 // the referenced declaration to the generated name 68 var map = shapeNameMap[ref.shape]; 69 map.refs.push(ref); 70 ref.shape = map.name; 71 } 72 } 73 74 function pruneShapes() { 75 // prune shapes visited only once or only have type specifiers 76 each(shapeNameMap, function(name, map) { 77 if (Object.keys(visitedShapes[map.name]).join() === 'type' && 78 ['structure','map','list'].indexOf(visitedShapes[map.name].type) < 0) { 79 // flatten out the shape (only a scalar type property is on the shape) 80 for (var i = 0; i < map.refs.length; i++) { 81 var ref = map.refs[i]; 82 debugInfo.flattened[name] = true; 83 delete ref.shape; 84 ref.type = visitedShapes[map.name].type; 85 86 // string type is default, don't need to specify this 87 if (ref.type === 'string') delete ref.type; 88 } 89 90 // we flattened all refs, we can prune the shape too 91 delete visitedShapes[map.name]; 92 debugInfo.pruned[name] = true; 93 } else if (map.refs.length === 1) { // only visited once 94 // merge shape data onto ref 95 var shape = visitedShapes[map.name]; 96 97 for (var i = 0; i < map.refs.length; i++) { 98 delete map.refs[i].shape; 99 for (var prop in shape) { 100 if (shape.hasOwnProperty(prop)) map.refs[i][prop] = shape[prop]; 101 } 102 } 103 104 // delete the visited shape 105 delete visitedShapes[map.name]; 106 debugInfo.pruned[name] = true; 107 } 108 }); 109 } 110 111 function traverseShapeRef(ref) { 112 if (!ref) return; 113 114 deleteTraits(ref); 115 116 traverseShapeRef(ref.key); // for maps 117 traverseShapeRef(ref.value); // for maps 118 traverseShapeRef(ref.member); // for lists 119 120 // for structures 121 each(ref.members || {}, function(key, value) { traverseShapeRef(value); }); 122 123 // resolve shape declarations 124 trackShapeDeclaration(ref); 125 } 126 127 function traverseOperation(op) { 128 deleteTraits(op); 129 130 delete op.name; 131 if (op.http) { 132 if (op.http.method === 'POST') delete op.http.method; 133 if (op.http.requestUri === '/') delete op.http.requestUri; 134 if (Object.keys(op.http).length === 0) delete op.http; 135 } 136 137 traverseShapeRef(op.input); 138 traverseShapeRef(op.output); 139 } 140 141 function traverseApi() { 142 deleteTraits(api); 143 each(api.operations, function(name, op) { traverseOperation(op); }); 144 api.shapes = visitedShapes; 145 } 146 147 traverseApi(); 148 pruneShapes(); 149 logResults(); 150 return api; 151 } 152 153 module.exports = Translator;