UNPKG

4.99 kBJavaScriptView Raw
1/* A couple of utility methods */
2
3function each(obj, iter) {
4 for (var key in obj) {
5 if (obj.hasOwnProperty(key)) iter(key, obj[key]);
6 }
7}
8
9function nextString(str) {
10 return 'S' + (parseInt(str.substr(1), 36) + 1).toString(36);
11}
12
13/* End utility methods */
14
15function 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)) {
101 //Translator prefers timestampFormat trait in members rather than in shape
102 if (map.refs[i].hasOwnProperty(prop) && ['timestampFormat'].indexOf(prop) >= 0) {
103 continue;
104 }
105 map.refs[i][prop] = shape[prop];
106 }
107
108 }
109 }
110
111 // delete the visited shape
112 delete visitedShapes[map.name];
113 debugInfo.pruned[name] = true;
114 }
115 });
116 }
117
118 function traverseShapeRef(ref) {
119 if (!ref) return;
120
121 deleteTraits(ref);
122
123 traverseShapeRef(ref.key); // for maps
124 traverseShapeRef(ref.value); // for maps
125 traverseShapeRef(ref.member); // for lists
126
127 // for structures
128 each(ref.members || {}, function(key, value) { traverseShapeRef(value); });
129
130 // resolve shape declarations
131 trackShapeDeclaration(ref);
132 }
133
134 function traverseOperation(op) {
135 deleteTraits(op);
136
137 delete op.name;
138 if (op.http) {
139 if (op.http.method === 'POST') delete op.http.method;
140 if (op.http.requestUri === '/') delete op.http.requestUri;
141 if (Object.keys(op.http).length === 0) delete op.http;
142 }
143
144 traverseShapeRef(op.input);
145 traverseShapeRef(op.output);
146 }
147
148 function traverseApi() {
149 deleteTraits(api);
150 each(api.operations, function(name, op) { traverseOperation(op); });
151 api.shapes = visitedShapes;
152 }
153
154 traverseApi();
155 pruneShapes();
156 logResults();
157 return api;
158}
159
160module.exports = Translator;