├── .gitignore
├── .travis.yml
├── _.js
├── __tests__
├── __snapshots__
│ └── fixtures.js.snap
├── array-mixed.js
├── array-numbers.js
├── array-objs-camel.js
├── array-objs.js
├── boolean.js
├── chrome
│ ├── chrome.json
│ └── out.d.ts
├── depth-01.js
├── depth-02.js
├── dup-02.js
├── dup-members-propname-mismatch.js
├── dup-members.js
├── dup-merge.js
├── dup.js
├── fixtures.js
├── flow.js
├── gmaps
│ ├── in.json
│ └── out.d.ts
├── invalid-interface-name.js
├── invalid-propname.js
├── large-01.js
├── magento
│ ├── categories-out.d.ts
│ ├── categories.json
│ ├── out.d.ts
│ └── product.json
├── missing-props.js
├── multi-complex.js
├── multi-missing.js
├── multi-union.js
├── multi
│ ├── 01.json
│ └── 02.json
├── namespace.js
├── null.js
├── numbers.js
├── petition
│ ├── input.json
│ └── output.d.ts
├── prefix-empty.js
├── prefix.js
├── rootName.js
├── swagger
│ ├── schema.d.ts
│ └── schema.json
├── top-level-array-mixed.js
├── top-level-array-objects.js
├── top-level-array.js
├── top-level-multi-arrays.js
└── top-level-string.js
├── crossbow.yml
├── docs
├── css
│ └── styles.css
├── dist
│ ├── codemirror.js
│ ├── index.js
│ ├── javascript.js
│ └── json-ts.min.js
├── index.html
├── javascript
│ ├── index.html
│ ├── javascript.js
│ ├── json-ld.html
│ └── typescript.html
├── json
│ ├── invalid-keys.json
│ ├── nested.json
│ ├── optional.json
│ └── recursive.json
├── lib
│ ├── codemirror.css
│ └── codemirror.js
├── package.json
├── src
│ └── index.js
└── yarn.lock
├── json-ts2.gif
├── package.json
├── readme.md
├── src
├── bin.ts
├── collapse-interfaces.ts
├── index.ts
├── parser.ts
├── printer.ts
├── transformer.ts
└── util.ts
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | node_modules
3 | parser.js
4 | printer.js
5 | transformer.js
6 | example.js
7 | src/*.js
8 | _.min.js
9 | /dist/**
10 | .crossbow/**
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | git:
3 | depth: 2
4 | language: node_js
5 | node_js:
6 | - "8"
7 | - "6"
--------------------------------------------------------------------------------
/_.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Lodash (Custom Build)
4 | * Build: `lodash include="startCase,toLower" -o _.js`
5 | * Copyright JS Foundation and other contributors
6 | * Released under MIT license
7 | * Based on Underscore.js 1.8.3
8 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
9 | */
10 | ;(function() {
11 |
12 | /** Used as a safe reference for `undefined` in pre-ES5 environments. */
13 | var undefined;
14 |
15 | /** Used as the semantic version number. */
16 | var VERSION = '4.17.4';
17 |
18 | /** Used as references for various `Number` constants. */
19 | var INFINITY = 1 / 0;
20 |
21 | /** `Object#toString` result references. */
22 | var nullTag = '[object Null]',
23 | symbolTag = '[object Symbol]',
24 | undefinedTag = '[object Undefined]';
25 |
26 | /** Used to match words composed of alphanumeric characters. */
27 | var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
28 |
29 | /** Used to match Latin Unicode letters (excluding mathematical operators). */
30 | var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g;
31 |
32 | /** Used to compose unicode character classes. */
33 | var rsAstralRange = '\\ud800-\\udfff',
34 | rsComboMarksRange = '\\u0300-\\u036f',
35 | reComboHalfMarksRange = '\\ufe20-\\ufe2f',
36 | rsComboSymbolsRange = '\\u20d0-\\u20ff',
37 | rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
38 | rsDingbatRange = '\\u2700-\\u27bf',
39 | rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff',
40 | rsMathOpRange = '\\xac\\xb1\\xd7\\xf7',
41 | rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf',
42 | rsPunctuationRange = '\\u2000-\\u206f',
43 | rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000',
44 | rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde',
45 | rsVarRange = '\\ufe0e\\ufe0f',
46 | rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;
47 |
48 | /** Used to compose unicode capture groups. */
49 | var rsApos = "['\u2019]",
50 | rsAstral = '[' + rsAstralRange + ']',
51 | rsBreak = '[' + rsBreakRange + ']',
52 | rsCombo = '[' + rsComboRange + ']',
53 | rsDigits = '\\d+',
54 | rsDingbat = '[' + rsDingbatRange + ']',
55 | rsLower = '[' + rsLowerRange + ']',
56 | rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']',
57 | rsFitz = '\\ud83c[\\udffb-\\udfff]',
58 | rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
59 | rsNonAstral = '[^' + rsAstralRange + ']',
60 | rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
61 | rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
62 | rsUpper = '[' + rsUpperRange + ']',
63 | rsZWJ = '\\u200d';
64 |
65 | /** Used to compose unicode regexes. */
66 | var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')',
67 | rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')',
68 | rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?',
69 | rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?',
70 | reOptMod = rsModifier + '?',
71 | rsOptVar = '[' + rsVarRange + ']?',
72 | rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
73 | rsOrdLower = '\\d*(?:(?:1st|2nd|3rd|(?![123])\\dth)\\b)',
74 | rsOrdUpper = '\\d*(?:(?:1ST|2ND|3RD|(?![123])\\dTH)\\b)',
75 | rsSeq = rsOptVar + reOptMod + rsOptJoin,
76 | rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq,
77 | rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
78 |
79 | /** Used to match apostrophes. */
80 | var reApos = RegExp(rsApos, 'g');
81 |
82 | /**
83 | * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and
84 | * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).
85 | */
86 | var reComboMark = RegExp(rsCombo, 'g');
87 |
88 | /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
89 | var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
90 |
91 | /** Used to match complex or compound words. */
92 | var reUnicodeWord = RegExp([
93 | rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')',
94 | rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')',
95 | rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower,
96 | rsUpper + '+' + rsOptContrUpper,
97 | rsOrdUpper,
98 | rsOrdLower,
99 | rsDigits,
100 | rsEmoji
101 | ].join('|'), 'g');
102 |
103 | /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
104 | var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');
105 |
106 | /** Used to detect strings that need a more robust regexp to match words. */
107 | var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;
108 |
109 | /** Used to map Latin Unicode letters to basic Latin letters. */
110 | var deburredLetters = {
111 | // Latin-1 Supplement block.
112 | '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A',
113 | '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a',
114 | '\xc7': 'C', '\xe7': 'c',
115 | '\xd0': 'D', '\xf0': 'd',
116 | '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E',
117 | '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e',
118 | '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I',
119 | '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i',
120 | '\xd1': 'N', '\xf1': 'n',
121 | '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O',
122 | '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o',
123 | '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U',
124 | '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u',
125 | '\xdd': 'Y', '\xfd': 'y', '\xff': 'y',
126 | '\xc6': 'Ae', '\xe6': 'ae',
127 | '\xde': 'Th', '\xfe': 'th',
128 | '\xdf': 'ss',
129 | // Latin Extended-A block.
130 | '\u0100': 'A', '\u0102': 'A', '\u0104': 'A',
131 | '\u0101': 'a', '\u0103': 'a', '\u0105': 'a',
132 | '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C',
133 | '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c',
134 | '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd',
135 | '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E',
136 | '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e',
137 | '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G',
138 | '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g',
139 | '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h',
140 | '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I',
141 | '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i',
142 | '\u0134': 'J', '\u0135': 'j',
143 | '\u0136': 'K', '\u0137': 'k', '\u0138': 'k',
144 | '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L',
145 | '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l',
146 | '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N',
147 | '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n',
148 | '\u014c': 'O', '\u014e': 'O', '\u0150': 'O',
149 | '\u014d': 'o', '\u014f': 'o', '\u0151': 'o',
150 | '\u0154': 'R', '\u0156': 'R', '\u0158': 'R',
151 | '\u0155': 'r', '\u0157': 'r', '\u0159': 'r',
152 | '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S',
153 | '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's',
154 | '\u0162': 'T', '\u0164': 'T', '\u0166': 'T',
155 | '\u0163': 't', '\u0165': 't', '\u0167': 't',
156 | '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U',
157 | '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u',
158 | '\u0174': 'W', '\u0175': 'w',
159 | '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y',
160 | '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z',
161 | '\u017a': 'z', '\u017c': 'z', '\u017e': 'z',
162 | '\u0132': 'IJ', '\u0133': 'ij',
163 | '\u0152': 'Oe', '\u0153': 'oe',
164 | '\u0149': "'n", '\u017f': 's'
165 | };
166 |
167 | /** Detect free variable `global` from Node.js. */
168 | var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
169 |
170 | /** Detect free variable `self`. */
171 | var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
172 |
173 | /** Used as a reference to the global object. */
174 | var root = freeGlobal || freeSelf || Function('return this')();
175 |
176 | /** Detect free variable `exports`. */
177 | var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
178 |
179 | /** Detect free variable `module`. */
180 | var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
181 |
182 | /*--------------------------------------------------------------------------*/
183 |
184 | /**
185 | * A specialized version of `_.map` for arrays without support for iteratee
186 | * shorthands.
187 | *
188 | * @private
189 | * @param {Array} [array] The array to iterate over.
190 | * @param {Function} iteratee The function invoked per iteration.
191 | * @returns {Array} Returns the new mapped array.
192 | */
193 | function arrayMap(array, iteratee) {
194 | var index = -1,
195 | length = array == null ? 0 : array.length,
196 | result = Array(length);
197 |
198 | while (++index < length) {
199 | result[index] = iteratee(array[index], index, array);
200 | }
201 | return result;
202 | }
203 |
204 | /**
205 | * A specialized version of `_.reduce` for arrays without support for
206 | * iteratee shorthands.
207 | *
208 | * @private
209 | * @param {Array} [array] The array to iterate over.
210 | * @param {Function} iteratee The function invoked per iteration.
211 | * @param {*} [accumulator] The initial value.
212 | * @param {boolean} [initAccum] Specify using the first element of `array` as
213 | * the initial value.
214 | * @returns {*} Returns the accumulated value.
215 | */
216 | function arrayReduce(array, iteratee, accumulator, initAccum) {
217 | var index = -1,
218 | length = array == null ? 0 : array.length;
219 |
220 | if (initAccum && length) {
221 | accumulator = array[++index];
222 | }
223 | while (++index < length) {
224 | accumulator = iteratee(accumulator, array[index], index, array);
225 | }
226 | return accumulator;
227 | }
228 |
229 | /**
230 | * Converts an ASCII `string` to an array.
231 | *
232 | * @private
233 | * @param {string} string The string to convert.
234 | * @returns {Array} Returns the converted array.
235 | */
236 | function asciiToArray(string) {
237 | return string.split('');
238 | }
239 |
240 | /**
241 | * Splits an ASCII `string` into an array of its words.
242 | *
243 | * @private
244 | * @param {string} The string to inspect.
245 | * @returns {Array} Returns the words of `string`.
246 | */
247 | function asciiWords(string) {
248 | return string.match(reAsciiWord) || [];
249 | }
250 |
251 | /**
252 | * The base implementation of `_.propertyOf` without support for deep paths.
253 | *
254 | * @private
255 | * @param {Object} object The object to query.
256 | * @returns {Function} Returns the new accessor function.
257 | */
258 | function basePropertyOf(object) {
259 | return function(key) {
260 | return object == null ? undefined : object[key];
261 | };
262 | }
263 |
264 | /**
265 | * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A
266 | * letters to basic Latin letters.
267 | *
268 | * @private
269 | * @param {string} letter The matched letter to deburr.
270 | * @returns {string} Returns the deburred letter.
271 | */
272 | var deburrLetter = basePropertyOf(deburredLetters);
273 |
274 | /**
275 | * Checks if `string` contains Unicode symbols.
276 | *
277 | * @private
278 | * @param {string} string The string to inspect.
279 | * @returns {boolean} Returns `true` if a symbol is found, else `false`.
280 | */
281 | function hasUnicode(string) {
282 | return reHasUnicode.test(string);
283 | }
284 |
285 | /**
286 | * Checks if `string` contains a word composed of Unicode symbols.
287 | *
288 | * @private
289 | * @param {string} string The string to inspect.
290 | * @returns {boolean} Returns `true` if a word is found, else `false`.
291 | */
292 | function hasUnicodeWord(string) {
293 | return reHasUnicodeWord.test(string);
294 | }
295 |
296 | /**
297 | * Converts `string` to an array.
298 | *
299 | * @private
300 | * @param {string} string The string to convert.
301 | * @returns {Array} Returns the converted array.
302 | */
303 | function stringToArray(string) {
304 | return hasUnicode(string)
305 | ? unicodeToArray(string)
306 | : asciiToArray(string);
307 | }
308 |
309 | /**
310 | * Converts a Unicode `string` to an array.
311 | *
312 | * @private
313 | * @param {string} string The string to convert.
314 | * @returns {Array} Returns the converted array.
315 | */
316 | function unicodeToArray(string) {
317 | return string.match(reUnicode) || [];
318 | }
319 |
320 | /**
321 | * Splits a Unicode `string` into an array of its words.
322 | *
323 | * @private
324 | * @param {string} The string to inspect.
325 | * @returns {Array} Returns the words of `string`.
326 | */
327 | function unicodeWords(string) {
328 | return string.match(reUnicodeWord) || [];
329 | }
330 |
331 | /*--------------------------------------------------------------------------*/
332 |
333 | /** Used for built-in method references. */
334 | var objectProto = Object.prototype;
335 |
336 | /** Used to check objects for own properties. */
337 | var hasOwnProperty = objectProto.hasOwnProperty;
338 |
339 | /**
340 | * Used to resolve the
341 | * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
342 | * of values.
343 | */
344 | var nativeObjectToString = objectProto.toString;
345 |
346 | /** Built-in value references. */
347 | var Symbol = root.Symbol,
348 | symToStringTag = Symbol ? Symbol.toStringTag : undefined;
349 |
350 | /** Used to lookup unminified function names. */
351 | var realNames = {};
352 |
353 | /** Used to convert symbols to primitives and strings. */
354 | var symbolProto = Symbol ? Symbol.prototype : undefined,
355 | symbolToString = symbolProto ? symbolProto.toString : undefined;
356 |
357 | /*------------------------------------------------------------------------*/
358 |
359 | /**
360 | * Creates a `lodash` object which wraps `value` to enable implicit method
361 | * chain sequences. Methods that operate on and return arrays, collections,
362 | * and functions can be chained together. Methods that retrieve a single value
363 | * or may return a primitive value will automatically end the chain sequence
364 | * and return the unwrapped value. Otherwise, the value must be unwrapped
365 | * with `_#value`.
366 | *
367 | * Explicit chain sequences, which must be unwrapped with `_#value`, may be
368 | * enabled using `_.chain`.
369 | *
370 | * The execution of chained methods is lazy, that is, it's deferred until
371 | * `_#value` is implicitly or explicitly called.
372 | *
373 | * Lazy evaluation allows several methods to support shortcut fusion.
374 | * Shortcut fusion is an optimization to merge iteratee calls; this avoids
375 | * the creation of intermediate arrays and can greatly reduce the number of
376 | * iteratee executions. Sections of a chain sequence qualify for shortcut
377 | * fusion if the section is applied to an array and iteratees accept only
378 | * one argument. The heuristic for whether a section qualifies for shortcut
379 | * fusion is subject to change.
380 | *
381 | * Chaining is supported in custom builds as long as the `_#value` method is
382 | * directly or indirectly included in the build.
383 | *
384 | * In addition to lodash methods, wrappers have `Array` and `String` methods.
385 | *
386 | * The wrapper `Array` methods are:
387 | * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`
388 | *
389 | * The wrapper `String` methods are:
390 | * `replace` and `split`
391 | *
392 | * The wrapper methods that support shortcut fusion are:
393 | * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,
394 | * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,
395 | * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`
396 | *
397 | * The chainable wrapper methods are:
398 | * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,
399 | * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,
400 | * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,
401 | * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,
402 | * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,
403 | * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,
404 | * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,
405 | * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,
406 | * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,
407 | * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,
408 | * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,
409 | * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,
410 | * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,
411 | * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,
412 | * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,
413 | * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,
414 | * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,
415 | * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,
416 | * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,
417 | * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,
418 | * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,
419 | * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,
420 | * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,
421 | * `zipObject`, `zipObjectDeep`, and `zipWith`
422 | *
423 | * The wrapper methods that are **not** chainable by default are:
424 | * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,
425 | * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`,
426 | * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`,
427 | * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`,
428 | * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`,
429 | * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`,
430 | * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`,
431 | * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`,
432 | * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`,
433 | * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`,
434 | * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`,
435 | * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`,
436 | * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`,
437 | * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`,
438 | * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`,
439 | * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`,
440 | * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`,
441 | * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`,
442 | * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`,
443 | * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`,
444 | * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`,
445 | * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`,
446 | * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`,
447 | * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`,
448 | * `upperFirst`, `value`, and `words`
449 | *
450 | * @name _
451 | * @constructor
452 | * @category Seq
453 | * @param {*} value The value to wrap in a `lodash` instance.
454 | * @returns {Object} Returns the new `lodash` wrapper instance.
455 | * @example
456 | *
457 | * function square(n) {
458 | * return n * n;
459 | * }
460 | *
461 | * var wrapped = _([1, 2, 3]);
462 | *
463 | * // Returns an unwrapped value.
464 | * wrapped.reduce(_.add);
465 | * // => 6
466 | *
467 | * // Returns a wrapped value.
468 | * var squares = wrapped.map(square);
469 | *
470 | * _.isArray(squares);
471 | * // => false
472 | *
473 | * _.isArray(squares.value());
474 | * // => true
475 | */
476 | function lodash() {
477 | // No operation performed.
478 | }
479 |
480 | /*------------------------------------------------------------------------*/
481 |
482 | /**
483 | * The base implementation of `getTag` without fallbacks for buggy environments.
484 | *
485 | * @private
486 | * @param {*} value The value to query.
487 | * @returns {string} Returns the `toStringTag`.
488 | */
489 | function baseGetTag(value) {
490 | if (value == null) {
491 | return value === undefined ? undefinedTag : nullTag;
492 | }
493 | return (symToStringTag && symToStringTag in Object(value))
494 | ? getRawTag(value)
495 | : objectToString(value);
496 | }
497 |
498 | /**
499 | * The base implementation of `_.slice` without an iteratee call guard.
500 | *
501 | * @private
502 | * @param {Array} array The array to slice.
503 | * @param {number} [start=0] The start position.
504 | * @param {number} [end=array.length] The end position.
505 | * @returns {Array} Returns the slice of `array`.
506 | */
507 | function baseSlice(array, start, end) {
508 | var index = -1,
509 | length = array.length;
510 |
511 | if (start < 0) {
512 | start = -start > length ? 0 : (length + start);
513 | }
514 | end = end > length ? length : end;
515 | if (end < 0) {
516 | end += length;
517 | }
518 | length = start > end ? 0 : ((end - start) >>> 0);
519 | start >>>= 0;
520 |
521 | var result = Array(length);
522 | while (++index < length) {
523 | result[index] = array[index + start];
524 | }
525 | return result;
526 | }
527 |
528 | /**
529 | * The base implementation of `_.toString` which doesn't convert nullish
530 | * values to empty strings.
531 | *
532 | * @private
533 | * @param {*} value The value to process.
534 | * @returns {string} Returns the string.
535 | */
536 | function baseToString(value) {
537 | // Exit early for strings to avoid a performance hit in some environments.
538 | if (typeof value == 'string') {
539 | return value;
540 | }
541 | if (isArray(value)) {
542 | // Recursively convert values (susceptible to call stack limits).
543 | return arrayMap(value, baseToString) + '';
544 | }
545 | if (isSymbol(value)) {
546 | return symbolToString ? symbolToString.call(value) : '';
547 | }
548 | var result = (value + '');
549 | return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
550 | }
551 |
552 | /**
553 | * Casts `array` to a slice if it's needed.
554 | *
555 | * @private
556 | * @param {Array} array The array to inspect.
557 | * @param {number} start The start position.
558 | * @param {number} [end=array.length] The end position.
559 | * @returns {Array} Returns the cast slice.
560 | */
561 | function castSlice(array, start, end) {
562 | var length = array.length;
563 | end = end === undefined ? length : end;
564 | return (!start && end >= length) ? array : baseSlice(array, start, end);
565 | }
566 |
567 | /**
568 | * Creates a function like `_.lowerFirst`.
569 | *
570 | * @private
571 | * @param {string} methodName The name of the `String` case method to use.
572 | * @returns {Function} Returns the new case function.
573 | */
574 | function createCaseFirst(methodName) {
575 | return function(string) {
576 | string = toString(string);
577 |
578 | var strSymbols = hasUnicode(string)
579 | ? stringToArray(string)
580 | : undefined;
581 |
582 | var chr = strSymbols
583 | ? strSymbols[0]
584 | : string.charAt(0);
585 |
586 | var trailing = strSymbols
587 | ? castSlice(strSymbols, 1).join('')
588 | : string.slice(1);
589 |
590 | return chr[methodName]() + trailing;
591 | };
592 | }
593 |
594 | /**
595 | * Creates a function like `_.camelCase`.
596 | *
597 | * @private
598 | * @param {Function} callback The function to combine each word.
599 | * @returns {Function} Returns the new compounder function.
600 | */
601 | function createCompounder(callback) {
602 | return function(string) {
603 | return arrayReduce(words(deburr(string).replace(reApos, '')), callback, '');
604 | };
605 | }
606 |
607 | /**
608 | * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
609 | *
610 | * @private
611 | * @param {*} value The value to query.
612 | * @returns {string} Returns the raw `toStringTag`.
613 | */
614 | function getRawTag(value) {
615 | var isOwn = hasOwnProperty.call(value, symToStringTag),
616 | tag = value[symToStringTag];
617 |
618 | try {
619 | value[symToStringTag] = undefined;
620 | var unmasked = true;
621 | } catch (e) {}
622 |
623 | var result = nativeObjectToString.call(value);
624 | if (unmasked) {
625 | if (isOwn) {
626 | value[symToStringTag] = tag;
627 | } else {
628 | delete value[symToStringTag];
629 | }
630 | }
631 | return result;
632 | }
633 |
634 | /**
635 | * Converts `value` to a string using `Object.prototype.toString`.
636 | *
637 | * @private
638 | * @param {*} value The value to convert.
639 | * @returns {string} Returns the converted string.
640 | */
641 | function objectToString(value) {
642 | return nativeObjectToString.call(value);
643 | }
644 |
645 | /*------------------------------------------------------------------------*/
646 |
647 | /**
648 | * Checks if `value` is classified as an `Array` object.
649 | *
650 | * @static
651 | * @memberOf _
652 | * @since 0.1.0
653 | * @category Lang
654 | * @param {*} value The value to check.
655 | * @returns {boolean} Returns `true` if `value` is an array, else `false`.
656 | * @example
657 | *
658 | * _.isArray([1, 2, 3]);
659 | * // => true
660 | *
661 | * _.isArray(document.body.children);
662 | * // => false
663 | *
664 | * _.isArray('abc');
665 | * // => false
666 | *
667 | * _.isArray(_.noop);
668 | * // => false
669 | */
670 | var isArray = Array.isArray;
671 |
672 | /**
673 | * Checks if `value` is object-like. A value is object-like if it's not `null`
674 | * and has a `typeof` result of "object".
675 | *
676 | * @static
677 | * @memberOf _
678 | * @since 4.0.0
679 | * @category Lang
680 | * @param {*} value The value to check.
681 | * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
682 | * @example
683 | *
684 | * _.isObjectLike({});
685 | * // => true
686 | *
687 | * _.isObjectLike([1, 2, 3]);
688 | * // => true
689 | *
690 | * _.isObjectLike(_.noop);
691 | * // => false
692 | *
693 | * _.isObjectLike(null);
694 | * // => false
695 | */
696 | function isObjectLike(value) {
697 | return value != null && typeof value == 'object';
698 | }
699 |
700 | /**
701 | * Checks if `value` is classified as a `Symbol` primitive or object.
702 | *
703 | * @static
704 | * @memberOf _
705 | * @since 4.0.0
706 | * @category Lang
707 | * @param {*} value The value to check.
708 | * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
709 | * @example
710 | *
711 | * _.isSymbol(Symbol.iterator);
712 | * // => true
713 | *
714 | * _.isSymbol('abc');
715 | * // => false
716 | */
717 | function isSymbol(value) {
718 | return typeof value == 'symbol' ||
719 | (isObjectLike(value) && baseGetTag(value) == symbolTag);
720 | }
721 |
722 | /**
723 | * Converts `value` to a string. An empty string is returned for `null`
724 | * and `undefined` values. The sign of `-0` is preserved.
725 | *
726 | * @static
727 | * @memberOf _
728 | * @since 4.0.0
729 | * @category Lang
730 | * @param {*} value The value to convert.
731 | * @returns {string} Returns the converted string.
732 | * @example
733 | *
734 | * _.toString(null);
735 | * // => ''
736 | *
737 | * _.toString(-0);
738 | * // => '-0'
739 | *
740 | * _.toString([1, 2, 3]);
741 | * // => '1,2,3'
742 | */
743 | function toString(value) {
744 | return value == null ? '' : baseToString(value);
745 | }
746 |
747 | /*------------------------------------------------------------------------*/
748 |
749 | /**
750 | * Deburrs `string` by converting
751 | * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)
752 | * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A)
753 | * letters to basic Latin letters and removing
754 | * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).
755 | *
756 | * @static
757 | * @memberOf _
758 | * @since 3.0.0
759 | * @category String
760 | * @param {string} [string=''] The string to deburr.
761 | * @returns {string} Returns the deburred string.
762 | * @example
763 | *
764 | * _.deburr('déjà vu');
765 | * // => 'deja vu'
766 | */
767 | function deburr(string) {
768 | string = toString(string);
769 | return string && string.replace(reLatin, deburrLetter).replace(reComboMark, '');
770 | }
771 |
772 | /**
773 | * Converts `string` to
774 | * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage).
775 | *
776 | * @static
777 | * @memberOf _
778 | * @since 3.1.0
779 | * @category String
780 | * @param {string} [string=''] The string to convert.
781 | * @returns {string} Returns the start cased string.
782 | * @example
783 | *
784 | * _.startCase('--foo-bar--');
785 | * // => 'Foo Bar'
786 | *
787 | * _.startCase('fooBar');
788 | * // => 'Foo Bar'
789 | *
790 | * _.startCase('__FOO_BAR__');
791 | * // => 'FOO BAR'
792 | */
793 | var startCase = createCompounder(function(result, word, index) {
794 | return result + (index ? ' ' : '') + upperFirst(word);
795 | });
796 |
797 | /**
798 | * Converts `string`, as a whole, to lower case just like
799 | * [String#toLowerCase](https://mdn.io/toLowerCase).
800 | *
801 | * @static
802 | * @memberOf _
803 | * @since 4.0.0
804 | * @category String
805 | * @param {string} [string=''] The string to convert.
806 | * @returns {string} Returns the lower cased string.
807 | * @example
808 | *
809 | * _.toLower('--Foo-Bar--');
810 | * // => '--foo-bar--'
811 | *
812 | * _.toLower('fooBar');
813 | * // => 'foobar'
814 | *
815 | * _.toLower('__FOO_BAR__');
816 | * // => '__foo_bar__'
817 | */
818 | function toLower(value) {
819 | return toString(value).toLowerCase();
820 | }
821 |
822 | /**
823 | * Converts the first character of `string` to upper case.
824 | *
825 | * @static
826 | * @memberOf _
827 | * @since 4.0.0
828 | * @category String
829 | * @param {string} [string=''] The string to convert.
830 | * @returns {string} Returns the converted string.
831 | * @example
832 | *
833 | * _.upperFirst('fred');
834 | * // => 'Fred'
835 | *
836 | * _.upperFirst('FRED');
837 | * // => 'FRED'
838 | */
839 | var upperFirst = createCaseFirst('toUpperCase');
840 |
841 | /**
842 | * Splits `string` into an array of its words.
843 | *
844 | * @static
845 | * @memberOf _
846 | * @since 3.0.0
847 | * @category String
848 | * @param {string} [string=''] The string to inspect.
849 | * @param {RegExp|string} [pattern] The pattern to match words.
850 | * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
851 | * @returns {Array} Returns the words of `string`.
852 | * @example
853 | *
854 | * _.words('fred, barney, & pebbles');
855 | * // => ['fred', 'barney', 'pebbles']
856 | *
857 | * _.words('fred, barney, & pebbles', /[^, ]+/g);
858 | * // => ['fred', 'barney', '&', 'pebbles']
859 | */
860 | function words(string, pattern, guard) {
861 | string = toString(string);
862 | pattern = guard ? undefined : pattern;
863 |
864 | if (pattern === undefined) {
865 | return hasUnicodeWord(string) ? unicodeWords(string) : asciiWords(string);
866 | }
867 | return string.match(pattern) || [];
868 | }
869 |
870 | /*------------------------------------------------------------------------*/
871 |
872 | // Add methods that return wrapped values in chain sequences.
873 | lodash.words = words;
874 |
875 | /*------------------------------------------------------------------------*/
876 |
877 | // Add methods that return unwrapped values in chain sequences.
878 | lodash.deburr = deburr;
879 | lodash.isArray = isArray;
880 | lodash.isObjectLike = isObjectLike;
881 | lodash.isSymbol = isSymbol;
882 | lodash.startCase = startCase;
883 | lodash.toLower = toLower;
884 | lodash.toString = toString;
885 | lodash.upperFirst = upperFirst;
886 |
887 | /*------------------------------------------------------------------------*/
888 |
889 | /**
890 | * The semantic version number.
891 | *
892 | * @static
893 | * @memberOf _
894 | * @type {string}
895 | */
896 | lodash.VERSION = VERSION;
897 |
898 | /*--------------------------------------------------------------------------*/
899 |
900 | // Some AMD build optimizers, like r.js, check for condition patterns like:
901 | if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
902 | // Expose Lodash on the global object to prevent errors when Lodash is
903 | // loaded by a script tag in the presence of an AMD loader.
904 | // See http://requirejs.org/docs/errors.html#mismatch for more details.
905 | // Use `_.noConflict` to remove Lodash from the global object.
906 | root._ = lodash;
907 |
908 | // Define as an anonymous module so, through path mapping, it can be
909 | // referenced as the "underscore" module.
910 | define(function() {
911 | return lodash;
912 | });
913 | }
914 | // Check for `exports` after `define` in case a build optimizer adds it.
915 | else if (freeModule) {
916 | // Export for Node.js.
917 | (freeModule.exports = lodash)._ = lodash;
918 | // Export for CommonJS support.
919 | freeExports._ = lodash;
920 | }
921 | else {
922 | // Export to the global object.
923 | root._ = lodash;
924 | }
925 | }.call(this));
926 |
--------------------------------------------------------------------------------
/__tests__/array-mixed.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "ids": [1, "2"]
6 | }
7 | `;
8 |
9 | const expected = `
10 | interface IRootObject {
11 | ids: (number | string)[];
12 | }
13 | `;
14 |
15 | it('works with array of mixed number/strings (any)', function() {
16 | expect(json2ts(json)).toEqual(expected.slice(1));
17 | });
18 |
--------------------------------------------------------------------------------
/__tests__/array-numbers.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "ids": [1, 2, 3],
6 | "user": {
7 | "pets": ["dog", "cat"]
8 | }
9 | }
10 | `;
11 |
12 | const expected = `
13 | interface IRootObject {
14 | ids: number[];
15 | user: IUser;
16 | }
17 | interface IUser {
18 | pets: string[];
19 | }
20 | `;
21 |
22 | it('works with array of numbers', function() {
23 | expect(json2ts(json)).toEqual(expected.slice(1));
24 | });
25 |
--------------------------------------------------------------------------------
/__tests__/array-objs-camel.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | //language=JSON
5 | const json = `{
6 | "carBrands": [
7 | {
8 | "brandName": "shane"
9 | },
10 | {
11 | "brandName": "sally"
12 | }
13 | ]
14 | }
15 | `;
16 |
17 | const expected = `
18 | interface IRootObject {
19 | carBrands: ICarBrandsItem[];
20 | }
21 | interface ICarBrandsItem {
22 | brandName: string;
23 | }
24 | `;
25 |
26 | it('works with array of mixed number/strings (any) with camel case array name', function() {
27 | expect(json2ts(json)).toEqual(expected.slice(1));
28 | });
29 |
--------------------------------------------------------------------------------
/__tests__/array-objs.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | //language=JSON
5 | const json = `{
6 | "users": [
7 | {
8 | "name": "shane"
9 | },
10 | {
11 | "name": "sally"
12 | }
13 | ]
14 | }
15 | `;
16 |
17 | const expected = `
18 | interface IRootObject {
19 | users: IUsersItem[];
20 | }
21 | interface IUsersItem {
22 | name: string;
23 | }
24 | `;
25 |
26 | it('works with array of mixed number/strings (any)', function() {
27 | expect(json2ts(json)).toEqual(expected.slice(1));
28 | });
29 |
--------------------------------------------------------------------------------
/__tests__/boolean.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "alive": true,
6 | "sad": false,
7 | "bools": [true, false]
8 | }
9 | `;
10 |
11 | const expected = `
12 | interface IRootObject {
13 | alive: boolean;
14 | sad: boolean;
15 | bools: boolean[];
16 | }
17 | `;
18 |
19 | it('bools', function() {
20 | expect(json2ts(json)).toEqual(expected.slice(1));
21 | });
22 |
--------------------------------------------------------------------------------
/__tests__/chrome/out.d.ts:
--------------------------------------------------------------------------------
1 | interface IRootObject {
2 | version: IVersion;
3 | domains: IDomainsItem[];
4 | }
5 |
6 | interface IVersion {
7 | major: string;
8 | minor: string;
9 | }
10 |
11 | interface IDomainsItem {
12 | domain: string;
13 | experimental?: boolean;
14 | types?: ITypesItem[];
15 | commands: ICommandsItem[];
16 | events?: IEventsItem[];
17 | description?: string;
18 | dependencies?: string[];
19 | deprecated?: boolean;
20 | }
21 |
22 | interface ICommandsItem {
23 | name: string;
24 | description?: string;
25 | returns?: IReturnsItem[];
26 | parameters?: IParametersItem[];
27 | experimental?: boolean;
28 | redirect?: string;
29 | handlers?: string[];
30 | }
31 |
32 | interface IEventsItem {
33 | name: string;
34 | description?: string;
35 | parameters: IParametersItem[];
36 | }
37 |
38 | interface IParametersItem {
39 | name: string;
40 | type?: string;
41 | description?: string;
42 | $ref?: string;
43 | optional?: boolean;
44 | }
45 |
46 | interface ITypesItem {
47 | id: string;
48 | type: string;
49 | 'enum'?: string[];
50 | description?: string;
51 | properties?: IPropertiesItem[];
52 | experimental?: boolean;
53 | items?: IItems;
54 | }
55 |
56 | interface IReturnsItem {
57 | name: string;
58 | type?: string;
59 | $ref?: string;
60 | experimental?: boolean;
61 | description?: string;
62 | items?: IItems;
63 | }
64 |
65 | interface IPropertiesItem {
66 | name: string;
67 | type?: string;
68 | optional: boolean;
69 | description: string;
70 | $ref?: string;
71 | }
72 |
73 | interface IItems {
74 | type: string;
75 | description: string;
76 | }
77 |
78 |
--------------------------------------------------------------------------------
/__tests__/depth-01.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "date": "01/02/03",
6 | "summary": {
7 | "url": "http://example.com",
8 | "path": "/where",
9 | "loc": {
10 | "lat": 10,
11 | "lng": 11,
12 | }
13 | }
14 | }
15 | `;
16 |
17 | const expected = `
18 | interface IRootObject {
19 | date: string;
20 | summary: ISummary;
21 | }
22 | interface ISummary {
23 | url: string;
24 | path: string;
25 | loc: ILoc;
26 | }
27 | interface ILoc {
28 | lat: number;
29 | lng: number;
30 | }
31 | `;
32 |
33 | it('works 1 level of depth', function() {
34 | expect(json2ts(json)).toEqual(expected.slice(1));
35 | });
36 |
--------------------------------------------------------------------------------
/__tests__/depth-02.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "date": "01/02/03",
6 | "summary": {
7 | "url": "http://example.com",
8 | "path": "/where",
9 | "loc": {
10 | "lat": 10,
11 | "lng": 11,
12 | "address": {
13 | "house_number": "01",
14 | "street": "big barn lane",
15 | }
16 | }
17 | }
18 | }
19 | `;
20 |
21 | const expected = `
22 | interface IRootObject {
23 | date: string;
24 | summary: ISummary;
25 | }
26 | interface ISummary {
27 | url: string;
28 | path: string;
29 | loc: ILoc;
30 | }
31 | interface ILoc {
32 | lat: number;
33 | lng: number;
34 | address: IAddress;
35 | }
36 | interface IAddress {
37 | house_number: string;
38 | street: string;
39 | }
40 | `;
41 |
42 | it('works 2 levels of depth', function() {
43 | expect(json2ts(json)).toEqual(expected.slice(1));
44 | });
45 |
--------------------------------------------------------------------------------
/__tests__/dup-02.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "user": {
6 | "name": "Shane",
7 | "age": 19
8 | },
9 | "admin": {
10 | "user": {
11 | "name": "Shane",
12 | "age": 20
13 | }
14 | }
15 | }
16 | `;
17 |
18 | const expected = `
19 | interface IRootObject {
20 | user: IUser;
21 | admin: IAdmin;
22 | }
23 | interface IUser {
24 | name: string;
25 | age: number;
26 | }
27 | interface IAdmin {
28 | user: IUser;
29 | }
30 | `;
31 |
32 | it('works when prop names differ, but members match', function() {
33 | expect(json2ts(json)).toEqual(expected.slice(1));
34 | });
35 |
--------------------------------------------------------------------------------
/__tests__/dup-members-propname-mismatch.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "geo": {
6 | "lat": 2,
7 | "lng": 3
8 | },
9 | "loc": {
10 | "lat": 2,
11 | "lng": 3
12 | },
13 | "geometry": {
14 | "bounds": {
15 | "lat": 2,
16 | "lng": 3
17 | }
18 | }
19 | }
20 | `;
21 |
22 | const expected = `
23 | interface IRootObject {
24 | geo: IGeo;
25 | loc: ILoc;
26 | geometry: IGeometry;
27 | }
28 | interface IGeo {
29 | lat: number;
30 | lng: number;
31 | }
32 | interface ILoc {
33 | lat: number;
34 | lng: number;
35 | }
36 | interface IGeometry {
37 | bounds: IBounds;
38 | }
39 | interface IBounds {
40 | lat: number;
41 | lng: number;
42 | }
43 | `;
44 |
45 | it('works when prop names differ, but members match', function() {
46 | expect(json2ts(json)).toEqual(expected.slice(1));
47 | });
48 |
--------------------------------------------------------------------------------
/__tests__/dup-members.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "user": {
6 | "name": "Shane",
7 | "age": 19
8 | },
9 | "admin": {
10 | "user": {
11 | "name": "Shane",
12 | "age": 20
13 | }
14 | }
15 | }
16 | `;
17 |
18 | const expected = `
19 | interface IRootObject {
20 | user: IUser;
21 | admin: IAdmin;
22 | }
23 | interface IUser {
24 | name: string;
25 | age: number;
26 | }
27 | interface IAdmin {
28 | user: IUser;
29 | }
30 | `;
31 |
32 | it('works when prop names differ, but members match', function() {
33 | expect(json2ts(json)).toEqual(expected.slice(1));
34 | });
35 |
--------------------------------------------------------------------------------
/__tests__/dup-merge.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `
5 | {
6 | "items": [
7 | {
8 | "attribute_code": "category_ids",
9 | "value": [
10 | "3",
11 | "5"
12 | ]
13 | },
14 | {
15 | "attribute_code": "options_container",
16 | "value": "container2"
17 | }
18 | ]
19 | }
20 | `;
21 |
22 | const expected = `
23 | interface IRootObject {
24 | items: IItemsItem[];
25 | }
26 | interface IItemsItem {
27 | attribute_code: string;
28 | value: string[] | string;
29 | }
30 | `;
31 |
32 | it('can collapse interfaces into union types', function() {
33 | expect(json2ts(json)).toEqual(expected.slice(1));
34 | });
35 |
--------------------------------------------------------------------------------
/__tests__/dup.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "summary": {
6 | "loc": {
7 | "lat": 10,
8 | "lng": 11
9 | }
10 | },
11 | "loc": {
12 | "lat": 10,
13 | "lng": 11
14 | }
15 | }
16 | `;
17 |
18 | const expected = `
19 | interface IRootObject {
20 | summary: ISummary;
21 | loc: ILoc;
22 | }
23 | interface ISummary {
24 | loc: ILoc;
25 | }
26 | interface ILoc {
27 | lat: number;
28 | lng: number;
29 | }
30 | `;
31 |
32 | it('works when prop name + members match', function() {
33 | expect(json2ts(json)).toEqual(expected.slice(1));
34 | });
35 |
--------------------------------------------------------------------------------
/__tests__/fixtures.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const fs = require('fs');
3 | const data = (name) => fs.readFileSync(name, 'utf8');
4 |
5 | it('matches gmaps', function () {
6 | const gmaps = data('__tests__/gmaps/in.json');
7 | expect(json2ts(gmaps)).toMatchSnapshot();
8 | });
9 |
10 | it('matches magento', function () {
11 | const gmaps = data('__tests__/magento/product.json');
12 | expect(json2ts(gmaps)).toMatchSnapshot();
13 | });
14 |
15 | it('matches magento categories', function () {
16 | const magentoCategories = data('__tests__/magento/categories.json');
17 | expect(json2ts(magentoCategories)).toMatchSnapshot();
18 | });
19 |
20 | it('matches petition', function () {
21 | const gmaps = data('__tests__/petition/input.json');
22 | expect(json2ts(gmaps)).toMatchSnapshot();
23 | });
24 |
25 | it('matches swagger', function () {
26 | const gmaps = data('__tests__/swagger/schema.json');
27 | expect(json2ts(gmaps)).toMatchSnapshot();
28 | });
29 |
30 |
--------------------------------------------------------------------------------
/__tests__/flow.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "alive": true,
6 | "sad": false,
7 | "bools": [true, false]
8 | }
9 | `;
10 |
11 | const expected = `
12 | // @flow
13 | export type IRootObject = {
14 | alive: boolean;
15 | sad: boolean;
16 | bools: boolean[];
17 | };
18 | `;
19 |
20 | it('supports flow output', function() {
21 | expect(json2ts(json, {flow: true})).toEqual(expected.slice(1));
22 | });
23 |
--------------------------------------------------------------------------------
/__tests__/gmaps/in.json:
--------------------------------------------------------------------------------
1 | {
2 | "results" : [
3 | {
4 | "address_components" : [
5 | {
6 | "long_name" : "London",
7 | "short_name" : "London",
8 | "types" : [ "locality", "political" ]
9 | },
10 | {
11 | "long_name" : "London",
12 | "short_name" : "London",
13 | "types" : [ "postal_town" ]
14 | },
15 | {
16 | "long_name" : "Greater London",
17 | "short_name" : "Greater London",
18 | "types" : [ "administrative_area_level_2", "political" ]
19 | },
20 | {
21 | "long_name" : "England",
22 | "short_name" : "England",
23 | "types" : [ "administrative_area_level_1", "political" ]
24 | },
25 | {
26 | "long_name" : "United Kingdom",
27 | "short_name" : "GB",
28 | "types" : [ "country", "political" ]
29 | }
30 | ],
31 | "formatted_address" : "London, UK",
32 | "geometry" : {
33 | "bounds" : {
34 | "northeast" : {
35 | "lat" : 51.6723432,
36 | "lng" : 0.148271
37 | },
38 | "southwest" : {
39 | "lat" : 51.38494009999999,
40 | "lng" : -0.3514683
41 | }
42 | },
43 | "location" : {
44 | "lat" : 51.5073509,
45 | "lng" : -0.1277583
46 | },
47 | "location_type" : "APPROXIMATE",
48 | "viewport" : {
49 | "northeast" : {
50 | "lat" : 51.6723432,
51 | "lng" : 0.1482319
52 | },
53 | "southwest" : {
54 | "lat" : 51.38494009999999,
55 | "lng" : -0.3514683
56 | }
57 | }
58 | },
59 | "place_id" : "ChIJdd4hrwug2EcRmSrV3Vo6llI",
60 | "types" : [ "locality", "political" ]
61 | }
62 | ],
63 | "status" : "OK"
64 | }
65 |
--------------------------------------------------------------------------------
/__tests__/gmaps/out.d.ts:
--------------------------------------------------------------------------------
1 | interface IRootObject {
2 | version: IVersion;
3 | domains: IDomainsItem[];
4 | }
5 |
6 | interface IVersion {
7 | major: string;
8 | minor: string;
9 | }
10 |
11 | interface IDomainsItem {
12 | domain: string;
13 | experimental?: boolean;
14 | types?: ITypesItem[];
15 | commands: ICommandsItem[];
16 | events?: IEventsItem[];
17 | description?: string;
18 | dependencies?: string[];
19 | deprecated?: boolean;
20 | }
21 |
22 | interface ICommandsItem {
23 | name: string;
24 | description?: string;
25 | returns?: IReturnsItem[];
26 | parameters?: IParametersItem[];
27 | experimental?: boolean;
28 | redirect?: string;
29 | handlers?: string[];
30 | }
31 |
32 | interface IEventsItem {
33 | name: string;
34 | description?: string;
35 | parameters?: IParametersItem[];
36 | experimental?: boolean;
37 | }
38 |
39 | interface IParametersItem {
40 | name: string;
41 | type?: string;
42 | description?: string;
43 | $ref?: string;
44 | optional?: boolean;
45 | experimental?: boolean;
46 | 'enum'?: string[];
47 | deprecated?: boolean;
48 | items?: IItems;
49 | minItems?: number;
50 | }
51 |
52 | interface ITypesItem {
53 | id: string;
54 | type: string;
55 | 'enum'?: string[];
56 | description?: string;
57 | properties?: IPropertiesItem[];
58 | experimental?: boolean;
59 | items?: IItems;
60 | minItems?: number;
61 | maxItems?: number;
62 | }
63 |
64 | interface IReturnsItem {
65 | name: string;
66 | type?: string;
67 | $ref?: string;
68 | description?: string;
69 | experimental?: boolean;
70 | items?: IItems;
71 | optional?: boolean;
72 | }
73 |
74 | interface IPropertiesItem {
75 | name: string;
76 | type?: string;
77 | description?: string;
78 | optional?: boolean;
79 | $ref?: string;
80 | items?: IItems;
81 | experimental?: boolean;
82 | 'enum'?: string[];
83 | minItems?: number;
84 | maxItems?: number;
85 | }
86 |
87 | interface IItems {
88 | $ref?: string;
89 | type?: string;
90 | 'enum'?: string[];
91 | description?: string;
92 | }
93 |
--------------------------------------------------------------------------------
/__tests__/invalid-interface-name.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "POST /here": {
6 | "body": {
7 | "name": "string"
8 | }
9 | }
10 | }
11 | `;
12 |
13 | const expected = `
14 | interface IRootObject {
15 | 'POST /here': {
16 | body: IBody;
17 | };
18 | }
19 | interface IBody {
20 | name: string;
21 | }
22 | `;
23 |
24 | it('bails on interface extraction if name invalid', function() {
25 | expect(json2ts(json)).toEqual(expected.slice(1));
26 | });
27 |
--------------------------------------------------------------------------------
/__tests__/invalid-propname.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "in^valid": "here";
6 | }
7 | `;
8 |
9 | const expected = `
10 | interface IRootObject {
11 | 'in^valid': string;
12 | }
13 | `;
14 |
15 | it('quotes invalid property names', function() {
16 | expect(json2ts(json)).toEqual(expected.slice(1));
17 | });
18 |
--------------------------------------------------------------------------------
/__tests__/large-01.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `
5 | {
6 | "id": 15,
7 | "sku": "24-UG06",
8 | "name": "Affirm Water Bottle",
9 | "attribute_set_id": 11,
10 | "price": 7,
11 | "status": 1,
12 | "visibility": 4,
13 | "type_id": "simple",
14 | "created_at": "2016-07-11 10:19:09",
15 | "updated_at": "2016-07-11 10:19:09",
16 | "extension_attributes": [],
17 | "product_links": [],
18 | "options": [],
19 | "media_gallery_entries": [
20 | {
21 | "id": 19,
22 | "media_type": "image",
23 | "label": "Image",
24 | "position": 1,
25 | "disabled": false,
26 | "types": [
27 | "image",
28 | "small_image",
29 | "thumbnail"
30 | ],
31 | "file": "\/u\/g\/ug06-lb-0.jpg"
32 | },
33 | {
34 | "id": 20,
35 | "media_type": "image",
36 | "label": "Image",
37 | "position": 2,
38 | "disabled": false,
39 | "types": [
40 | "image",
41 | "small_image",
42 | "thumbnail"
43 | ],
44 | "file": "\/u\/g\/ug07-bk-0.jpg"
45 | },
46 | {
47 | "id": 21,
48 | "media_type": "image",
49 | "label": "Image",
50 | "position": 3,
51 | "disabled": false,
52 | "types": [
53 | "image",
54 | "small_image",
55 | "thumbnail"
56 | ],
57 | "file": "\/u\/g\/ug07-bk-0_alt1.jpg"
58 | },
59 | {
60 | "id": 22,
61 | "media_type": "image",
62 | "label": "Image",
63 | "position": 4,
64 | "disabled": false,
65 | "types": [
66 | "image",
67 | "small_image",
68 | "thumbnail"
69 | ],
70 | "file": "\/u\/g\/ug07-bk-0_alt1.jpg"
71 | }
72 | ],
73 | "tier_prices": [],
74 | "custom_attributes": [
75 | {
76 | "attribute_code": "description",
77 | "value": "You'll stay hydrated with ease with the Affirm Water Bottle by your side or in hand. Measurements on the outside help you keep track of how much you're drinking, while the screw-top lid prevents spills. A metal carabiner clip allows you to attach it to the outside of a backpack or bag for easy access.<\/p>\n
\nMade of plastic.<\/li>\n Grooved sides for an easy grip.<\/li>\n<\/ul>"
78 | },
79 | {
80 | "attribute_code": "image",
81 | "value": "\/u\/g\/ug06-lb-0.jpg"
82 | },
83 | {
84 | "attribute_code": "small_image",
85 | "value": "\/u\/g\/ug06-lb-0.jpg"
86 | },
87 | {
88 | "attribute_code": "thumbnail",
89 | "value": "\/u\/g\/ug06-lb-0.jpg"
90 | },
91 | {
92 | "attribute_code": "category_ids",
93 | "value": [
94 | "3",
95 | "5"
96 | ]
97 | },
98 | {
99 | "attribute_code": "options_container",
100 | "value": "container2"
101 | },
102 | {
103 | "attribute_code": "required_options",
104 | "value": "0"
105 | },
106 | {
107 | "attribute_code": "has_options",
108 | "value": "0"
109 | },
110 | {
111 | "attribute_code": "url_key",
112 | "value": "affirm-water-bottle"
113 | },
114 | {
115 | "attribute_code": "tax_class_id",
116 | "value": "2"
117 | },
118 | {
119 | "attribute_code": "activity",
120 | "value": "8,9,17,11"
121 | },
122 | {
123 | "attribute_code": "material",
124 | "value": "44"
125 | },
126 | {
127 | "attribute_code": "gender",
128 | "value": "80,81,82,83,84"
129 | },
130 | {
131 | "attribute_code": "category_gear",
132 | "value": "87,89"
133 | }
134 | ]
135 | }
136 | `;
137 |
138 | const expected = `
139 | interface IRootObject {
140 | id: number;
141 | sku: string;
142 | name: string;
143 | attribute_set_id: number;
144 | price: number;
145 | status: number;
146 | visibility: number;
147 | type_id: string;
148 | created_at: string;
149 | updated_at: string;
150 | extension_attributes: any[];
151 | product_links: any[];
152 | options: any[];
153 | media_gallery_entries: IMediaGalleryEntriesItem[];
154 | tier_prices: any[];
155 | custom_attributes: ICustomAttributesItem[];
156 | }
157 | interface IMediaGalleryEntriesItem {
158 | id: number;
159 | media_type: string;
160 | label: string;
161 | position: number;
162 | disabled: boolean;
163 | types: string[];
164 | file: string;
165 | }
166 | interface ICustomAttributesItem {
167 | attribute_code: string;
168 | value: string | string[];
169 | }
170 | `;
171 |
172 | it('works with large objects', function() {
173 | expect(json2ts(json)).toEqual(expected.slice(1));
174 | });
175 |
--------------------------------------------------------------------------------
/__tests__/magento/categories-out.d.ts:
--------------------------------------------------------------------------------
1 | interface IChildrenDataItem {
2 | id: number;
3 | parent_id: number;
4 | name: string;
5 | image: null|string;
6 | is_active: boolean;
7 | include_in_menu: boolean;
8 | position: number;
9 | level: number;
10 | product_count: number;
11 | children_data: IChildrenDataItem[];
12 | }
13 |
--------------------------------------------------------------------------------
/__tests__/magento/out.d.ts:
--------------------------------------------------------------------------------
1 | interface IRootObject {
2 | id: number;
3 | sku: string;
4 | name: string;
5 | attribute_set_id: number;
6 | price: number;
7 | status: number;
8 | visibility: number;
9 | type_id: string;
10 | created_at: string;
11 | updated_at: string;
12 | extension_attributes: Array;
13 | product_links: Array;
14 | options: Array;
15 | media_gallery_entries: IMediaGalleryEntriesItem[];
16 | tier_prices: Array;
17 | custom_attributes: ICustomAttributesItem[];
18 | }
19 |
20 | interface IMediaGalleryEntriesItem {
21 | id: number;
22 | media_type: string;
23 | label: string;
24 | position: number;
25 | disabled: boolean;
26 | types: string[];
27 | file: string;
28 | }
29 |
30 | interface ICustomAttributesItem {
31 | attribute_code: string;
32 | value: string|string[];
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/__tests__/magento/product.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": 15,
3 | "sku": "24-UG06",
4 | "name": "Affirm Water Bottle",
5 | "attribute_set_id": 11,
6 | "price": 7,
7 | "status": 1,
8 | "visibility": 4,
9 | "type_id": "simple",
10 | "created_at": "2016-07-11 10:19:09",
11 | "updated_at": "2016-07-11 10:19:09",
12 | "extension_attributes": [],
13 | "product_links": [],
14 | "options": [],
15 | "media_gallery_entries": [
16 | {
17 | "id": 19,
18 | "media_type": "image",
19 | "label": "Image",
20 | "position": 1,
21 | "disabled": false,
22 | "types": [
23 | "image",
24 | "small_image",
25 | "thumbnail"
26 | ],
27 | "file": "\/u\/g\/ug06-lb-0.jpg"
28 | },
29 | {
30 | "id": 20,
31 | "media_type": "image",
32 | "label": "Image",
33 | "position": 2,
34 | "disabled": false,
35 | "types": [
36 | "image",
37 | "small_image",
38 | "thumbnail"
39 | ],
40 | "file": "\/u\/g\/ug07-bk-0.jpg"
41 | },
42 | {
43 | "id": 21,
44 | "media_type": "image",
45 | "label": "Image",
46 | "position": 3,
47 | "disabled": false,
48 | "types": [
49 | "image",
50 | "small_image",
51 | "thumbnail"
52 | ],
53 | "file": "\/u\/g\/ug07-bk-0_alt1.jpg"
54 | },
55 | {
56 | "id": 22,
57 | "media_type": "image",
58 | "label": "Image",
59 | "position": 4,
60 | "disabled": false,
61 | "types": [
62 | "image",
63 | "small_image",
64 | "thumbnail"
65 | ],
66 | "file": "\/u\/g\/ug07-bk-0_alt1.jpg"
67 | }
68 | ],
69 | "tier_prices": [],
70 | "custom_attributes": [
71 | {
72 | "attribute_code": "description",
73 | "value": "You'll stay hydrated with ease with the Affirm Water Bottle by your side or in hand. Measurements on the outside help you keep track of how much you're drinking, while the screw-top lid prevents spills. A metal carabiner clip allows you to attach it to the outside of a backpack or bag for easy access.<\/p>\n
\nMade of plastic.<\/li>\n Grooved sides for an easy grip.<\/li>\n<\/ul>"
74 | },
75 | {
76 | "attribute_code": "image",
77 | "value": "\/u\/g\/ug06-lb-0.jpg"
78 | },
79 | {
80 | "attribute_code": "small_image",
81 | "value": "\/u\/g\/ug06-lb-0.jpg"
82 | },
83 | {
84 | "attribute_code": "thumbnail",
85 | "value": "\/u\/g\/ug06-lb-0.jpg"
86 | },
87 | {
88 | "attribute_code": "category_ids",
89 | "value": [
90 | "3",
91 | "5"
92 | ]
93 | },
94 | {
95 | "attribute_code": "options_container",
96 | "value": "container2"
97 | },
98 | {
99 | "attribute_code": "required_options",
100 | "value": "0"
101 | },
102 | {
103 | "attribute_code": "has_options",
104 | "value": "0"
105 | },
106 | {
107 | "attribute_code": "url_key",
108 | "value": "affirm-water-bottle"
109 | },
110 | {
111 | "attribute_code": "tax_class_id",
112 | "value": "2"
113 | },
114 | {
115 | "attribute_code": "activity",
116 | "value": "8,9,17,11"
117 | },
118 | {
119 | "attribute_code": "material",
120 | "value": "44"
121 | },
122 | {
123 | "attribute_code": "gender",
124 | "value": "80,81,82,83,84"
125 | },
126 | {
127 | "attribute_code": "category_gear",
128 | "value": "87,89"
129 | }
130 | ]
131 | }
--------------------------------------------------------------------------------
/__tests__/missing-props.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 |
3 | const json = `
4 | {
5 | "items": [
6 | {
7 | "pets": [1, 2]
8 | },
9 | {
10 | "name": "shane"
11 | }
12 | ]
13 | }
14 | `;
15 |
16 | const expected = `
17 | interface IRootObject {
18 | items: IItemsItem[];
19 | }
20 | interface IItemsItem {
21 | pets?: number[];
22 | name?: string;
23 | }
24 | `;
25 |
26 | it('can mark multiple missing props', function() {
27 | // console.log(json2ts(json));
28 | expect(json2ts(json)).toEqual(expected.slice(1));
29 | });
30 |
--------------------------------------------------------------------------------
/__tests__/multi-complex.js:
--------------------------------------------------------------------------------
1 | const {json2tsMulti} = require('../');
2 |
3 | // language=JSON
4 | const json = `
5 | {
6 | "status": "success",
7 | "cart": {
8 | "basket": {
9 | "type": "",
10 | "tax_label": "(Ex VAT)"
11 | }
12 | }
13 | }
14 | `;
15 |
16 | // language=JSON
17 | const json2 = `
18 | {
19 | "status": "success",
20 | "cart": {
21 | "basket": {
22 | "type": "collect",
23 | "tax_label": "(Ex VAT)",
24 | "branch": "S01"
25 | }
26 | }
27 | }
28 | `;
29 |
30 | const expected = `
31 | interface IRootObject {
32 | status: string;
33 | cart: ICart;
34 | }
35 | interface ICart {
36 | basket: IBasket;
37 | }
38 | interface IBasket {
39 | type: string;
40 | tax_label: string;
41 | branch?: string;
42 | }
43 | `;
44 |
45 | it('multiple inputs - when one is missing in each', function() {
46 | expect(json2tsMulti([json2, json])).toEqual(expected.slice(1));
47 | // console.log(json2tsMulti([json2, json]));
48 | });
49 |
--------------------------------------------------------------------------------
/__tests__/multi-missing.js:
--------------------------------------------------------------------------------
1 | const {json2tsMulti} = require('../');
2 |
3 | const json = `{
4 | "name": "Shane"
5 | }
6 | `;
7 | const json2 = `{
8 | "pets": "kittie"
9 | }
10 | `;
11 |
12 | const expected = `
13 | interface IRootObject {
14 | pets?: string;
15 | name?: string;
16 | }
17 | `;
18 |
19 | it('multiple inputs - when one is missing in each', function() {
20 | expect(json2tsMulti([json2, json])).toEqual(expected.slice(1));
21 | });
22 |
--------------------------------------------------------------------------------
/__tests__/multi-union.js:
--------------------------------------------------------------------------------
1 | const {json2tsMulti} = require('../');
2 |
3 | const json = `{
4 | "name": "Shane"
5 | }
6 | `;
7 | const json2 = `{
8 | "name": null
9 | }
10 | `;
11 |
12 | const expected = `
13 | interface IRootObject {
14 | name: string | null;
15 | }
16 | `;
17 |
18 | it('multiple inputs - union', function() {
19 | expect(json2tsMulti([json, json2])).toEqual(expected.slice(1));
20 | });
21 |
--------------------------------------------------------------------------------
/__tests__/multi/01.json:
--------------------------------------------------------------------------------
1 | {
2 | "status": "success",
3 | "cart": {
4 | "basket": {
5 | "type": "",
6 | "tax_label": "(Ex VAT)"
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/__tests__/multi/02.json:
--------------------------------------------------------------------------------
1 | {
2 | "status": "success",
3 | "cart": {
4 | "basket": {
5 | "type": "",
6 | "tax_label": "(Ex VAT)",
7 | "branch": "S01"
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/__tests__/namespace.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `
5 | {
6 | "items": [
7 | {
8 | "attribute_code": "category_ids",
9 | "value": [1]
10 | },
11 | {
12 | "attribute_code": "category_ids_2",
13 | "value": [2, 3]
14 | }
15 | ]
16 | }
17 | `;
18 |
19 | const expected = `
20 | declare namespace Project {
21 | export interface IRootObject {
22 | items: IItemsItem[];
23 | }
24 | export interface IItemsItem {
25 | attribute_code: string;
26 | value: number[];
27 | }
28 | }
29 | `;
30 |
31 | it('can wrap in namespace', function() {
32 | expect(json2ts(json, {namespace: 'Project'})).toEqual(expected.slice(1));
33 | });
34 |
--------------------------------------------------------------------------------
/__tests__/null.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `{
5 | "alive": null,
6 | "nulls": [null, null]
7 | }
8 | `;
9 |
10 | const expected = `
11 | interface IRootObject {
12 | alive: null;
13 | nulls: null[];
14 | }
15 | `;
16 |
17 | it('nulls', function() {
18 | expect(json2ts(json)).toEqual(expected.slice(1));
19 | });
20 |
--------------------------------------------------------------------------------
/__tests__/numbers.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `
5 | {
6 | "num": 120,
7 | "neg": -120,
8 | "decimal": 1.2,
9 | "nums": [1, -1, 1.4]
10 | }
11 | `;
12 |
13 | const expected = `
14 | interface IRootObject {
15 | num: number;
16 | neg: number;
17 | decimal: number;
18 | nums: number[];
19 | }
20 | `;
21 |
22 | it('works with array of numbers', function() {
23 | expect(json2ts(json)).toEqual(expected.slice(1));
24 | });
25 |
--------------------------------------------------------------------------------
/__tests__/petition/input.json:
--------------------------------------------------------------------------------
1 | { "format" : "linked-data-api", "version" : "0.2", "result" : {"_about" : "http://lda.data.parliament.uk/epetitions.json", "definition" : "http://lda.data.parliament.uk/meta/epetitions.json", "extendedMetadataVersion" : "http://lda.data.parliament.uk/epetitions.json?_metadata=all", "first" : "http://lda.data.parliament.uk/epetitions.json?_page=0", "hasPart" : "http://lda.data.parliament.uk/epetitions.json", "isPartOf" : "http://lda.data.parliament.uk/epetitions.json", "items" : [{"_about" : "http://data.parliament.uk/resources/727136", "created" : {"_value" : "2017-04-18T11:53:42.241Z", "_datatype" : "dateTime"}
2 | , "identifier" : {"_value" : "195537"}
3 | , "label" : {"_value" : "Referendum on Brexit to be held simultaneously to the election on 8th June."}
4 | , "numberOfSignatures" : 601, "status" : "closed"}
5 | , {"_about" : "http://data.parliament.uk/resources/726572", "created" : {"_value" : "2017-04-18T10:32:53.749Z", "_datatype" : "dateTime"}
6 | , "identifier" : {"_value" : "195498"}
7 | , "label" : {"_value" : "Invite international election observers to monitor the next General Election."}
8 | , "numberOfSignatures" : 1169, "status" : "closed"}
9 | , {"_about" : "http://data.parliament.uk/resources/727395", "created" : {"_value" : "2017-04-18T10:19:08.887Z", "_datatype" : "dateTime"}
10 | , "identifier" : {"_value" : "195490"}
11 | , "label" : {"_value" : "Allow 16 and 17 year olds to vote in the General Election on the 8th of June."}
12 | , "numberOfSignatures" : 444, "status" : "closed"}
13 | , {"_about" : "http://data.parliament.uk/resources/726558", "created" : {"_value" : "2017-04-16T19:10:27.188Z", "_datatype" : "dateTime"}
14 | , "identifier" : {"_value" : "195342"}
15 | , "label" : {"_value" : "The UK Government should retain our EU Renewable Energy targets as a minimum"}
16 | , "numberOfSignatures" : 1194, "status" : "closed"}
17 | , {"_about" : "http://data.parliament.uk/resources/726079", "created" : {"_value" : "2017-04-16T06:26:13.511Z", "_datatype" : "dateTime"}
18 | , "identifier" : {"_value" : "195293"}
19 | , "label" : {"_value" : "Afghans should get UK settlement, visit and student visa decision in two weeks."}
20 | , "numberOfSignatures" : 3118, "status" : "closed"}
21 | , {"_about" : "http://data.parliament.uk/resources/726425", "created" : {"_value" : "2017-04-14T19:45:15.621Z", "_datatype" : "dateTime"}
22 | , "identifier" : {"_value" : "195193"}
23 | , "label" : {"_value" : "Renew the UK work visa for the professional wrestlers Dahlia Black and TK Cooper"}
24 | , "numberOfSignatures" : 1533, "status" : "closed"}
25 | , {"_about" : "http://data.parliament.uk/resources/723962", "created" : {"_value" : "2017-04-14T09:01:17.448Z", "_datatype" : "dateTime"}
26 | , "identifier" : {"_value" : "195146"}
27 | , "label" : {"_value" : "Publicly confirm that gay men from Chechnya can seek asylum in the UK"}
28 | , "numberOfSignatures" : 20459, "status" : "closed"}
29 | , {"_about" : "http://data.parliament.uk/resources/721197", "created" : {"_value" : "2017-04-13T10:31:22.885Z", "_datatype" : "dateTime"}
30 | , "identifier" : {"_value" : "195077"}
31 | , "label" : {"_value" : "Scrap the \"rape clause\" and the \"family cap\" on social security payments."}
32 | , "numberOfSignatures" : 25307, "status" : "closed"}
33 | , {"_about" : "http://data.parliament.uk/resources/726101", "created" : {"_value" : "2017-04-12T12:47:05.547Z", "_datatype" : "dateTime"}
34 | , "identifier" : {"_value" : "194974"}
35 | , "label" : {"_value" : "Change Thames crossing decision from option C3 to option A14"}
36 | , "numberOfSignatures" : 2608, "status" : "closed"}
37 | , {"_about" : "http://data.parliament.uk/resources/727247", "created" : {"_value" : "2017-04-12T10:45:18.684Z", "_datatype" : "dateTime"}
38 | , "identifier" : {"_value" : "194951"}
39 | , "label" : {"_value" : "Begin an operation to evacuate members of LGBT+ community in Chechnya."}
40 | , "numberOfSignatures" : 521, "status" : "closed"}
41 | ], "itemsPerPage" : 10, "next" : "http://lda.data.parliament.uk/epetitions.json?_page=1", "page" : 0, "startIndex" : 1, "totalResults" : 8870, "type" : ["http://purl.org/linked-data/api/vocab#ListEndpoint", "http://purl.org/linked-data/api/vocab#Page"]}
42 | }
43 |
--------------------------------------------------------------------------------
/__tests__/petition/output.d.ts:
--------------------------------------------------------------------------------
1 | interface IRootObject {
2 | format: string;
3 | version: string;
4 | result: IResult;
5 | }
6 |
7 | interface IResult {
8 | _about: string;
9 | definition: string;
10 | extendedMetadataVersion: string;
11 | first: string;
12 | hasPart: string;
13 | isPartOf: string;
14 | items: IItemsItem[];
15 | itemsPerPage: number;
16 | next: string;
17 | page: number;
18 | startIndex: number;
19 | totalResults: number;
20 | type: string[];
21 | }
22 |
23 | interface IItemsItem {
24 | _about: string;
25 | created: ICreated;
26 | identifier: IIdentifier;
27 | label: IIdentifier;
28 | numberOfSignatures: number;
29 | status: string;
30 | }
31 |
32 | interface ICreated {
33 | _value: string;
34 | _datatype: string;
35 | }
36 |
37 | interface IIdentifier {
38 | _value: string;
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/__tests__/prefix-empty.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `
5 | {
6 | "231thing": [{"name": "darius"}, {"age": 42}]
7 | }
8 | `;
9 |
10 | const expected = `
11 | interface RootObject {
12 | '231thing': _231ThingItem[];
13 | }
14 | interface _231ThingItem {
15 | name?: string;
16 | age?: number;
17 | }
18 | `;
19 |
20 | it('works with prefix=blank string and gives valid interface names', function() {
21 | expect(json2ts(json, {prefix: ""})).toEqual(expected.slice(1));
22 | });
23 |
--------------------------------------------------------------------------------
/__tests__/prefix.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `
5 | {
6 | "id": 15,
7 | "sku": "24-UG06",
8 | "name": "Affirm Water Bottle",
9 | "attribute_set_id": 11,
10 | "price": 7,
11 | "status": 1,
12 | "visibility": 4,
13 | "type_id": "simple",
14 | "created_at": "2016-07-11 10:19:09",
15 | "updated_at": "2016-07-11 10:19:09",
16 | "extension_attributes": [],
17 | "product_links": [],
18 | "options": [],
19 | "media_gallery_entries": [
20 | {
21 | "id": 19,
22 | "media_type": "image",
23 | "label": "Image",
24 | "position": 1,
25 | "disabled": false,
26 | "types": [
27 | "image",
28 | "small_image",
29 | "thumbnail"
30 | ],
31 | "file": "\/u\/g\/ug06-lb-0.jpg"
32 | },
33 | {
34 | "id": 20,
35 | "media_type": "image",
36 | "label": "Image",
37 | "position": 2,
38 | "disabled": false,
39 | "types": [
40 | "image",
41 | "small_image",
42 | "thumbnail"
43 | ],
44 | "file": "\/u\/g\/ug07-bk-0.jpg"
45 | },
46 | {
47 | "id": 21,
48 | "media_type": "image",
49 | "label": "Image",
50 | "position": 3,
51 | "disabled": false,
52 | "types": [
53 | "image",
54 | "small_image",
55 | "thumbnail"
56 | ],
57 | "file": "\/u\/g\/ug07-bk-0_alt1.jpg"
58 | },
59 | {
60 | "id": 22,
61 | "media_type": "image",
62 | "label": "Image",
63 | "position": 4,
64 | "disabled": false,
65 | "types": [
66 | "image",
67 | "small_image",
68 | "thumbnail"
69 | ],
70 | "file": "\/u\/g\/ug07-bk-0_alt1.jpg"
71 | }
72 | ],
73 | "tier_prices": [],
74 | "custom_attributes": [
75 | {
76 | "attribute_code": "description",
77 | "value": "You'll stay hydrated with ease with the Affirm Water Bottle by your side or in hand. Measurements on the outside help you keep track of how much you're drinking, while the screw-top lid prevents spills. A metal carabiner clip allows you to attach it to the outside of a backpack or bag for easy access.<\/p>\n
\nMade of plastic.<\/li>\n Grooved sides for an easy grip.<\/li>\n<\/ul>"
78 | },
79 | {
80 | "attribute_code": "image",
81 | "value": "\/u\/g\/ug06-lb-0.jpg"
82 | },
83 | {
84 | "attribute_code": "small_image",
85 | "value": "\/u\/g\/ug06-lb-0.jpg"
86 | },
87 | {
88 | "attribute_code": "thumbnail",
89 | "value": "\/u\/g\/ug06-lb-0.jpg"
90 | },
91 | {
92 | "attribute_code": "category_ids",
93 | "value": [
94 | "3",
95 | "5"
96 | ]
97 | },
98 | {
99 | "attribute_code": "options_container",
100 | "value": "container2"
101 | },
102 | {
103 | "attribute_code": "required_options",
104 | "value": "0"
105 | },
106 | {
107 | "attribute_code": "has_options",
108 | "value": "0"
109 | },
110 | {
111 | "attribute_code": "url_key",
112 | "value": "affirm-water-bottle"
113 | },
114 | {
115 | "attribute_code": "tax_class_id",
116 | "value": "2"
117 | },
118 | {
119 | "attribute_code": "activity",
120 | "value": "8,9,17,11"
121 | },
122 | {
123 | "attribute_code": "material",
124 | "value": "44"
125 | },
126 | {
127 | "attribute_code": "gender",
128 | "value": "80,81,82,83,84"
129 | },
130 | {
131 | "attribute_code": "category_gear",
132 | "value": "87,89"
133 | }
134 | ]
135 | }
136 | `;
137 |
138 | const expected = `
139 | interface RootObject {
140 | id: number;
141 | sku: string;
142 | name: string;
143 | attribute_set_id: number;
144 | price: number;
145 | status: number;
146 | visibility: number;
147 | type_id: string;
148 | created_at: string;
149 | updated_at: string;
150 | extension_attributes: any[];
151 | product_links: any[];
152 | options: any[];
153 | media_gallery_entries: MediaGalleryEntriesItem[];
154 | tier_prices: any[];
155 | custom_attributes: CustomAttributesItem[];
156 | }
157 | interface MediaGalleryEntriesItem {
158 | id: number;
159 | media_type: string;
160 | label: string;
161 | position: number;
162 | disabled: boolean;
163 | types: string[];
164 | file: string;
165 | }
166 | interface CustomAttributesItem {
167 | attribute_code: string;
168 | value: string | string[];
169 | }
170 | `;
171 |
172 | it('works with prefix=blank string', function() {
173 | expect(json2ts(json, {prefix: ""})).toEqual(expected.slice(1));
174 | });
175 |
--------------------------------------------------------------------------------
/__tests__/rootName.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `
5 | {
6 | "id": 15
7 | }
8 | `;
9 |
10 | const expected = `
11 | interface MyRoot {
12 | id: number;
13 | }
14 | `;
15 |
16 | it('works with prefix=blank string', function() {
17 | expect(json2ts(json, {rootName: "MyRoot", prefix: ""})).toEqual(expected.slice(1));
18 | });
19 |
--------------------------------------------------------------------------------
/__tests__/top-level-array-mixed.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | // language=JSON
5 | const json = `
6 | [
7 | {
8 | "name": "Shane"
9 | },
10 | 2,
11 | "oops"
12 | ]
13 | `;
14 |
15 | const expected = `
16 | type IRootObject = any[];
17 | interface IRootObjectItem {
18 | name: string;
19 | }
20 | `;
21 |
22 | it('works with top level array + mixed values', function() {
23 | expect(json2ts(json)).toEqual(expected.slice(1));
24 | });
25 |
--------------------------------------------------------------------------------
/__tests__/top-level-array-objects.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | // language=JSON
5 | const json = `
6 | [
7 | {
8 | "name": "Shane"
9 | },
10 | {
11 | "name": "Kittie",
12 | "age": 28
13 | }
14 | ]
15 | `;
16 |
17 | const expected = `
18 | type IRootObject = IRootObjectItem[];
19 | interface IRootObjectItem {
20 | name: string;
21 | age?: number;
22 | }
23 | `;
24 |
25 | it('works with top level array + objects', function() {
26 | expect(json2ts(json)).toEqual(expected.slice(1));
27 | });
28 |
--------------------------------------------------------------------------------
/__tests__/top-level-array.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `
5 | [1, 2]
6 | `;
7 |
8 | const expected = `
9 | type IRootObject = number[];
10 | `;
11 |
12 | it('works with top level simple array', function() {
13 | expect(json2ts(json)).toEqual(expected.slice(1));
14 | });
15 |
--------------------------------------------------------------------------------
/__tests__/top-level-multi-arrays.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const json2tsMulti = require('../').json2tsMulti;
3 | const assert = require('assert');
4 |
5 | // language=JSON
6 | const json = `
7 | [{"name": "kittie"}]
8 | `;
9 |
10 | // language=JSON
11 | const json2 = `
12 | [{"name": "shane", "age": 10}]
13 | `;
14 |
15 | const expected = `
16 | type IRootObject = IRootObjectItem[];
17 | interface IRootObjectItem {
18 | name: string;
19 | age?: number;
20 | }
21 | `;
22 |
23 | it('works with top level array when merging', function() {
24 | expect(json2tsMulti([json, json2])).toEqual(expected.slice(1));
25 | });
26 |
--------------------------------------------------------------------------------
/__tests__/top-level-string.js:
--------------------------------------------------------------------------------
1 | const json2ts = require('../').json2ts;
2 | const assert = require('assert');
3 |
4 | const json = `"6345634563"`;
5 |
6 | const expected = `
7 | type IRootObject = string;
8 | `;
9 |
10 | it('works with top level string', function() {
11 | expect(json2ts(json)).toEqual(expected.slice(1, -1));
12 | });
13 |
14 | it('works with top level number', function() {
15 | expect(json2ts(1)).toEqual(`type IRootObject = number;`);
16 | });
17 |
18 | it('works with top level null', function() {
19 | expect(json2ts('null')).toEqual(`type IRootObject = null;`);
20 | });
21 |
--------------------------------------------------------------------------------
/crossbow.yml:
--------------------------------------------------------------------------------
1 | tasks:
2 | build:
3 | - browserify
4 | - uglify
5 | - copy
6 |
7 | browserify:
8 | description: A special build for browser-globals
9 | tasks:
10 | - >
11 | @npm browserify dist/index.js -d |
12 | exorcist dist/json-ts.js.map
13 | > dist/json-ts.js
14 |
15 | uglify:
16 | - ifChanged: dist/json-ts.js
17 | tasks: >
18 | @npm uglifyjs
19 | --compress --mangle --
20 | dist/json-ts.js > dist/json-ts.min.js
21 | && cp dist/json-ts.min.js docs/dist/json-ts.min.js
22 | - ifChanged: docs/lib/codemirror.js
23 | tasks: >
24 | @npm uglifyjs
25 | --compress --mangle --
26 | docs/lib/codemirror.js > docs/dist/codemirror.js
27 | - ifChanged: docs/javascript/javascript.js
28 | tasks: >
29 | @npm uglifyjs
30 | --compress --mangle --
31 | docs/javascript/javascript.js > docs/dist/javascript.js
32 |
33 | copy: >
34 | @sh cp dist/json-ts.min.js docs/json-ts.min.js
--------------------------------------------------------------------------------
/docs/css/styles.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 | body, html {
5 | margin: 0;
6 | padding: 0;
7 | font: 15px/1.8 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif
8 | }
9 | .status {
10 | position: fixed;
11 | bottom: 0;
12 | z-index: 2;
13 | padding: 10px;
14 | width: 100%;
15 | }
16 | .status.error {
17 | color: red;
18 | background: rgba(254, 205, 205, .8);
19 | }
20 | .status.success {
21 | color: blue;
22 | background: rgba(210, 225, 244, .8);
23 | }
24 | .debug {
25 | margin: 0;
26 | }
27 | .hint {
28 | padding: .5em 1em;
29 | background: #294E80;
30 | color: white;
31 | }
32 | .hint p {
33 | margin: 0;
34 | }
35 | .hint.hint--left {
36 | background: #3667a9;
37 | }
38 | .CodeMirror {
39 | /* Set height, width, borders, and global font properties here */
40 | /*height: 300px;*/
41 | font-size: 12px;
42 | border-bottom: 2px solid #e9e9e9;
43 | height: calc(50vh - 37px - 47px);
44 | }
45 | .header {
46 | padding: 0;
47 | background: #294E80;
48 | color: white;
49 | text-align: center;
50 | }
51 | .logo {
52 | padding: .5em 1em;
53 | }
54 | .header a {
55 | color: white;
56 | }
57 | .header h1 {
58 | margin: 0;
59 | font-size: 12px;
60 | font-weight: normal;
61 | }
62 | .controls {
63 | font-size: 12px;
64 | background: #3667a9;
65 | padding: 1em 0;
66 | grid-template-columns: 50% 50%;
67 | display: none;
68 | }
69 | .controls p {
70 | margin: 0;
71 | }
72 | .field {
73 | line-height: 1;
74 | padding: 0 1em;
75 | }
76 | .field.check {
77 | display: flex;
78 | align-items: center;
79 | justify-content: center;
80 | }
81 | .field label {
82 | text-align: left;
83 | display: block;
84 | margin-bottom: 5px;
85 | }
86 | .field label span {
87 | display: block;
88 | padding: .2em 0;
89 | font-size: 14px;
90 | }
91 | .field input[type="text"] {
92 | width: 100%;
93 | height: 35px;
94 | padding-left: 10px;
95 | }
96 | .examples {
97 | background: orange;
98 | padding: 10px;
99 | position: fixed;
100 | bottom: 45px;
101 | z-index: 100;
102 | width: 100%;
103 | }
104 | @media (min-width: 600px) {
105 | .CodeMirror {
106 | height: calc(100vh - 160px);
107 | border-bottom: 0;
108 | }
109 | .header {
110 | display: flex;
111 | }
112 | .controls {
113 | display: flex;
114 | flex: 1;
115 | align-items: center;
116 | padding-left: 2em;
117 | }
118 | .field {
119 | display: flex;
120 | align-items: center;
121 | }
122 | .field label {
123 | margin-bottom: 0;
124 | margin-right: 10px;
125 | }
126 | .logo {
127 | display: flex;
128 | align-items: center;
129 | }
130 | .wrapper {
131 | display: flex;
132 | background-color: #fff;
133 | color: #444;
134 | }
135 | .wrapper > * {
136 | width: 50%;
137 | }
138 | .examples {
139 | /*position: fixed;*/
140 | /*bottom: 0;*/
141 | /*left: 0;*/
142 | /*width: 50%;*/
143 | /*display: block;*/
144 | }
145 | }
--------------------------------------------------------------------------------
/docs/dist/index.js:
--------------------------------------------------------------------------------
1 | /******/ (function(modules) { // webpackBootstrap
2 | /******/ // The module cache
3 | /******/ var installedModules = {};
4 | /******/
5 | /******/ // The require function
6 | /******/ function __webpack_require__(moduleId) {
7 | /******/
8 | /******/ // Check if module is in cache
9 | /******/ if(installedModules[moduleId]) {
10 | /******/ return installedModules[moduleId].exports;
11 | /******/ }
12 | /******/ // Create a new module (and put it into the cache)
13 | /******/ var module = installedModules[moduleId] = {
14 | /******/ i: moduleId,
15 | /******/ l: false,
16 | /******/ exports: {}
17 | /******/ };
18 | /******/
19 | /******/ // Execute the module function
20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21 | /******/
22 | /******/ // Flag the module as loaded
23 | /******/ module.l = true;
24 | /******/
25 | /******/ // Return the exports of the module
26 | /******/ return module.exports;
27 | /******/ }
28 | /******/
29 | /******/
30 | /******/ // expose the modules object (__webpack_modules__)
31 | /******/ __webpack_require__.m = modules;
32 | /******/
33 | /******/ // expose the module cache
34 | /******/ __webpack_require__.c = installedModules;
35 | /******/
36 | /******/ // identity function for calling harmony imports with the correct context
37 | /******/ __webpack_require__.i = function(value) { return value; };
38 | /******/
39 | /******/ // define getter function for harmony exports
40 | /******/ __webpack_require__.d = function(exports, name, getter) {
41 | /******/ if(!__webpack_require__.o(exports, name)) {
42 | /******/ Object.defineProperty(exports, name, {
43 | /******/ configurable: false,
44 | /******/ enumerable: true,
45 | /******/ get: getter
46 | /******/ });
47 | /******/ }
48 | /******/ };
49 | /******/
50 | /******/ // getDefaultExport function for compatibility with non-harmony modules
51 | /******/ __webpack_require__.n = function(module) {
52 | /******/ var getter = module && module.__esModule ?
53 | /******/ function getDefault() { return module['default']; } :
54 | /******/ function getModuleExports() { return module; };
55 | /******/ __webpack_require__.d(getter, 'a', getter);
56 | /******/ return getter;
57 | /******/ };
58 | /******/
59 | /******/ // Object.prototype.hasOwnProperty.call
60 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
61 | /******/
62 | /******/ // __webpack_public_path__
63 | /******/ __webpack_require__.p = "";
64 | /******/
65 | /******/ // Load entry module and return exports
66 | /******/ return __webpack_require__(__webpack_require__.s = 1);
67 | /******/ })
68 | /************************************************************************/
69 | /******/ ([
70 | /* 0 */
71 | /***/ (function(module, __webpack_exports__, __webpack_require__) {
72 |
73 | "use strict";
74 | Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
75 | var index = typeof fetch=='function' ? fetch : function(url, options) {
76 | options = options || {};
77 | return new Promise( function (resolve, reject) {
78 | var request = new XMLHttpRequest();
79 |
80 | request.open(options.method || 'get', url);
81 |
82 | for (var i in options.headers) {
83 | request.setRequestHeader(i, options.headers[i]);
84 | }
85 |
86 | request.withCredentials = options.credentials=='include';
87 |
88 | request.onload = function () {
89 | resolve(response());
90 | };
91 |
92 | request.onerror = reject;
93 |
94 | request.send(options.body);
95 |
96 | function response() {
97 | var keys = [],
98 | all = [],
99 | headers = {},
100 | header;
101 |
102 | request.getAllResponseHeaders().replace(/^(.*?):\s*([\s\S]*?)$/gm, function (m, key, value) {
103 | keys.push(key = key.toLowerCase());
104 | all.push([key, value]);
105 | header = headers[key];
106 | headers[key] = header ? (header + "," + value) : value;
107 | });
108 |
109 | return {
110 | ok: (request.status/200|0) == 1, // 200-399
111 | status: request.status,
112 | statusText: request.statusText,
113 | url: request.responseURL,
114 | clone: response,
115 | text: function () { return Promise.resolve(request.responseText); },
116 | json: function () { return Promise.resolve(request.responseText).then(JSON.parse); },
117 | xml: function () { return Promise.resolve(request.responseXML); },
118 | blob: function () { return Promise.resolve(new Blob([request.response])); },
119 | headers: {
120 | keys: function () { return keys; },
121 | entries: function () { return all; },
122 | get: function (n) { return headers[n.toLowerCase()]; },
123 | has: function (n) { return n.toLowerCase() in headers; }
124 | }
125 | };
126 | }
127 | });
128 | };
129 |
130 | /* harmony default export */ __webpack_exports__["default"] = (index);
131 | //# sourceMappingURL=unfetch.es.js.map
132 |
133 |
134 | /***/ }),
135 | /* 1 */
136 | /***/ (function(module, exports, __webpack_require__) {
137 |
138 | const fetch = __webpack_require__(0).default;
139 |
140 | const iniialJson = getHash() || `{
141 | "author": "shakyShane",
142 | "profile": {
143 | "links": [
144 | {
145 | "name": "twitter",
146 | "url": "https://twitter.com/shaneOsbourne"
147 | },
148 | {
149 | "name": "Medium",
150 | "url": "https://medium.com/@shakyShane"
151 | }
152 | ]
153 | },
154 | "location": null,
155 | "email": null,
156 | "bio": null,
157 | "public_repos": 145,
158 | "public_gists": 69,
159 | "followers": 298,
160 | "following": 1,
161 | "created_at": "2012-04-14T17:34:37Z",
162 | "updated_at": "2017-04-26T12:43:35Z"
163 | }`;
164 | const $ = document.querySelector.bind(document);
165 | const statusElem = $('.status');
166 | const statusText = $('.debug');
167 | const flow = $('#flow');
168 | const rootName = $('#root-name');
169 | const prefix = $('#prefix');
170 | const namespace = $('#namespace');
171 | const examplesElem = $('#examples');
172 | const examples = [
173 | ['json/nested.json', 'Nested Objects & Arrays'],
174 | ['json/optional.json', 'Optional Fields'],
175 | ['json/recursive.json', 'Recursive data structures'],
176 | ['json/invalid-keys.json', 'Invalid property names']
177 | ];
178 |
179 | examples.forEach(([json, title]) => {
180 | const opt = document.createElement('option');
181 | opt.value = json;
182 | opt.textContent = title;
183 | examplesElem.appendChild(opt);
184 | });
185 |
186 | examplesElem.addEventListener('change', function () {
187 | if (examplesElem.value !== 'Select an example') {
188 | examplesElem.parentNode.classList.add('loading');
189 | const resp = fetch(examplesElem.value).then(x => x.text());
190 | resp.then(x => jsonInput.setValue(x));
191 | }
192 | });
193 |
194 | let defaults = {
195 | flow: false,
196 | namespace: '',
197 | prefix: 'I',
198 | rootName: 'RootObject'
199 | };
200 |
201 | flow.addEventListener('change', function () {
202 | setOutput(jsonInput.getValue(), options({flow: flow.checked}));
203 | });
204 | namespace.addEventListener('input', function () {
205 | setOutput(jsonInput.getValue(), options({namespace: namespace.value}));
206 | });
207 | rootName.addEventListener('input', function () {
208 | setOutput(jsonInput.getValue(), options({rootName: rootName.value}));
209 | });
210 | rootName.addEventListener('input', function () {
211 | setOutput(jsonInput.getValue(), options({rootName: rootName.value}));
212 | });
213 | prefix.addEventListener('input', function () {
214 | setOutput(jsonInput.getValue(), options({prefix: prefix.value}));
215 | });
216 | const ts = json2ts(iniialJson, defaults);
217 |
218 | var tsOutput = CodeMirror($('#ts'), {
219 | value: ts,
220 | mode: {name: "javascript", typescript: true}
221 | });
222 | var jsonInput = CodeMirror($('#app'), {
223 | value: iniialJson,
224 | mode: {name: "javascript", json: true}
225 | });
226 |
227 | function setOutput(json, options) {
228 | if (json && json.length > 3) {
229 | JSON.parse(json);
230 | const ts = json2ts(json, options);
231 | tsOutput.setValue(ts);
232 | status('success', 'JSON: All good!');
233 | }
234 | }
235 |
236 | jsonInput.on('change', function (obj) {
237 | try {
238 | setOutput(jsonInput.getValue());
239 | } catch (e) {
240 | status('error', `Error parsing JSON: ${e.message}`);
241 | console.log('Some JSON error?', e.message);
242 | }
243 | });
244 |
245 | function getHash() {
246 | if (window.location.hash) {
247 | if (window.location.hash.slice(0, 5) === '#src=') {
248 | const maybe = window.location.hash.slice(5);
249 | if (maybe) {
250 | return decodeURIComponent(maybe);
251 | }
252 | }
253 | }
254 | return "";
255 | }
256 |
257 | const share = $('a[href="#share"]');
258 | share.addEventListener('click', function (e) {
259 | e.preventDefault();
260 | window.location.hash = `src=${encodeURIComponent(jsonInput.getValue().trim())}`;
261 | });
262 |
263 | function status(type, text) {
264 | statusElem.classList.remove('success');
265 | statusElem.classList.remove('error');
266 | statusElem.classList.add(type);
267 | statusText.textContent = text;
268 | }
269 | function options(incoming) {
270 | defaults = Object.assign({},
271 | defaults,
272 | incoming
273 | );
274 | return defaults;
275 | }
276 |
277 |
278 | /***/ })
279 | /******/ ]);
--------------------------------------------------------------------------------
/docs/dist/javascript.js:
--------------------------------------------------------------------------------
1 | !function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],e):e(CodeMirror)}(function(e){"use strict";function t(e,t,r){return/^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(t.lastType)||"quasi"==t.lastType&&/\{\s*$/.test(e.string.slice(0,e.pos-(r||0)))}e.defineMode("javascript",function(r,n){function a(e){for(var t,r=!1,n=!1;null!=(t=e.next());){if(!r){if("/"==t&&!n)return;"["==t?n=!0:n&&"]"==t&&(n=!1)}r=!r&&"\\"==t}}function i(e,t,r){return Ve=e,Ee=r,t}function o(e,r){var n=e.next();if('"'==n||"'"==n)return r.tokenize=c(n),r.tokenize(e,r);if("."==n&&e.match(/^\d+(?:[eE][+\-]?\d+)?/))return i("number","number");if("."==n&&e.match(".."))return i("spread","meta");if(/[\[\]{}\(\),;\:\.]/.test(n))return i(n);if("="==n&&e.eat(">"))return i("=>","operator");if("0"==n&&e.eat(/x/i))return e.eatWhile(/[\da-f]/i),i("number","number");if("0"==n&&e.eat(/o/i))return e.eatWhile(/[0-7]/i),i("number","number");if("0"==n&&e.eat(/b/i))return e.eatWhile(/[01]/i),i("number","number");if(/\d/.test(n))return e.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/),i("number","number");if("/"==n)return e.eat("*")?(r.tokenize=u,u(e,r)):e.eat("/")?(e.skipToEnd(),i("comment","comment")):t(e,r,1)?(a(e),e.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/),i("regexp","string-2")):(e.eatWhile(Oe),i("operator","operator",e.current()));if("`"==n)return r.tokenize=l,l(e,r);if("#"==n)return e.skipToEnd(),i("error","error");if(Oe.test(n))return">"==n&&r.lexical&&">"==r.lexical.type||e.eatWhile(Oe),i("operator","operator",e.current());if(qe.test(n)){e.eatWhile(qe);var o=e.current(),s=Ce.propertyIsEnumerable(o)&&Ce[o];return s&&"."!=r.lastType?i(s.type,s.style,o):i("variable","variable",o)}}function c(e){return function(t,r){var n,a=!1;if(Ae&&"@"==t.peek()&&t.match(We))return r.tokenize=o,i("jsonld-keyword","meta");for(;null!=(n=t.next())&&(n!=e||a);)a=!a&&"\\"==n;return a||(r.tokenize=o),i("string","string")}}function u(e,t){for(var r,n=!1;r=e.next();){if("/"==r&&n){t.tokenize=o;break}n="*"==r}return i("comment","comment")}function l(e,t){for(var r,n=!1;null!=(r=e.next());){if(!n&&("`"==r||"$"==r&&e.eat("{"))){t.tokenize=o;break}n=!n&&"\\"==r}return i("quasi","string-2",e.current())}function s(e,t){t.fatArrowAt&&(t.fatArrowAt=null);var r=e.string.indexOf("=>",e.start);if(!(r<0)){if($e){var n=/:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(e.string.slice(e.start,r));n&&(r=n.index)}for(var a=0,i=!1,o=r-1;o>=0;--o){var c=e.string.charAt(o),u=Pe.indexOf(c);if(u>=0&&u<3){if(!a){++o;break}if(0==--a){"("==c&&(i=!0);break}}else if(u>=3&&u<6)++a;else if(qe.test(c))i=!0;else{if(/["'\/]/.test(c))return;if(i&&!a){++o;break}}}i&&!a&&(t.fatArrowAt=o)}}function f(e,t,r,n,a,i){this.indented=e,this.column=t,this.type=r,this.prev=a,this.info=i,null!=n&&(this.align=n)}function d(e,t){for(var r=e.localVars;r;r=r.next)if(r.name==t)return!0;for(var n=e.context;n;n=n.prev)for(var r=n.vars;r;r=r.next)if(r.name==t)return!0}function p(e,t,r,n,a){var i=e.cc;for(Ne.state=e,Ne.stream=a,Ne.marked=null,Ne.cc=i,Ne.style=t,e.lexical.hasOwnProperty("align")||(e.lexical.align=!0);;){if((i.length?i.pop():Te?j:g)(r,n)){for(;i.length&&i[i.length-1].lex;)i.pop()();return Ne.marked?Ne.marked:"variable"==r&&d(e,n)?"variable-2":t}}}function m(){for(var e=arguments.length-1;e>=0;e--)Ne.cc.push(arguments[e])}function v(){return m.apply(null,arguments),!0}function y(e){function t(t){for(var r=t;r;r=r.next)if(r.name==e)return!0;return!1}var r=Ne.state;if(Ne.marked="def",r.context){if(t(r.localVars))return;r.localVars={name:e,next:r.localVars}}else{if(t(r.globalVars))return;n.globalVars&&(r.globalVars={name:e,next:r.globalVars})}}function k(){Ne.state.context={prev:Ne.state.context,vars:Ne.state.localVars},Ne.state.localVars=Be}function b(){Ne.state.localVars=Ne.state.context.vars,Ne.state.context=Ne.state.context.prev}function x(e,t){var r=function(){var r=Ne.state,n=r.indented;if("stat"==r.lexical.type)n=r.lexical.indented;else for(var a=r.lexical;a&&")"==a.type&&a.align;a=a.prev)n=a.indented;r.lexical=new f(n,Ne.stream.column(),e,null,r.lexical,t)};return r.lex=!0,r}function h(){var e=Ne.state;e.lexical.prev&&(")"==e.lexical.type&&(e.indented=e.lexical.indented),e.lexical=e.lexical.prev)}function w(e){function t(r){return r==e?v():";"==e?m():v(t)}return t}function g(e,t){return"var"==e?v(x("vardef",t.length),Z,w(";"),h):"keyword a"==e?v(x("form"),V,g,h):"keyword b"==e?v(x("form"),g,h):"{"==e?v(x("}"),J,h):";"==e?v():"if"==e?("else"==Ne.state.lexical.info&&Ne.state.cc[Ne.state.cc.length-1]==h&&Ne.state.cc.pop()(),v(x("form"),V,g,h,ne)):"function"==e?v(le):"for"==e?v(x("form"),ae,g,h):"variable"==e?$e&&"type"==t?(Ne.marked="keyword",v(L,w("operator"),L,w(";"))):v(x("stat"),N):"switch"==e?v(x("form"),V,w("{"),x("}","switch"),J,h,h):"case"==e?v(j,w(":")):"default"==e?v(w(":")):"catch"==e?v(x("form"),k,w("("),se,w(")"),g,h,b):"class"==e?v(x("form"),de,h):"export"==e?v(x("stat"),ye,h):"import"==e?v(x("stat"),be,h):"module"==e?v(x("form"),_,w("{"),x("}"),J,h,h):"async"==e?v(g):"@"==t?v(j,g):m(x("stat"),j,w(";"),h)}function j(e){return E(e,!1)}function M(e){return E(e,!0)}function V(e){return"("!=e?m():v(x(")"),j,w(")"),h)}function E(e,t){if(Ne.state.fatArrowAt==Ne.stream.start){var r=t?O:C;if("("==e)return v(k,x(")"),F(_,")"),h,w("=>"),r,b);if("variable"==e)return m(k,_,w("=>"),r,b)}var n=t?T:A;return Se.hasOwnProperty(e)?v(n):"function"==e?v(le,n):"class"==e?v(x("form"),fe,h):"keyword c"==e||"async"==e?v(t?z:I):"("==e?v(x(")"),I,w(")"),h,n):"operator"==e||"spread"==e?v(t?M:j):"["==e?v(x("]"),je,h,n):"{"==e?G(H,"}",null,n):"quasi"==e?m($,n):"new"==e?v(W(t)):v()}function I(e){return e.match(/[;\}\)\],]/)?m():m(j)}function z(e){return e.match(/[;\}\)\],]/)?m():m(M)}function A(e,t){return","==e?v(j):T(e,t,!1)}function T(e,t,r){var n=0==r?A:T,a=0==r?j:M;return"=>"==e?v(k,r?O:C,b):"operator"==e?/\+\+|--/.test(t)?v(n):"?"==t?v(j,w(":"),a):v(a):"quasi"==e?m($,n):";"!=e?"("==e?G(M,")","call",n):"."==e?v(B,n):"["==e?v(x("]"),I,w("]"),h,n):void 0:void 0}function $(e,t){return"quasi"!=e?m():"${"!=t.slice(t.length-2)?v($):v(j,q)}function q(e){if("}"==e)return Ne.marked="string-2",Ne.state.tokenize=l,v($)}function C(e){return s(Ne.stream,Ne.state),m("{"==e?g:j)}function O(e){return s(Ne.stream,Ne.state),m("{"==e?g:M)}function W(e){return function(t){return"."==t?v(e?S:P):m(e?M:j)}}function P(e,t){if("target"==t)return Ne.marked="keyword",v(A)}function S(e,t){if("target"==t)return Ne.marked="keyword",v(T)}function N(e){return":"==e?v(h,g):m(A,w(";"),h)}function B(e){if("variable"==e)return Ne.marked="property",v()}function H(e,t){return"async"==e?(Ne.marked="property",v(H)):"variable"==e||"keyword"==Ne.style?(Ne.marked="property",v("get"==t||"set"==t?U:D)):"number"==e||"string"==e?(Ne.marked=Ae?"property":Ne.style+" property",v(D)):"jsonld-keyword"==e?v(D):"modifier"==e?v(H):"["==e?v(j,w("]"),D):"spread"==e?v(j):":"==e?m(D):void 0}function U(e){return"variable"!=e?m(D):(Ne.marked="property",v(le))}function D(e){return":"==e?v(M):"("==e?m(le):void 0}function F(e,t,r){function n(a,i){if(r?r.indexOf(a)>-1:","==a){var o=Ne.state.lexical;return"call"==o.info&&(o.pos=(o.pos||0)+1),v(function(r,n){return r==t||n==t?m():m(e)},n)}return a==t||i==t?v():v(w(t))}return function(r,a){return r==t||a==t?v():m(e,n)}}function G(e,t,r){for(var n=3;n"==e)return v(L)}function R(e,t){return"variable"==e||"keyword"==Ne.style?(Ne.marked="property",v(R)):"?"==t?v(R):":"==e?v(L):"["==e?v(j,K,w("]"),R):void 0}function X(e){return"variable"==e?v(X):":"==e?v(L):void 0}function Y(e,t){return"<"==t?v(x(">"),F(L,">"),h,Y):"|"==t||"."==e?v(L):"["==e?v(w("]"),Y):"extends"==t?v(L):void 0}function Z(){return m(_,K,te,re)}function _(e,t){return"modifier"==e?v(_):"variable"==e?(y(t),v()):"spread"==e?v(_):"["==e?G(_,"]"):"{"==e?G(ee,"}"):void 0}function ee(e,t){return"variable"!=e||Ne.stream.match(/^\s*:/,!1)?("variable"==e&&(Ne.marked="property"),"spread"==e?v(_):"}"==e?m():v(w(":"),_,te)):(y(t),v(te))}function te(e,t){if("="==t)return v(M)}function re(e){if(","==e)return v(Z)}function ne(e,t){if("keyword b"==e&&"else"==t)return v(x("form","else"),g,h)}function ae(e){if("("==e)return v(x(")"),ie,w(")"),h)}function ie(e){return"var"==e?v(Z,w(";"),ce):";"==e?v(ce):"variable"==e?v(oe):m(j,w(";"),ce)}function oe(e,t){return"in"==t||"of"==t?(Ne.marked="keyword",v(j)):v(A,ce)}function ce(e,t){return";"==e?v(ue):"in"==t||"of"==t?(Ne.marked="keyword",v(j)):m(j,w(";"),ue)}function ue(e){")"!=e&&v(j)}function le(e,t){return"*"==t?(Ne.marked="keyword",v(le)):"variable"==e?(y(t),v(le)):"("==e?v(k,x(")"),F(se,")"),h,K,g,b):$e&&"<"==t?v(x(">"),F(L,">"),h,le):void 0}function se(e){return"spread"==e?v(se):m(_,K,te)}function fe(e,t){return"variable"==e?de(e,t):pe(e,t)}function de(e,t){if("variable"==e)return y(t),v(pe)}function pe(e,t){return"<"==t?v(x(">"),F(L,">"),h,pe):"extends"==t||"implements"==t||$e&&","==e?v($e?L:j,pe):"{"==e?v(x("}"),me,h):void 0}function me(e,t){return"variable"==e||"keyword"==Ne.style?("async"==t||"static"==t||"get"==t||"set"==t||$e&&("public"==t||"private"==t||"protected"==t||"readonly"==t||"abstract"==t))&&Ne.stream.match(/^\s+[\w$\xa1-\uffff]/,!1)?(Ne.marked="keyword",v(me)):(Ne.marked="property",v($e?ve:le,me)):"["==e?v(j,w("]"),$e?ve:le,me):"*"==t?(Ne.marked="keyword",v(me)):";"==e?v(me):"}"==e?v():"@"==t?v(j,me):void 0}function ve(e,t){return"?"==t?v(ve):":"==e?v(L,te):"="==t?v(M):m(le)}function ye(e,t){return"*"==t?(Ne.marked="keyword",v(ge,w(";"))):"default"==t?(Ne.marked="keyword",v(j,w(";"))):"{"==e?v(F(ke,"}"),ge,w(";")):m(g)}function ke(e,t){return"as"==t?(Ne.marked="keyword",v(w("variable"))):"variable"==e?m(M,ke):void 0}function be(e){return"string"==e?v():m(xe,he,ge)}function xe(e,t){return"{"==e?G(xe,"}"):("variable"==e&&y(t),"*"==t&&(Ne.marked="keyword"),v(we))}function he(e){if(","==e)return v(xe,he)}function we(e,t){if("as"==t)return Ne.marked="keyword",v(xe)}function ge(e,t){if("from"==t)return Ne.marked="keyword",v(j)}function je(e){return"]"==e?v():m(F(M,"]"))}function Me(e,t){return"operator"==e.lastType||","==e.lastType||Oe.test(t.charAt(0))||/[,.]/.test(t.charAt(0))}var Ve,Ee,Ie=r.indentUnit,ze=n.statementIndent,Ae=n.jsonld,Te=n.json||Ae,$e=n.typescript,qe=n.wordCharacters||/[\w$\xa1-\uffff]/,Ce=function(){function e(e){return{type:e,style:"keyword"}}var t=e("keyword a"),r=e("keyword b"),n=e("keyword c"),a=e("operator"),i={type:"atom",style:"atom"},o={if:e("if"),while:t,with:t,else:r,do:r,try:r,finally:r,return:n,break:n,continue:n,new:e("new"),delete:n,throw:n,debugger:n,var:e("var"),const:e("var"),let:e("var"),function:e("function"),catch:e("catch"),for:e("for"),switch:e("switch"),case:e("case"),default:e("default"),in:a,typeof:a,instanceof:a,true:i,false:i,null:i,undefined:i,NaN:i,Infinity:i,this:e("this"),class:e("class"),super:e("atom"),yield:n,export:e("export"),import:e("import"),extends:n,await:n,async:e("async")};if($e){var c={type:"variable",style:"variable-3"},u={interface:e("class"),implements:n,namespace:n,module:e("module"),enum:e("module"),public:e("modifier"),private:e("modifier"),protected:e("modifier"),abstract:e("modifier"),as:a,string:c,number:c,boolean:c,any:c};for(var l in u)o[l]=u[l]}return o}(),Oe=/[+\-*&%=<>!?|~^@]/,We=/^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/,Pe="([{}])",Se={atom:!0,number:!0,variable:!0,string:!0,regexp:!0,this:!0,"jsonld-keyword":!0},Ne={state:null,column:null,marked:null,cc:null},Be={name:"this",next:{name:"arguments"}};return h.lex=!0,{startState:function(e){var t={tokenize:o,lastType:"sof",cc:[],lexical:new f((e||0)-Ie,0,"block",!1),localVars:n.localVars,context:n.localVars&&{vars:n.localVars},indented:e||0};return n.globalVars&&"object"==typeof n.globalVars&&(t.globalVars=n.globalVars),t},token:function(e,t){if(e.sol()&&(t.lexical.hasOwnProperty("align")||(t.lexical.align=!1),t.indented=e.indentation(),s(e,t)),t.tokenize!=u&&e.eatSpace())return null;var r=t.tokenize(e,t);return"comment"==Ve?r:(t.lastType="operator"!=Ve||"++"!=Ee&&"--"!=Ee?Ve:"incdec",p(t,r,Ve,Ee,e))},indent:function(t,r){if(t.tokenize==u)return e.Pass;if(t.tokenize!=o)return 0;var a,i=r&&r.charAt(0),c=t.lexical;if(!/^\s*else\b/.test(r))for(var l=t.cc.length-1;l>=0;--l){var s=t.cc[l];if(s==h)c=c.prev;else if(s!=ne)break}for(;("stat"==c.type||"form"==c.type)&&("}"==i||(a=t.cc[t.cc.length-1])&&(a==A||a==T)&&!/^[,\.=+\-*:?[\(]/.test(r));)c=c.prev;ze&&")"==c.type&&"stat"==c.prev.type&&(c=c.prev);var f=c.type,d=i==f;return"vardef"==f?c.indented+("operator"==t.lastType||","==t.lastType?c.info+1:0):"form"==f&&"{"==i?c.indented:"form"==f?c.indented+Ie:"stat"==f?c.indented+(Me(t,r)?ze||Ie:0):"switch"!=c.info||d||0==n.doubleIndentSwitch?c.align?c.column+(d?0:1):c.indented+(d?0:Ie):c.indented+(/^(?:case|default)\b/.test(r)?Ie:2*Ie)},electricInput:/^\s*(?:case .*?:|default:|\{|\})$/,blockCommentStart:Te?null:"/*",blockCommentEnd:Te?null:"*/",lineComment:Te?null:"//",fold:"brace",closeBrackets:"()[]{}''\"\"``",helperType:Te?"json":"javascript",jsonldMode:Ae,jsonMode:Te,expressionAllowed:t,skipExpression:function(e){var t=e.cc[e.cc.length-1];t!=j&&t!=M||e.cc.pop()}}}),e.registerHelper("wordChars","javascript",/[\w$]/),e.defineMIME("text/javascript","javascript"),e.defineMIME("text/ecmascript","javascript"),e.defineMIME("application/javascript","javascript"),e.defineMIME("application/x-javascript","javascript"),e.defineMIME("application/ecmascript","javascript"),e.defineMIME("application/json",{name:"javascript",json:!0}),e.defineMIME("application/x-json",{name:"javascript",json:!0}),e.defineMIME("application/ld+json",{name:"javascript",jsonld:!0}),e.defineMIME("text/typescript",{name:"javascript",typescript:!0}),e.defineMIME("application/typescript",{name:"javascript",typescript:!0})});
2 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | Convert JSON to Typescript or Flow definitions
9 |
10 |
11 |
12 |
13 |
47 |
57 |
58 | Examples
59 |
60 | Select an example
61 |
62 |
63 | `
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/docs/javascript/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | CodeMirror: JavaScript mode
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
27 |
28 |
29 | JavaScript mode
30 |
31 |
32 |
81 |
82 |
90 |
91 |
92 | JavaScript mode supports several configuration options:
93 |
94 | json
which will set the mode to expect JSON
95 | data rather than a JavaScript program.
96 | jsonld
which will set the mode to expect
97 | JSON-LD linked data rather
98 | than a JavaScript program (demo ).
99 | typescript
which will activate additional
100 | syntax highlighting and some other things for TypeScript code
101 | (demo ).
102 | statementIndent
which (given a number) will
103 | determine the amount of indentation to use for statements
104 | continued on a new line.
105 | wordCharacters
, a regexp that indicates which
106 | characters should be considered part of an identifier.
107 | Defaults to /[\w$]/
, which does not handle
108 | non-ASCII identifiers. Can be set to something more elaborate
109 | to improve Unicode support.
110 |
111 |
112 |
113 | MIME types defined: text/javascript
, application/json
, application/ld+json
, text/typescript
, application/typescript
.
114 |
115 |
--------------------------------------------------------------------------------
/docs/javascript/javascript.js:
--------------------------------------------------------------------------------
1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 | // Distributed under an MIT license: http://codemirror.net/LICENSE
3 |
4 | (function(mod) {
5 | if (typeof exports == "object" && typeof module == "object") // CommonJS
6 | mod(require("../../lib/codemirror"));
7 | else if (typeof define == "function" && define.amd) // AMD
8 | define(["../../lib/codemirror"], mod);
9 | else // Plain browser env
10 | mod(CodeMirror);
11 | })(function(CodeMirror) {
12 | "use strict";
13 |
14 | function expressionAllowed(stream, state, backUp) {
15 | return /^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
16 | (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
17 | }
18 |
19 | CodeMirror.defineMode("javascript", function(config, parserConfig) {
20 | var indentUnit = config.indentUnit;
21 | var statementIndent = parserConfig.statementIndent;
22 | var jsonldMode = parserConfig.jsonld;
23 | var jsonMode = parserConfig.json || jsonldMode;
24 | var isTS = parserConfig.typescript;
25 | var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
26 |
27 | // Tokenizer
28 |
29 | var keywords = function(){
30 | function kw(type) {return {type: type, style: "keyword"};}
31 | var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
32 | var operator = kw("operator"), atom = {type: "atom", style: "atom"};
33 |
34 | var jsKeywords = {
35 | "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
36 | "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
37 | "var": kw("var"), "const": kw("var"), "let": kw("var"),
38 | "function": kw("function"), "catch": kw("catch"),
39 | "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
40 | "in": operator, "typeof": operator, "instanceof": operator,
41 | "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
42 | "this": kw("this"), "class": kw("class"), "super": kw("atom"),
43 | "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
44 | "await": C, "async": kw("async")
45 | };
46 |
47 | // Extend the 'normal' keywords with the TypeScript language extensions
48 | if (isTS) {
49 | var type = {type: "variable", style: "variable-3"};
50 | var tsKeywords = {
51 | // object-like things
52 | "interface": kw("class"),
53 | "implements": C,
54 | "namespace": C,
55 | "module": kw("module"),
56 | "enum": kw("module"),
57 |
58 | // scope modifiers
59 | "public": kw("modifier"),
60 | "private": kw("modifier"),
61 | "protected": kw("modifier"),
62 | "abstract": kw("modifier"),
63 |
64 | // operators
65 | "as": operator,
66 |
67 | // types
68 | "string": type, "number": type, "boolean": type, "any": type
69 | };
70 |
71 | for (var attr in tsKeywords) {
72 | jsKeywords[attr] = tsKeywords[attr];
73 | }
74 | }
75 |
76 | return jsKeywords;
77 | }();
78 |
79 | var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
80 | var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
81 |
82 | function readRegexp(stream) {
83 | var escaped = false, next, inSet = false;
84 | while ((next = stream.next()) != null) {
85 | if (!escaped) {
86 | if (next == "/" && !inSet) return;
87 | if (next == "[") inSet = true;
88 | else if (inSet && next == "]") inSet = false;
89 | }
90 | escaped = !escaped && next == "\\";
91 | }
92 | }
93 |
94 | // Used as scratch variables to communicate multiple values without
95 | // consing up tons of objects.
96 | var type, content;
97 | function ret(tp, style, cont) {
98 | type = tp; content = cont;
99 | return style;
100 | }
101 | function tokenBase(stream, state) {
102 | var ch = stream.next();
103 | if (ch == '"' || ch == "'") {
104 | state.tokenize = tokenString(ch);
105 | return state.tokenize(stream, state);
106 | } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
107 | return ret("number", "number");
108 | } else if (ch == "." && stream.match("..")) {
109 | return ret("spread", "meta");
110 | } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
111 | return ret(ch);
112 | } else if (ch == "=" && stream.eat(">")) {
113 | return ret("=>", "operator");
114 | } else if (ch == "0" && stream.eat(/x/i)) {
115 | stream.eatWhile(/[\da-f]/i);
116 | return ret("number", "number");
117 | } else if (ch == "0" && stream.eat(/o/i)) {
118 | stream.eatWhile(/[0-7]/i);
119 | return ret("number", "number");
120 | } else if (ch == "0" && stream.eat(/b/i)) {
121 | stream.eatWhile(/[01]/i);
122 | return ret("number", "number");
123 | } else if (/\d/.test(ch)) {
124 | stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
125 | return ret("number", "number");
126 | } else if (ch == "/") {
127 | if (stream.eat("*")) {
128 | state.tokenize = tokenComment;
129 | return tokenComment(stream, state);
130 | } else if (stream.eat("/")) {
131 | stream.skipToEnd();
132 | return ret("comment", "comment");
133 | } else if (expressionAllowed(stream, state, 1)) {
134 | readRegexp(stream);
135 | stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
136 | return ret("regexp", "string-2");
137 | } else {
138 | stream.eatWhile(isOperatorChar);
139 | return ret("operator", "operator", stream.current());
140 | }
141 | } else if (ch == "`") {
142 | state.tokenize = tokenQuasi;
143 | return tokenQuasi(stream, state);
144 | } else if (ch == "#") {
145 | stream.skipToEnd();
146 | return ret("error", "error");
147 | } else if (isOperatorChar.test(ch)) {
148 | if (ch != ">" || !state.lexical || state.lexical.type != ">")
149 | stream.eatWhile(isOperatorChar);
150 | return ret("operator", "operator", stream.current());
151 | } else if (wordRE.test(ch)) {
152 | stream.eatWhile(wordRE);
153 | var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
154 | return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
155 | ret("variable", "variable", word);
156 | }
157 | }
158 |
159 | function tokenString(quote) {
160 | return function(stream, state) {
161 | var escaped = false, next;
162 | if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
163 | state.tokenize = tokenBase;
164 | return ret("jsonld-keyword", "meta");
165 | }
166 | while ((next = stream.next()) != null) {
167 | if (next == quote && !escaped) break;
168 | escaped = !escaped && next == "\\";
169 | }
170 | if (!escaped) state.tokenize = tokenBase;
171 | return ret("string", "string");
172 | };
173 | }
174 |
175 | function tokenComment(stream, state) {
176 | var maybeEnd = false, ch;
177 | while (ch = stream.next()) {
178 | if (ch == "/" && maybeEnd) {
179 | state.tokenize = tokenBase;
180 | break;
181 | }
182 | maybeEnd = (ch == "*");
183 | }
184 | return ret("comment", "comment");
185 | }
186 |
187 | function tokenQuasi(stream, state) {
188 | var escaped = false, next;
189 | while ((next = stream.next()) != null) {
190 | if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
191 | state.tokenize = tokenBase;
192 | break;
193 | }
194 | escaped = !escaped && next == "\\";
195 | }
196 | return ret("quasi", "string-2", stream.current());
197 | }
198 |
199 | var brackets = "([{}])";
200 | // This is a crude lookahead trick to try and notice that we're
201 | // parsing the argument patterns for a fat-arrow function before we
202 | // actually hit the arrow token. It only works if the arrow is on
203 | // the same line as the arguments and there's no strange noise
204 | // (comments) in between. Fallback is to only notice when we hit the
205 | // arrow, and not declare the arguments as locals for the arrow
206 | // body.
207 | function findFatArrow(stream, state) {
208 | if (state.fatArrowAt) state.fatArrowAt = null;
209 | var arrow = stream.string.indexOf("=>", stream.start);
210 | if (arrow < 0) return;
211 |
212 | if (isTS) { // Try to skip TypeScript return type declarations after the arguments
213 | var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
214 | if (m) arrow = m.index
215 | }
216 |
217 | var depth = 0, sawSomething = false;
218 | for (var pos = arrow - 1; pos >= 0; --pos) {
219 | var ch = stream.string.charAt(pos);
220 | var bracket = brackets.indexOf(ch);
221 | if (bracket >= 0 && bracket < 3) {
222 | if (!depth) { ++pos; break; }
223 | if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
224 | } else if (bracket >= 3 && bracket < 6) {
225 | ++depth;
226 | } else if (wordRE.test(ch)) {
227 | sawSomething = true;
228 | } else if (/["'\/]/.test(ch)) {
229 | return;
230 | } else if (sawSomething && !depth) {
231 | ++pos;
232 | break;
233 | }
234 | }
235 | if (sawSomething && !depth) state.fatArrowAt = pos;
236 | }
237 |
238 | // Parser
239 |
240 | var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
241 |
242 | function JSLexical(indented, column, type, align, prev, info) {
243 | this.indented = indented;
244 | this.column = column;
245 | this.type = type;
246 | this.prev = prev;
247 | this.info = info;
248 | if (align != null) this.align = align;
249 | }
250 |
251 | function inScope(state, varname) {
252 | for (var v = state.localVars; v; v = v.next)
253 | if (v.name == varname) return true;
254 | for (var cx = state.context; cx; cx = cx.prev) {
255 | for (var v = cx.vars; v; v = v.next)
256 | if (v.name == varname) return true;
257 | }
258 | }
259 |
260 | function parseJS(state, style, type, content, stream) {
261 | var cc = state.cc;
262 | // Communicate our context to the combinators.
263 | // (Less wasteful than consing up a hundred closures on every call.)
264 | cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
265 |
266 | if (!state.lexical.hasOwnProperty("align"))
267 | state.lexical.align = true;
268 |
269 | while(true) {
270 | var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
271 | if (combinator(type, content)) {
272 | while(cc.length && cc[cc.length - 1].lex)
273 | cc.pop()();
274 | if (cx.marked) return cx.marked;
275 | if (type == "variable" && inScope(state, content)) return "variable-2";
276 | return style;
277 | }
278 | }
279 | }
280 |
281 | // Combinator utils
282 |
283 | var cx = {state: null, column: null, marked: null, cc: null};
284 | function pass() {
285 | for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
286 | }
287 | function cont() {
288 | pass.apply(null, arguments);
289 | return true;
290 | }
291 | function register(varname) {
292 | function inList(list) {
293 | for (var v = list; v; v = v.next)
294 | if (v.name == varname) return true;
295 | return false;
296 | }
297 | var state = cx.state;
298 | cx.marked = "def";
299 | if (state.context) {
300 | if (inList(state.localVars)) return;
301 | state.localVars = {name: varname, next: state.localVars};
302 | } else {
303 | if (inList(state.globalVars)) return;
304 | if (parserConfig.globalVars)
305 | state.globalVars = {name: varname, next: state.globalVars};
306 | }
307 | }
308 |
309 | // Combinators
310 |
311 | var defaultVars = {name: "this", next: {name: "arguments"}};
312 | function pushcontext() {
313 | cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
314 | cx.state.localVars = defaultVars;
315 | }
316 | function popcontext() {
317 | cx.state.localVars = cx.state.context.vars;
318 | cx.state.context = cx.state.context.prev;
319 | }
320 | function pushlex(type, info) {
321 | var result = function() {
322 | var state = cx.state, indent = state.indented;
323 | if (state.lexical.type == "stat") indent = state.lexical.indented;
324 | else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
325 | indent = outer.indented;
326 | state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
327 | };
328 | result.lex = true;
329 | return result;
330 | }
331 | function poplex() {
332 | var state = cx.state;
333 | if (state.lexical.prev) {
334 | if (state.lexical.type == ")")
335 | state.indented = state.lexical.indented;
336 | state.lexical = state.lexical.prev;
337 | }
338 | }
339 | poplex.lex = true;
340 |
341 | function expect(wanted) {
342 | function exp(type) {
343 | if (type == wanted) return cont();
344 | else if (wanted == ";") return pass();
345 | else return cont(exp);
346 | };
347 | return exp;
348 | }
349 |
350 | function statement(type, value) {
351 | if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
352 | if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
353 | if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
354 | if (type == "{") return cont(pushlex("}"), block, poplex);
355 | if (type == ";") return cont();
356 | if (type == "if") {
357 | if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
358 | cx.state.cc.pop()();
359 | return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
360 | }
361 | if (type == "function") return cont(functiondef);
362 | if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
363 | if (type == "variable") {
364 | if (isTS && value == "type") {
365 | cx.marked = "keyword"
366 | return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
367 | } else {
368 | return cont(pushlex("stat"), maybelabel);
369 | }
370 | }
371 | if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
372 | block, poplex, poplex);
373 | if (type == "case") return cont(expression, expect(":"));
374 | if (type == "default") return cont(expect(":"));
375 | if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
376 | statement, poplex, popcontext);
377 | if (type == "class") return cont(pushlex("form"), className, poplex);
378 | if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
379 | if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
380 | if (type == "module") return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
381 | if (type == "async") return cont(statement)
382 | if (value == "@") return cont(expression, statement)
383 | return pass(pushlex("stat"), expression, expect(";"), poplex);
384 | }
385 | function expression(type) {
386 | return expressionInner(type, false);
387 | }
388 | function expressionNoComma(type) {
389 | return expressionInner(type, true);
390 | }
391 | function parenExpr(type) {
392 | if (type != "(") return pass()
393 | return cont(pushlex(")"), expression, expect(")"), poplex)
394 | }
395 | function expressionInner(type, noComma) {
396 | if (cx.state.fatArrowAt == cx.stream.start) {
397 | var body = noComma ? arrowBodyNoComma : arrowBody;
398 | if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
399 | else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
400 | }
401 |
402 | var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
403 | if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
404 | if (type == "function") return cont(functiondef, maybeop);
405 | if (type == "class") return cont(pushlex("form"), classExpression, poplex);
406 | if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
407 | if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
408 | if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
409 | if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
410 | if (type == "{") return contCommasep(objprop, "}", null, maybeop);
411 | if (type == "quasi") return pass(quasi, maybeop);
412 | if (type == "new") return cont(maybeTarget(noComma));
413 | return cont();
414 | }
415 | function maybeexpression(type) {
416 | if (type.match(/[;\}\)\],]/)) return pass();
417 | return pass(expression);
418 | }
419 | function maybeexpressionNoComma(type) {
420 | if (type.match(/[;\}\)\],]/)) return pass();
421 | return pass(expressionNoComma);
422 | }
423 |
424 | function maybeoperatorComma(type, value) {
425 | if (type == ",") return cont(expression);
426 | return maybeoperatorNoComma(type, value, false);
427 | }
428 | function maybeoperatorNoComma(type, value, noComma) {
429 | var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
430 | var expr = noComma == false ? expression : expressionNoComma;
431 | if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
432 | if (type == "operator") {
433 | if (/\+\+|--/.test(value)) return cont(me);
434 | if (value == "?") return cont(expression, expect(":"), expr);
435 | return cont(expr);
436 | }
437 | if (type == "quasi") { return pass(quasi, me); }
438 | if (type == ";") return;
439 | if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
440 | if (type == ".") return cont(property, me);
441 | if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
442 | }
443 | function quasi(type, value) {
444 | if (type != "quasi") return pass();
445 | if (value.slice(value.length - 2) != "${") return cont(quasi);
446 | return cont(expression, continueQuasi);
447 | }
448 | function continueQuasi(type) {
449 | if (type == "}") {
450 | cx.marked = "string-2";
451 | cx.state.tokenize = tokenQuasi;
452 | return cont(quasi);
453 | }
454 | }
455 | function arrowBody(type) {
456 | findFatArrow(cx.stream, cx.state);
457 | return pass(type == "{" ? statement : expression);
458 | }
459 | function arrowBodyNoComma(type) {
460 | findFatArrow(cx.stream, cx.state);
461 | return pass(type == "{" ? statement : expressionNoComma);
462 | }
463 | function maybeTarget(noComma) {
464 | return function(type) {
465 | if (type == ".") return cont(noComma ? targetNoComma : target);
466 | else return pass(noComma ? expressionNoComma : expression);
467 | };
468 | }
469 | function target(_, value) {
470 | if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
471 | }
472 | function targetNoComma(_, value) {
473 | if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
474 | }
475 | function maybelabel(type) {
476 | if (type == ":") return cont(poplex, statement);
477 | return pass(maybeoperatorComma, expect(";"), poplex);
478 | }
479 | function property(type) {
480 | if (type == "variable") {cx.marked = "property"; return cont();}
481 | }
482 | function objprop(type, value) {
483 | if (type == "async") {
484 | cx.marked = "property";
485 | return cont(objprop);
486 | } else if (type == "variable" || cx.style == "keyword") {
487 | cx.marked = "property";
488 | if (value == "get" || value == "set") return cont(getterSetter);
489 | return cont(afterprop);
490 | } else if (type == "number" || type == "string") {
491 | cx.marked = jsonldMode ? "property" : (cx.style + " property");
492 | return cont(afterprop);
493 | } else if (type == "jsonld-keyword") {
494 | return cont(afterprop);
495 | } else if (type == "modifier") {
496 | return cont(objprop)
497 | } else if (type == "[") {
498 | return cont(expression, expect("]"), afterprop);
499 | } else if (type == "spread") {
500 | return cont(expression);
501 | } else if (type == ":") {
502 | return pass(afterprop)
503 | }
504 | }
505 | function getterSetter(type) {
506 | if (type != "variable") return pass(afterprop);
507 | cx.marked = "property";
508 | return cont(functiondef);
509 | }
510 | function afterprop(type) {
511 | if (type == ":") return cont(expressionNoComma);
512 | if (type == "(") return pass(functiondef);
513 | }
514 | function commasep(what, end, sep) {
515 | function proceed(type, value) {
516 | if (sep ? sep.indexOf(type) > -1 : type == ",") {
517 | var lex = cx.state.lexical;
518 | if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
519 | return cont(function(type, value) {
520 | if (type == end || value == end) return pass()
521 | return pass(what)
522 | }, proceed);
523 | }
524 | if (type == end || value == end) return cont();
525 | return cont(expect(end));
526 | }
527 | return function(type, value) {
528 | if (type == end || value == end) return cont();
529 | return pass(what, proceed);
530 | };
531 | }
532 | function contCommasep(what, end, info) {
533 | for (var i = 3; i < arguments.length; i++)
534 | cx.cc.push(arguments[i]);
535 | return cont(pushlex(end, info), commasep(what, end), poplex);
536 | }
537 | function block(type) {
538 | if (type == "}") return cont();
539 | return pass(statement, block);
540 | }
541 | function maybetype(type, value) {
542 | if (isTS) {
543 | if (type == ":") return cont(typeexpr);
544 | if (value == "?") return cont(maybetype);
545 | }
546 | }
547 | function typeexpr(type) {
548 | if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);}
549 | if (type == "string" || type == "number" || type == "atom") return cont(afterType);
550 | if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
551 | if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType)
552 | }
553 | function maybeReturnType(type) {
554 | if (type == "=>") return cont(typeexpr)
555 | }
556 | function typeprop(type, value) {
557 | if (type == "variable" || cx.style == "keyword") {
558 | cx.marked = "property"
559 | return cont(typeprop)
560 | } else if (value == "?") {
561 | return cont(typeprop)
562 | } else if (type == ":") {
563 | return cont(typeexpr)
564 | } else if (type == "[") {
565 | return cont(expression, maybetype, expect("]"), typeprop)
566 | }
567 | }
568 | function typearg(type) {
569 | if (type == "variable") return cont(typearg)
570 | else if (type == ":") return cont(typeexpr)
571 | }
572 | function afterType(type, value) {
573 | if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
574 | if (value == "|" || type == ".") return cont(typeexpr)
575 | if (type == "[") return cont(expect("]"), afterType)
576 | if (value == "extends") return cont(typeexpr)
577 | }
578 | function vardef() {
579 | return pass(pattern, maybetype, maybeAssign, vardefCont);
580 | }
581 | function pattern(type, value) {
582 | if (type == "modifier") return cont(pattern)
583 | if (type == "variable") { register(value); return cont(); }
584 | if (type == "spread") return cont(pattern);
585 | if (type == "[") return contCommasep(pattern, "]");
586 | if (type == "{") return contCommasep(proppattern, "}");
587 | }
588 | function proppattern(type, value) {
589 | if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
590 | register(value);
591 | return cont(maybeAssign);
592 | }
593 | if (type == "variable") cx.marked = "property";
594 | if (type == "spread") return cont(pattern);
595 | if (type == "}") return pass();
596 | return cont(expect(":"), pattern, maybeAssign);
597 | }
598 | function maybeAssign(_type, value) {
599 | if (value == "=") return cont(expressionNoComma);
600 | }
601 | function vardefCont(type) {
602 | if (type == ",") return cont(vardef);
603 | }
604 | function maybeelse(type, value) {
605 | if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
606 | }
607 | function forspec(type) {
608 | if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
609 | }
610 | function forspec1(type) {
611 | if (type == "var") return cont(vardef, expect(";"), forspec2);
612 | if (type == ";") return cont(forspec2);
613 | if (type == "variable") return cont(formaybeinof);
614 | return pass(expression, expect(";"), forspec2);
615 | }
616 | function formaybeinof(_type, value) {
617 | if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
618 | return cont(maybeoperatorComma, forspec2);
619 | }
620 | function forspec2(type, value) {
621 | if (type == ";") return cont(forspec3);
622 | if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
623 | return pass(expression, expect(";"), forspec3);
624 | }
625 | function forspec3(type) {
626 | if (type != ")") cont(expression);
627 | }
628 | function functiondef(type, value) {
629 | if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
630 | if (type == "variable") {register(value); return cont(functiondef);}
631 | if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
632 | if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef)
633 | }
634 | function funarg(type) {
635 | if (type == "spread") return cont(funarg);
636 | return pass(pattern, maybetype, maybeAssign);
637 | }
638 | function classExpression(type, value) {
639 | // Class expressions may have an optional name.
640 | if (type == "variable") return className(type, value);
641 | return classNameAfter(type, value);
642 | }
643 | function className(type, value) {
644 | if (type == "variable") {register(value); return cont(classNameAfter);}
645 | }
646 | function classNameAfter(type, value) {
647 | if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, classNameAfter)
648 | if (value == "extends" || value == "implements" || (isTS && type == ","))
649 | return cont(isTS ? typeexpr : expression, classNameAfter);
650 | if (type == "{") return cont(pushlex("}"), classBody, poplex);
651 | }
652 | function classBody(type, value) {
653 | if (type == "variable" || cx.style == "keyword") {
654 | if ((value == "async" || value == "static" || value == "get" || value == "set" ||
655 | (isTS && (value == "public" || value == "private" || value == "protected" || value == "readonly" || value == "abstract"))) &&
656 | cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false)) {
657 | cx.marked = "keyword";
658 | return cont(classBody);
659 | }
660 | cx.marked = "property";
661 | return cont(isTS ? classfield : functiondef, classBody);
662 | }
663 | if (type == "[")
664 | return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody)
665 | if (value == "*") {
666 | cx.marked = "keyword";
667 | return cont(classBody);
668 | }
669 | if (type == ";") return cont(classBody);
670 | if (type == "}") return cont();
671 | if (value == "@") return cont(expression, classBody)
672 | }
673 | function classfield(type, value) {
674 | if (value == "?") return cont(classfield)
675 | if (type == ":") return cont(typeexpr, maybeAssign)
676 | if (value == "=") return cont(expressionNoComma)
677 | return pass(functiondef)
678 | }
679 | function afterExport(type, value) {
680 | if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
681 | if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
682 | if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
683 | return pass(statement);
684 | }
685 | function exportField(type, value) {
686 | if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
687 | if (type == "variable") return pass(expressionNoComma, exportField);
688 | }
689 | function afterImport(type) {
690 | if (type == "string") return cont();
691 | return pass(importSpec, maybeMoreImports, maybeFrom);
692 | }
693 | function importSpec(type, value) {
694 | if (type == "{") return contCommasep(importSpec, "}");
695 | if (type == "variable") register(value);
696 | if (value == "*") cx.marked = "keyword";
697 | return cont(maybeAs);
698 | }
699 | function maybeMoreImports(type) {
700 | if (type == ",") return cont(importSpec, maybeMoreImports)
701 | }
702 | function maybeAs(_type, value) {
703 | if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
704 | }
705 | function maybeFrom(_type, value) {
706 | if (value == "from") { cx.marked = "keyword"; return cont(expression); }
707 | }
708 | function arrayLiteral(type) {
709 | if (type == "]") return cont();
710 | return pass(commasep(expressionNoComma, "]"));
711 | }
712 |
713 | function isContinuedStatement(state, textAfter) {
714 | return state.lastType == "operator" || state.lastType == "," ||
715 | isOperatorChar.test(textAfter.charAt(0)) ||
716 | /[,.]/.test(textAfter.charAt(0));
717 | }
718 |
719 | // Interface
720 |
721 | return {
722 | startState: function(basecolumn) {
723 | var state = {
724 | tokenize: tokenBase,
725 | lastType: "sof",
726 | cc: [],
727 | lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
728 | localVars: parserConfig.localVars,
729 | context: parserConfig.localVars && {vars: parserConfig.localVars},
730 | indented: basecolumn || 0
731 | };
732 | if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
733 | state.globalVars = parserConfig.globalVars;
734 | return state;
735 | },
736 |
737 | token: function(stream, state) {
738 | if (stream.sol()) {
739 | if (!state.lexical.hasOwnProperty("align"))
740 | state.lexical.align = false;
741 | state.indented = stream.indentation();
742 | findFatArrow(stream, state);
743 | }
744 | if (state.tokenize != tokenComment && stream.eatSpace()) return null;
745 | var style = state.tokenize(stream, state);
746 | if (type == "comment") return style;
747 | state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
748 | return parseJS(state, style, type, content, stream);
749 | },
750 |
751 | indent: function(state, textAfter) {
752 | if (state.tokenize == tokenComment) return CodeMirror.Pass;
753 | if (state.tokenize != tokenBase) return 0;
754 | var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
755 | // Kludge to prevent 'maybelse' from blocking lexical scope pops
756 | if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
757 | var c = state.cc[i];
758 | if (c == poplex) lexical = lexical.prev;
759 | else if (c != maybeelse) break;
760 | }
761 | while ((lexical.type == "stat" || lexical.type == "form") &&
762 | (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
763 | (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
764 | !/^[,\.=+\-*:?[\(]/.test(textAfter))))
765 | lexical = lexical.prev;
766 | if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
767 | lexical = lexical.prev;
768 | var type = lexical.type, closing = firstChar == type;
769 |
770 | if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
771 | else if (type == "form" && firstChar == "{") return lexical.indented;
772 | else if (type == "form") return lexical.indented + indentUnit;
773 | else if (type == "stat")
774 | return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
775 | else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
776 | return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
777 | else if (lexical.align) return lexical.column + (closing ? 0 : 1);
778 | else return lexical.indented + (closing ? 0 : indentUnit);
779 | },
780 |
781 | electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
782 | blockCommentStart: jsonMode ? null : "/*",
783 | blockCommentEnd: jsonMode ? null : "*/",
784 | lineComment: jsonMode ? null : "//",
785 | fold: "brace",
786 | closeBrackets: "()[]{}''\"\"``",
787 |
788 | helperType: jsonMode ? "json" : "javascript",
789 | jsonldMode: jsonldMode,
790 | jsonMode: jsonMode,
791 |
792 | expressionAllowed: expressionAllowed,
793 | skipExpression: function(state) {
794 | var top = state.cc[state.cc.length - 1]
795 | if (top == expression || top == expressionNoComma) state.cc.pop()
796 | }
797 | };
798 | });
799 |
800 | CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
801 |
802 | CodeMirror.defineMIME("text/javascript", "javascript");
803 | CodeMirror.defineMIME("text/ecmascript", "javascript");
804 | CodeMirror.defineMIME("application/javascript", "javascript");
805 | CodeMirror.defineMIME("application/x-javascript", "javascript");
806 | CodeMirror.defineMIME("application/ecmascript", "javascript");
807 | CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
808 | CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
809 | CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
810 | CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
811 | CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
812 |
813 | });
814 |
--------------------------------------------------------------------------------
/docs/javascript/json-ld.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | CodeMirror: JSON-LD mode
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
27 |
28 |
29 | JSON-LD mode
30 |
31 |
32 |
61 |
62 |
70 |
71 | This is a specialization of the JavaScript mode .
72 |
73 |
--------------------------------------------------------------------------------
/docs/javascript/typescript.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | CodeMirror: TypeScript mode
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
24 |
25 |
26 | TypeScript mode
27 |
28 |
29 |
51 |
52 |
59 |
60 | This is a specialization of the JavaScript mode .
61 |
62 |
--------------------------------------------------------------------------------
/docs/json/invalid-keys.json:
--------------------------------------------------------------------------------
1 | {
2 | "users": {
3 | "POST /user": {
4 | "headers": {
5 | "content-type": "application/json"
6 | }
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/docs/json/nested.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "shakyShane",
3 | "profile": {
4 | "links": [
5 | {
6 | "name": "twitter",
7 | "url": "https://twitter.com/shaneOsbourne"
8 | },
9 | {
10 | "name": "Medium",
11 | "url": "https://medium.com/@shakyShane"
12 | }
13 | ]
14 | },
15 | "location": null,
16 | "email": null,
17 | "bio": null,
18 | "public_repos": 145,
19 | "public_gists": 69,
20 | "followers": 298,
21 | "following": 1,
22 | "created_at": "2012-04-14T17:34:37Z",
23 | "updated_at": "2017-04-26T12:43:35Z"
24 | }
--------------------------------------------------------------------------------
/docs/json/optional.json:
--------------------------------------------------------------------------------
1 | {
2 | "users": [
3 | {
4 | "name": "shane",
5 | "age": "32"
6 | },
7 | {
8 | "name": "sally",
9 | "age": "29",
10 | "pets": [{
11 | "type": "cat",
12 | "name": "kittie"
13 | }]
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/docs/json/recursive.json:
--------------------------------------------------------------------------------
1 | {
2 | "items": [
3 | {
4 | "body": [
5 | {
6 | "body": [
7 | {
8 | "body": []
9 | }
10 | ]
11 | }
12 | ]
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/docs/lib/codemirror.css:
--------------------------------------------------------------------------------
1 | /* BASICS */
2 |
3 | .CodeMirror {
4 | /* Set height, width, borders, and global font properties here */
5 | font-family: monospace;
6 | height: 100vh;
7 | color: black;
8 | }
9 |
10 | /* PADDING */
11 |
12 | .CodeMirror-lines {
13 | padding: 1em 0; /* Vertical padding around content */
14 | }
15 | .CodeMirror pre {
16 | padding: 0 1em; /* Horizontal padding of content */
17 | }
18 |
19 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
20 | background-color: white; /* The little square between H and V scrollbars */
21 | }
22 |
23 | /* GUTTER */
24 |
25 | .CodeMirror-gutters {
26 | border-right: 1px solid #ddd;
27 | background-color: #f7f7f7;
28 | white-space: nowrap;
29 | }
30 | .CodeMirror-linenumbers {}
31 | .CodeMirror-linenumber {
32 | padding: 0 3px 0 5px;
33 | min-width: 20px;
34 | text-align: right;
35 | color: #999;
36 | white-space: nowrap;
37 | }
38 |
39 | .CodeMirror-guttermarker { color: black; }
40 | .CodeMirror-guttermarker-subtle { color: #999; }
41 |
42 | /* CURSOR */
43 |
44 | .CodeMirror-cursor {
45 | border-left: 1px solid black;
46 | border-right: none;
47 | width: 0;
48 | }
49 | /* Shown when moving in bi-directional text */
50 | .CodeMirror div.CodeMirror-secondarycursor {
51 | border-left: 1px solid silver;
52 | }
53 | .cm-fat-cursor .CodeMirror-cursor {
54 | width: auto;
55 | border: 0 !important;
56 | background: #7e7;
57 | }
58 | .cm-fat-cursor div.CodeMirror-cursors {
59 | z-index: 1;
60 | }
61 |
62 | .cm-animate-fat-cursor {
63 | width: auto;
64 | border: 0;
65 | -webkit-animation: blink 1.06s steps(1) infinite;
66 | -moz-animation: blink 1.06s steps(1) infinite;
67 | animation: blink 1.06s steps(1) infinite;
68 | background-color: #7e7;
69 | }
70 | @-moz-keyframes blink {
71 | 0% {}
72 | 50% { background-color: transparent; }
73 | 100% {}
74 | }
75 | @-webkit-keyframes blink {
76 | 0% {}
77 | 50% { background-color: transparent; }
78 | 100% {}
79 | }
80 | @keyframes blink {
81 | 0% {}
82 | 50% { background-color: transparent; }
83 | 100% {}
84 | }
85 |
86 | /* Can style cursor different in overwrite (non-insert) mode */
87 | .CodeMirror-overwrite .CodeMirror-cursor {}
88 |
89 | .cm-tab { display: inline-block; text-decoration: inherit; }
90 |
91 | .CodeMirror-rulers {
92 | position: absolute;
93 | left: 0; right: 0; top: -50px; bottom: -20px;
94 | overflow: hidden;
95 | }
96 | .CodeMirror-ruler {
97 | border-left: 1px solid #ccc;
98 | top: 0; bottom: 0;
99 | position: absolute;
100 | }
101 |
102 | /* DEFAULT THEME */
103 |
104 | .cm-s-default .cm-header {color: blue;}
105 | .cm-s-default .cm-quote {color: #090;}
106 | .cm-negative {color: #d44;}
107 | .cm-positive {color: #292;}
108 | .cm-header, .cm-strong {font-weight: bold;}
109 | .cm-em {font-style: italic;}
110 | .cm-link {text-decoration: underline;}
111 | .cm-strikethrough {text-decoration: line-through;}
112 |
113 | .cm-s-default .cm-keyword {color: #708;}
114 | .cm-s-default .cm-atom {color: #219;}
115 | .cm-s-default .cm-number {color: #164;}
116 | .cm-s-default .cm-def {color: #00f;}
117 | .cm-s-default .cm-variable,
118 | .cm-s-default .cm-punctuation,
119 | .cm-s-default .cm-property,
120 | .cm-s-default .cm-operator {}
121 | .cm-s-default .cm-variable-2 {color: #05a;}
122 | .cm-s-default .cm-variable-3 {color: #085;}
123 | .cm-s-default .cm-comment {color: #a50;}
124 | .cm-s-default .cm-string {color: #a11;}
125 | .cm-s-default .cm-string-2 {color: #f50;}
126 | .cm-s-default .cm-meta {color: #555;}
127 | .cm-s-default .cm-qualifier {color: #555;}
128 | .cm-s-default .cm-builtin {color: #30a;}
129 | .cm-s-default .cm-bracket {color: #997;}
130 | .cm-s-default .cm-tag {color: #170;}
131 | .cm-s-default .cm-attribute {color: #00c;}
132 | .cm-s-default .cm-hr {color: #999;}
133 | .cm-s-default .cm-link {color: #00c;}
134 |
135 | .cm-s-default .cm-error {color: #f00;}
136 | .cm-invalidchar {color: #f00;}
137 |
138 | .CodeMirror-composing { border-bottom: 2px solid; }
139 |
140 | /* Default styles for common addons */
141 |
142 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
143 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
144 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
145 | .CodeMirror-activeline-background {background: #e8f2ff;}
146 |
147 | /* STOP */
148 |
149 | /* The rest of this file contains styles related to the mechanics of
150 | the editor. You probably shouldn't touch them. */
151 |
152 | .CodeMirror {
153 | position: relative;
154 | overflow: hidden;
155 | background: white;
156 | }
157 |
158 | .CodeMirror-scroll {
159 | overflow: scroll !important; /* Things will break if this is overridden */
160 | /* 30px is the magic margin used to hide the element's real scrollbars */
161 | /* See overflow: hidden in .CodeMirror */
162 | margin-bottom: -30px; margin-right: -30px;
163 | padding-bottom: 30px;
164 | height: 100%;
165 | outline: none; /* Prevent dragging from highlighting the element */
166 | position: relative;
167 | }
168 | .CodeMirror-sizer {
169 | position: relative;
170 | border-right: 30px solid transparent;
171 | }
172 |
173 | /* The fake, visible scrollbars. Used to force redraw during scrolling
174 | before actual scrolling happens, thus preventing shaking and
175 | flickering artifacts. */
176 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
177 | position: absolute;
178 | z-index: 6;
179 | display: none;
180 | }
181 | .CodeMirror-vscrollbar {
182 | right: 0; top: 0;
183 | overflow-x: hidden;
184 | overflow-y: scroll;
185 | }
186 | .CodeMirror-hscrollbar {
187 | bottom: 0; left: 0;
188 | overflow-y: hidden;
189 | overflow-x: scroll;
190 | }
191 | .CodeMirror-scrollbar-filler {
192 | right: 0; bottom: 0;
193 | }
194 | .CodeMirror-gutter-filler {
195 | left: 0; bottom: 0;
196 | }
197 |
198 | .CodeMirror-gutters {
199 | position: absolute; left: 0; top: 0;
200 | min-height: 100%;
201 | z-index: 3;
202 | }
203 | .CodeMirror-gutter {
204 | white-space: normal;
205 | height: 100%;
206 | display: inline-block;
207 | vertical-align: top;
208 | margin-bottom: -30px;
209 | }
210 | .CodeMirror-gutter-wrapper {
211 | position: absolute;
212 | z-index: 4;
213 | background: none !important;
214 | border: none !important;
215 | }
216 | .CodeMirror-gutter-background {
217 | position: absolute;
218 | top: 0; bottom: 0;
219 | z-index: 4;
220 | }
221 | .CodeMirror-gutter-elt {
222 | position: absolute;
223 | cursor: default;
224 | z-index: 4;
225 | }
226 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent }
227 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
228 |
229 | .CodeMirror-lines {
230 | cursor: text;
231 | min-height: 1px; /* prevents collapsing before first draw */
232 | }
233 | .CodeMirror pre {
234 | /* Reset some styles that the rest of the page might have set */
235 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
236 | border-width: 0;
237 | background: transparent;
238 | font-family: inherit;
239 | font-size: inherit;
240 | margin: 0;
241 | white-space: pre;
242 | word-wrap: normal;
243 | line-height: inherit;
244 | color: inherit;
245 | z-index: 2;
246 | position: relative;
247 | overflow: visible;
248 | -webkit-tap-highlight-color: transparent;
249 | -webkit-font-variant-ligatures: contextual;
250 | font-variant-ligatures: contextual;
251 | }
252 | .CodeMirror-wrap pre {
253 | word-wrap: break-word;
254 | white-space: pre-wrap;
255 | word-break: normal;
256 | }
257 |
258 | .CodeMirror-linebackground {
259 | position: absolute;
260 | left: 0; right: 0; top: 0; bottom: 0;
261 | z-index: 0;
262 | }
263 |
264 | .CodeMirror-linewidget {
265 | position: relative;
266 | z-index: 2;
267 | overflow: auto;
268 | }
269 |
270 | .CodeMirror-widget {}
271 |
272 | .CodeMirror-rtl pre { direction: rtl; }
273 |
274 | .CodeMirror-code {
275 | outline: none;
276 | }
277 |
278 | /* Force content-box sizing for the elements where we expect it */
279 | .CodeMirror-scroll,
280 | .CodeMirror-sizer,
281 | .CodeMirror-gutter,
282 | .CodeMirror-gutters,
283 | .CodeMirror-linenumber {
284 | -moz-box-sizing: content-box;
285 | box-sizing: content-box;
286 | }
287 |
288 | .CodeMirror-measure {
289 | position: absolute;
290 | width: 100%;
291 | height: 0;
292 | overflow: hidden;
293 | visibility: hidden;
294 | }
295 |
296 | .CodeMirror-cursor {
297 | position: absolute;
298 | pointer-events: none;
299 | }
300 | .CodeMirror-measure pre { position: static; }
301 |
302 | div.CodeMirror-cursors {
303 | visibility: hidden;
304 | position: relative;
305 | z-index: 3;
306 | }
307 | div.CodeMirror-dragcursors {
308 | visibility: visible;
309 | }
310 |
311 | .CodeMirror-focused div.CodeMirror-cursors {
312 | visibility: visible;
313 | }
314 |
315 | .CodeMirror-selected { background: #d9d9d9; }
316 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
317 | .CodeMirror-crosshair { cursor: crosshair; }
318 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
319 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
320 |
321 | .cm-searching {
322 | background: #ffa;
323 | background: rgba(255, 255, 0, .4);
324 | }
325 |
326 | /* Used to force a border model for a node */
327 | .cm-force-border { padding-right: .1px; }
328 |
329 | @media print {
330 | /* Hide the cursor when printing */
331 | .CodeMirror div.CodeMirror-cursors {
332 | visibility: hidden;
333 | }
334 | }
335 |
336 | /* See issue #2901 */
337 | .cm-tab-wrap-hack:after { content: ''; }
338 |
339 | /* Help users use markselection to safely style text background */
340 | span.CodeMirror-selectedtext { background: none; }
341 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "unfetch": "^2.1.2",
4 | "webpack": "^2.6.1"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/docs/src/index.js:
--------------------------------------------------------------------------------
1 | const fetch = require('unfetch').default;
2 |
3 | const iniialJson = getHash() || `{
4 | "author": "shakyShane",
5 | "profile": {
6 | "links": [
7 | {
8 | "name": "twitter",
9 | "url": "https://twitter.com/shaneOsbourne"
10 | },
11 | {
12 | "name": "Medium",
13 | "url": "https://medium.com/@shakyShane"
14 | }
15 | ]
16 | },
17 | "location": null,
18 | "email": null,
19 | "bio": null,
20 | "public_repos": 145,
21 | "public_gists": 69,
22 | "followers": 298,
23 | "following": 1,
24 | "created_at": "2012-04-14T17:34:37Z",
25 | "updated_at": "2017-04-26T12:43:35Z"
26 | }`;
27 | const $ = document.querySelector.bind(document);
28 | const statusElem = $('.status');
29 | const statusText = $('.debug');
30 | const flow = $('#flow');
31 | const rootName = $('#root-name');
32 | const prefix = $('#prefix');
33 | const namespace = $('#namespace');
34 | const examplesElem = $('#examples');
35 | const examples = [
36 | ['json/nested.json', 'Nested Objects & Arrays'],
37 | ['json/optional.json', 'Optional Fields'],
38 | ['json/recursive.json', 'Recursive data structures'],
39 | ['json/invalid-keys.json', 'Invalid property names']
40 | ];
41 |
42 | examples.forEach(([json, title]) => {
43 | const opt = document.createElement('option');
44 | opt.value = json;
45 | opt.textContent = title;
46 | examplesElem.appendChild(opt);
47 | });
48 |
49 | examplesElem.addEventListener('change', function () {
50 | if (examplesElem.value !== 'Select an example') {
51 | examplesElem.parentNode.classList.add('loading');
52 | const resp = fetch(examplesElem.value).then(x => x.text());
53 | resp.then(x => jsonInput.setValue(x));
54 | }
55 | });
56 |
57 | let defaults = {
58 | flow: false,
59 | namespace: '',
60 | prefix: 'I',
61 | rootName: 'RootObject'
62 | };
63 |
64 | flow.addEventListener('change', function () {
65 | setOutput(jsonInput.getValue(), options({flow: flow.checked}));
66 | });
67 | namespace.addEventListener('input', function () {
68 | setOutput(jsonInput.getValue(), options({namespace: namespace.value}));
69 | });
70 | rootName.addEventListener('input', function () {
71 | setOutput(jsonInput.getValue(), options({rootName: rootName.value}));
72 | });
73 | rootName.addEventListener('input', function () {
74 | setOutput(jsonInput.getValue(), options({rootName: rootName.value}));
75 | });
76 | prefix.addEventListener('input', function () {
77 | setOutput(jsonInput.getValue(), options({prefix: prefix.value}));
78 | });
79 | const ts = json2ts(iniialJson, defaults);
80 |
81 | var tsOutput = CodeMirror($('#ts'), {
82 | value: ts,
83 | mode: {name: "javascript", typescript: true}
84 | });
85 | var jsonInput = CodeMirror($('#app'), {
86 | value: iniialJson,
87 | mode: {name: "javascript", json: true}
88 | });
89 |
90 | function setOutput(json, options) {
91 | if (json && json.length > 3) {
92 | JSON.parse(json);
93 | const ts = json2ts(json, options);
94 | tsOutput.setValue(ts);
95 | status('success', 'JSON: All good!');
96 | }
97 | }
98 |
99 | jsonInput.on('change', function (obj) {
100 | try {
101 | setOutput(jsonInput.getValue());
102 | } catch (e) {
103 | status('error', `Error parsing JSON: ${e.message}`);
104 | console.log('Some JSON error?', e.message);
105 | }
106 | });
107 |
108 | function getHash() {
109 | if (window.location.hash) {
110 | if (window.location.hash.slice(0, 5) === '#src=') {
111 | const maybe = window.location.hash.slice(5);
112 | if (maybe) {
113 | return decodeURIComponent(maybe);
114 | }
115 | }
116 | }
117 | return "";
118 | }
119 |
120 | const share = $('a[href="#share"]');
121 | share.addEventListener('click', function (e) {
122 | e.preventDefault();
123 | window.location.hash = `src=${encodeURIComponent(jsonInput.getValue().trim())}`;
124 | });
125 |
126 | function status(type, text) {
127 | statusElem.classList.remove('success');
128 | statusElem.classList.remove('error');
129 | statusElem.classList.add(type);
130 | statusText.textContent = text;
131 | }
132 | function options(incoming) {
133 | defaults = Object.assign({},
134 | defaults,
135 | incoming
136 | );
137 | return defaults;
138 | }
139 |
--------------------------------------------------------------------------------
/json-ts2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakyShane/json-ts/8d6195b2fa0aeaa9df7f35fa3142f5ada6c98beb/json-ts2.gif
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "json-ts",
3 | "version": "1.6.4",
4 | "description": "Automatically generate Typescript Definition files or Flow types from JSON input",
5 | "dependencies": {
6 | "get-stdin": "^5.0.1",
7 | "immutable": "^3.8.1",
8 | "minimist": "^1.2.0",
9 | "needsquotes": "^1.0.0",
10 | "transducers-js": "^0.4.174",
11 | "typescript": "^2.3.2"
12 | },
13 | "keywords": [
14 | "json",
15 | "typescript",
16 | "flow",
17 | "types",
18 | "interfaces"
19 | ],
20 | "scripts": {
21 | "test": "tsc && jest --runInBand",
22 | "prepublish": "npm test"
23 | },
24 | "bin": {
25 | "json-ts": "dist/bin.js"
26 | },
27 | "files": [
28 | "dist",
29 | "src",
30 | "_.js"
31 | ],
32 | "repository": "shakyshane/json-ts",
33 | "main": "dist/index.js",
34 | "devDependencies": {
35 | "@types/node": "^7.0.18",
36 | "browserify": "^14.3.0",
37 | "exorcist": "^0.4.0",
38 | "jest": "22",
39 | "source-map-support": "^0.4.15",
40 | "uglify-js": "^3.0.10"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/shakyShane/json-ts)
2 |
3 | ## `npm install -g json-ts`
4 |
5 | > Automatically generate Typescript Definition files or Flow types from JSON input.
6 |
7 | > Use it via the API, CLI, or [Website](https://shakyshane.github.io/json-ts/)
8 |
9 | 
10 |
11 | ### How does **json-ts** stack up against the competition?
12 |
13 | |Feature |json-ts (this library) |[json-to-ts](https://github.com/MariusAlch/json-to-ts) |[json2ts](http://json2ts.com/) |
14 | |---|---|---|---|
15 | |simple literal types (number, string etc) |**YES** |YES |YES |
16 | |array type, all elements of same kind |**YES** |YES |YES |
17 | |merge multiple json files|**YES (cli, v1.6 & above)** |NO |NO |
18 | |optional members | **YES** | YES | NO |
19 | |array union types | **YES** |YES |NO |
20 | |correct handling of top-level values ([strings](https://shakyshane.github.io/json-ts/#src=%22some-api-token-as-string%22), [arrays](https://shakyshane.github.io/json-ts/#src=%5B1%2C%202%2C%203%5D), [arrays of objects](https://shakyshane.github.io/json-ts/#src=%5B%0A%20%20%7B%22name%22%3A%20%22shane%22%7D%2C%0A%20%20%7B%22name%22%3A%20%22kittie%22%2C%20%22age%22%3A%2030%7D%0A%5D), [numbers](https://shakyshane.github.io/json-ts/#src=1) etc) |**YES** |NO |NO |
21 | |recursive data structures ([see here](https://github.com/shakyShane/json-ts/blob/master/__tests__/magento/categories.json)) |**YES** |NO |NO |
22 | |nested type literals (to account for invalid [interface names](https://github.com/shakyShane/json-ts/blob/master/__tests__/swagger/schema.json)) |**YES** |YES |NO |
23 | |output @flow types |**YES** |NO |NO |
24 | |Website |**[YES](https://shakyshane.github.io/json-ts/)** |[YES](http://www.jsontots.com/) |[YES](http://json2ts.com/) |
25 | |CLI |**YES** |NO |NO |
26 | |API |**YES** |YES |NO |
27 |
28 | ## Quick-start
29 | ```bash
30 | # install
31 | npm install -g json-ts
32 |
33 | # run against a single JSON file
34 | json-ts dir/myfile.json
35 |
36 | # run against multiple single JSON files (interfaces will be merged)
37 | json-ts api/resp1.json api/resp2.json
38 | ```
39 |
40 | ## Usage (CLI)
41 | Note: only stdin (which requires the --stdin flag) & filepaths are supported right now.
42 | Later I will add support for Windows, reading data from network requests etc.
43 |
44 | ```bash
45 | ## piping via stdin
46 | curl https://jsonplaceholder.typicode.com/posts/1 | json-ts --stdin
47 |
48 | ## reading single json file from disk
49 | json-ts my-file.json
50 |
51 | ## reading multiple json files from disk
52 | json-ts my-file.json my-other-file.json
53 | ```
54 |
55 | ... produces the following:
56 |
57 | ```ts
58 | interface IRootObject {
59 | userId: number;
60 | id: number;
61 | title: string;
62 | body: string;
63 | }
64 | ```
65 |
66 | ## Usage (API)
67 |
68 | ```bash
69 | npm install json-ts --save-dev
70 | ```
71 |
72 | ```js
73 | const { json2ts } = require('json-ts');
74 | const json = `
75 | {
76 | "name": "Shane"
77 | }
78 | `;
79 | console.log(json2ts(json))
80 | ```
81 |
82 | ... produces the following:
83 |
84 | ```ts
85 | interface IRootObject {
86 | name: string;
87 | }
88 | ```
89 |
90 | For more examples, see the [Tests](https://github.com/shakyShane/json-ts/tree/master/__tests__)
91 |
92 | ## Options
93 |
94 | - **namespace: string** - if provided, interfaces will be wrapped in a namespace (see below)
95 | ```bash
96 | # usage
97 | json-ts --namespace
98 |
99 | # example
100 | json-ts data/my-file.json --namespace API
101 | ```
102 | - **flow: boolean** - output types in Flow format.
103 | ```bash
104 | # usage
105 | json-ts --flow
106 |
107 | # example
108 | json-ts data/my-file.json --flow
109 | ```
110 | - **prefix: string** - override the `I` prefix on interface names
111 | ```bash
112 | # usage
113 | json-ts --prefix
114 |
115 | # example (remove prefix)
116 | json-ts data/my-file.json --prefix ""
117 | ```
118 | - **rootName: string** - override the `RootObject` name of the top-level interface
119 | ```bash
120 | # usage
121 | json-ts --rootName
122 |
123 | # example
124 | json-ts data/my-file.json --rootName "Product"
125 | ```
126 |
127 | ## TODO:
128 |
129 | ### options
130 |
131 | - [x] Allow choice of `I` prefix on interface names
132 | - [x] Allow naming of RootObject
133 | - [ ] Allow choice of spaces/tabs
134 |
135 | ### Core
136 | - [x] support array types
137 | - [x] support boolean types
138 | - [x] support null types
139 | - [x] output types for Flow via `--flow`
140 | - [x] PascalCase as default for all interface names
141 | - [x] de-dupe interfaces (it's dumb atm, POC)
142 | - [x] de-dupe interfaces where propname differs, but members are the same
143 | - [x] merge interfaces by creating union types for members
144 | - [x] union types for array that contain mixed literal types: `nums: [1, "2"] -> nums: number|string[]`
145 | (already works for complex objects)
146 | - [x] quoted member names when needed
147 | - [x] handle invalid name for interface
148 | - [x] support type alias declarations
149 | - [x] Use Typescript factory methods/printer for output
150 | - [x] Allow wrapping in namespace: eg:
151 | ```ts
152 | declare namespace Projects {
153 | export interface ILoc {
154 | lat: number;
155 | lng: number;
156 | }
157 | ...
158 | }
159 | ```
160 |
161 | ### CLI
162 | - [x] CLI tool to accept stdin (with `--stdin` flag)
163 | - [x] CLI tool to accept json file as input
164 | - [ ] CLI tool to accept URL as input (for validating against remote API)
165 | - [ ] configurable output (filename/stdout etc)
166 |
--------------------------------------------------------------------------------
/src/bin.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import minimist = require('minimist');
3 | import stdin = require('get-stdin');
4 | import {json2ts, json2tsMulti} from './';
5 | import {fromJS, OrderedSet} from 'immutable';
6 | import {join, parse, ParsedPath} from "path";
7 | import {existsSync, readFile, readFileSync} from "fs";
8 | const argv = minimist(process.argv.slice(2));
9 |
10 | // unique input
11 | const inputs = OrderedSet(argv._);
12 |
13 | // defaults
14 | const defaults = {
15 | stdin: false,
16 | namespace: false,
17 | flow: false
18 | };
19 |
20 | // merged options with defaults
21 | const options = {
22 | ...defaults,
23 | ...argv
24 | };
25 |
26 | if (options.stdin) {
27 | stdin().then((str: string) => {
28 | if (str === '') {
29 | console.error('no input provided');
30 | } else {
31 | try {
32 | JSON.parse(str);
33 | console.log(json2ts(str, options));
34 | } catch (e) {
35 | console.error('Invalid JSON');
36 | console.error(e.message);
37 | }
38 | }
39 | })
40 | .catch(err => {
41 | console.error(err);
42 | })
43 | } else {
44 | if (inputs.size === 0) {
45 | console.error('Oops! You provided no inputs');
46 | console.log(`
47 | You can pipe JSON to this program with the --stdin flag:
48 |
49 | curl http://example.com/some-json | json-ts --stdin
50 |
51 | Or, provide path names:
52 |
53 | json-ts path/to/my-file.json
54 | `);
55 | } else {
56 | const queue = inputs
57 | .map(input => {
58 | return {
59 | input,
60 | parsed: parse(input),
61 | };
62 | })
63 | .map(incoming => {
64 | return {
65 | incoming,
66 | resolved: resolveInput(incoming, process.cwd())
67 | }
68 | });
69 |
70 | const withErrors = queue.filter(x => x.resolved.errors.length > 0);
71 | const withoutErrors = queue.filter(x => x.resolved.errors.length === 0);
72 | if (withErrors.size) {
73 | console.log('Sorry, there were errors with your input.');
74 | withErrors.forEach(function (item) {
75 | console.log('');
76 | console.log(` ${item.incoming.input}:`);
77 | console.log(' ', item.resolved.errors[0].error.message);
78 | })
79 | } else {
80 | const strings = withoutErrors.map(item => {
81 | return item.resolved.content;
82 | });
83 | console.log(json2tsMulti((strings as any), options));
84 | }
85 | }
86 | }
87 |
88 | interface IIncomingInput {
89 | input: string,
90 | parsed: ParsedPath,
91 | }
92 | interface InputError {
93 | kind: string,
94 | error: Error
95 | }
96 | interface IResolvedInput {
97 | errors: InputError[],
98 | content?: string
99 | }
100 |
101 | function resolveInput(incoming: IIncomingInput, cwd): IResolvedInput {
102 | const absolute = join(cwd, incoming.parsed.dir, incoming.parsed.base);
103 | if (!existsSync(absolute)) {
104 | return {
105 | errors: [{
106 | kind: 'FileNotFound',
107 | error: new Error(`File not found`)
108 | }]
109 | }
110 | }
111 | const data = readFileSync(absolute, 'utf8');
112 | try {
113 | JSON.parse(data);
114 | return {
115 | errors: [],
116 | content: data
117 | }
118 | } catch (e) {
119 | return {
120 | errors: [{
121 | kind: 'InvalidJson',
122 | error: e
123 | }]
124 | }
125 | }
126 | }
127 | // console.log('options:', options);
128 | // console.log('inputs:', inputs);
129 | // console.log('args', argv);
130 |
--------------------------------------------------------------------------------
/src/collapse-interfaces.ts:
--------------------------------------------------------------------------------
1 | import * as ts from 'typescript';
2 | import {namedProp} from "./transformer";
3 | import {isEmptyArrayType, membersMatch} from "./util";
4 |
5 | export function collapseInterfaces(interfaces: any[]): any[] {
6 |
7 | /**
8 | * {
9 | * 'IItems': {count: 5, names: Set {'pets', 'age'} }
10 | * }
11 | * @type {any}
12 | */
13 | const memberStack = interfaces.reduce((acc, int) => {
14 | const lookup = acc[int.name.text];
15 | if (lookup) {
16 | lookup.count += 1;
17 | int.members.forEach(mem => {
18 | lookup.names.add(mem.name.text);
19 | })
20 | } else {
21 | acc[int.name.text] = {count: 1, names: new Set([])}
22 | }
23 | return acc;
24 | }, {});
25 |
26 | /**
27 | * Look at each interface and mark any members absent in others
28 | * as optional.
29 | */
30 | interfaces.forEach((i) => {
31 | const curName = i.name.text;
32 | const fromStack = memberStack[curName];
33 | if (fromStack.count === 1) {
34 | return;
35 | }
36 | i.members.forEach(localMember => {
37 | const localName = localMember.name.text;
38 | if (!fromStack.names.has(localName)) {
39 | localMember.questionToken = ts.createNode(ts.SyntaxKind.QuestionToken);
40 | }
41 | });
42 | });
43 |
44 | return interfaces.reduce((accInterfaces, current) => {
45 |
46 | const currentName = current.name.text;
47 | const currentMemberNames = new Set(current.members.map(x => (x.name || x.label).text));
48 | const matchingInterfaceIndex = accInterfaces.findIndex(x => (x.name || x.label).text === currentName);
49 |
50 | if (matchingInterfaceIndex === -1) {
51 | return accInterfaces.concat(current);
52 | }
53 |
54 | accInterfaces.forEach((int, index) => {
55 |
56 | if (index !== matchingInterfaceIndex) {
57 | return int;
58 | }
59 |
60 | const prevMemberNames = new Set(int.members.map(x => (x.name || x.label).text));
61 |
62 | // if the current interface has less props than a previous one
63 | // we need to back-track and make the previous one optional
64 | if (currentMemberNames.size < prevMemberNames.size) {
65 | // elements that existed before, but not in the current
66 | int.members.forEach(mem => {
67 | if (!currentMemberNames.has(mem.name.text)) {
68 | mem.questionToken = ts.createNode(ts.SyntaxKind.QuestionToken);
69 | }
70 | });
71 | }
72 |
73 | // Modify members based on missing props, union types etc
74 | modifyMembers(int.members, current.members);
75 | });
76 |
77 | return accInterfaces;
78 |
79 | }, []);
80 | }
81 |
82 | function modifyMembers(interfaceMembers, currentMembers) {
83 | currentMembers.forEach(mem => {
84 |
85 | const existingIndex = interfaceMembers.findIndex(x => x.name.text === mem.name.text);
86 | const existingMember = interfaceMembers[existingIndex];
87 |
88 | // Here, the current member does NOT already exist in this
89 | // interface, so we add it, but as optional
90 | if (!existingMember) {
91 | mem.questionToken = ts.createNode(ts.SyntaxKind.QuestionToken);
92 | interfaceMembers.push(mem);
93 | } else {
94 | // here it exists in both, are the types the same?
95 | // console.log(ts.SyntaxKind[mem.type.kind]);
96 | // console.log(existingMember.kind, mem.kind);
97 | if (membersMatch(existingMember, mem)) {
98 | return;
99 | } else {
100 | const updatedMember = namedProp({name: existingMember.name.text});
101 | // const exists = existingMember.type.types.some(x => x.kind === mem.kind);
102 |
103 | // already a union, so just push a new type
104 | if (existingMember.type.kind === ts.SyntaxKind.UnionType) {
105 | const asSet = new Set(existingMember.type.types.map(x => x.kind));
106 | if (!asSet.has(mem.type.kind)) {
107 | existingMember.type.types.push(mem.type);
108 | interfaceMembers[existingIndex] = existingMember;
109 | }
110 | } else { // not a union yet, so create one for next time around
111 |
112 | // was this previously marked as an empty array? eg: any[]
113 | // if so & the next item is NOT, then we can ignore the any[]
114 | if (isEmptyArrayType(existingMember) && !isEmptyArrayType(mem)) {
115 | updatedMember.type = ts.createNode(ts.SyntaxKind.ArrayType);
116 | updatedMember.type.elementType = mem.type.elementType;
117 | interfaceMembers[existingIndex] = updatedMember;
118 | } else {
119 | // If the INCOMING member type is an empty array, but we already have an array element with items, we bail
120 | if (isEmptyArrayType(mem) && existingMember.type.kind === ts.SyntaxKind.ArrayType && (!isEmptyArrayType(existingMember))) {
121 | return;
122 | }
123 | const memberNodes = [existingMember.type, mem.type];
124 | updatedMember.type = ts.createUnionOrIntersectionTypeNode(ts.SyntaxKind.UnionType, memberNodes);
125 | interfaceMembers[existingIndex] = updatedMember;
126 | }
127 | }
128 | }
129 | }
130 | });
131 | }
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as ts from 'typescript';
2 | import {parse} from "./parser";
3 | import {print, printLiteral} from "./printer";
4 | import {transform} from "./transformer";
5 | import {collapseInterfaces} from "./collapse-interfaces";
6 |
7 | export interface JsonTsOptions {
8 | namespace?: string
9 | flow?: boolean
10 | prefix?: string
11 | rootName?: string
12 | }
13 |
14 | export const defaults = {
15 | prefix: "I",
16 | rootName: "RootObject"
17 | };
18 |
19 | export function json2ts(validJsonString: string, options: JsonTsOptions = {}): string {
20 | const mergedOptions = {
21 | ...defaults,
22 | ...options
23 | };
24 | const {stack, inputKind} = parse(validJsonString, mergedOptions);
25 | switch (inputKind) {
26 | case ts.SyntaxKind.ArrayLiteralExpression:
27 | case ts.SyntaxKind.ObjectLiteralExpression: {
28 | const transformed = transform(stack, mergedOptions);
29 | const flattened = collapseInterfaces(transformed);
30 | const printed = print(flattened, inputKind, mergedOptions);
31 | return printed;
32 | }
33 | default: {
34 | const printed = printLiteral(stack[0], inputKind, mergedOptions);
35 | return printed;
36 | }
37 | }
38 | }
39 |
40 | export function json2tsMulti(validJsonStrings: string[], options: JsonTsOptions = {}): string {
41 | const inputKinds = new Set([]);
42 | const mergedOptions = {
43 | ...defaults,
44 | ...options
45 | };
46 | const joined = validJsonStrings.reduce((all, json) => {
47 | const {stack, inputKind} = parse(json, mergedOptions);
48 | inputKinds.add(inputKind);
49 | const transformed = transform(stack, mergedOptions);
50 | return all.concat(transformed);
51 | }, []);
52 |
53 | if (inputKinds.size > 1) {
54 | // todo handle mixed types
55 | }
56 |
57 | const flattened = collapseInterfaces(joined);
58 | const printed = print(flattened, Array.from(inputKinds)[0], mergedOptions);
59 | return printed;
60 | }
61 |
62 | export {
63 | parse,
64 | print,
65 | transform
66 | }
67 |
68 | declare var window;
69 | if ((typeof window !== 'undefined') && ((typeof window.json2ts) === 'undefined')) {
70 | window.json2ts = json2ts;
71 | window.json2ts.parse = parse;
72 | window.json2ts.transform = transform;
73 | window.json2ts.print = print;
74 | }
75 |
--------------------------------------------------------------------------------
/src/parser.ts:
--------------------------------------------------------------------------------
1 | import * as ts from 'typescript';
2 | import {JsonTsOptions} from "./index";
3 | import needsQuotes = require("needsquotes");
4 |
5 | export interface ParsedNode {
6 | kind: ts.SyntaxKind
7 | _kind: string
8 | name?: string
9 | value?: any
10 | body?: ParsedNode[]
11 | interfaceCandidate?: boolean
12 | }
13 |
14 | function walk(sourceFile: ts.SourceFile, initial = []): ParsedNode[] {
15 |
16 | const stack : Array = initial.slice();
17 | const elementStack : Array = initial.slice();
18 |
19 | function push(element) {
20 | const parent = elementStack[elementStack.length - 1];
21 | const siblings = (parent && parent.body) ? parent.body : stack;
22 | siblings.push(element);
23 | }
24 |
25 | eachProp(sourceFile);
26 |
27 | return stack;
28 |
29 | function addFromArrayElement(incoming) {
30 | switch(incoming.kind) {
31 | case ts.SyntaxKind.NullKeyword:
32 | case ts.SyntaxKind.TrueKeyword:
33 | case ts.SyntaxKind.FalseKeyword:
34 | case ts.SyntaxKind.NumericLiteral:
35 | case ts.SyntaxKind.StringLiteral: {
36 | push(literalTypeFromArrayElement(incoming, incoming.kind));
37 | break;
38 | }
39 | case ts.SyntaxKind.PrefixUnaryExpression: {
40 | push(literalTypeFromArrayElement(incoming, ts.SyntaxKind.NumericLiteral));
41 | break;
42 | }
43 | case ts.SyntaxKind.ObjectLiteralExpression: {
44 | const elem = {
45 | kind: ts.SyntaxKind.ObjectLiteralExpression,
46 | _kind: `ObjectLiteralExpression`,
47 | interfaceCandidate: true,
48 | body: [],
49 | };
50 | push(elem);
51 | elementStack.push(elem);
52 | eachProp(incoming.properties);
53 | elementStack.pop();
54 | break;
55 | }
56 | case ts.SyntaxKind.ArrayLiteralExpression: {
57 | const elem = {
58 | kind: ts.SyntaxKind.ArrayLiteralExpression,
59 | _kind: `ArrayLiteralExpression`,
60 | body: [],
61 | };
62 | push(elem);
63 | elementStack.push(elem);
64 | eachProp(incoming.elements);
65 | elementStack.pop();
66 | break;
67 | }
68 | }
69 | }
70 |
71 | function eachProp(properties) {
72 | properties.forEach(function (prop) {
73 | if (!prop.initializer) {
74 | return addFromArrayElement(prop);
75 | } else {
76 | switch (prop.initializer.kind) {
77 | case ts.SyntaxKind.TrueKeyword:
78 | case ts.SyntaxKind.FalseKeyword:
79 | case ts.SyntaxKind.NullKeyword:
80 | case ts.SyntaxKind.StringLiteral:
81 | case ts.SyntaxKind.NumericLiteral: {
82 | push(literalTypeFromProp(prop, prop.initializer.kind));
83 | break;
84 | }
85 | case ts.SyntaxKind.PrefixUnaryExpression: {
86 | push(literalTypeFromProp(prop, ts.SyntaxKind.NumericLiteral));
87 | break;
88 | }
89 | case ts.SyntaxKind.ObjectLiteralExpression: {
90 | const quotes = needsQuotes(prop.name.text).needsQuotes;
91 | const interfaceCandidate = (quotes === false);
92 |
93 | const elem = {
94 | name: prop.name.text,
95 | body: [],
96 | interfaceCandidate,
97 | kind: ts.SyntaxKind.ObjectLiteralExpression,
98 | _kind: `ObjectLiteralExpression`
99 | };
100 | push(elem);
101 | elementStack.push(elem);
102 | eachProp(prop.initializer.properties);
103 | elementStack.pop();
104 | break;
105 | }
106 | case ts.SyntaxKind.ArrayLiteralExpression: {
107 | const elem = {
108 | name: prop.name.text,
109 | body: [],
110 | kind: ts.SyntaxKind.ArrayLiteralExpression,
111 | _kind: `ArrayLiteralExpression`
112 | };
113 | push(elem);
114 | elementStack.push(elem);
115 | eachProp(prop.initializer.elements);
116 | elementStack.pop();
117 | break;
118 | }
119 | }
120 | }
121 | });
122 | }
123 |
124 | function literalTypeFromProp(prop, kind) {
125 | return {
126 | name: prop.name.text,
127 | value: prop.initializer.text,
128 | kind: kind,
129 | }
130 | }
131 |
132 | function literalTypeFromArrayElement(element, kind) {
133 | return {
134 | kind,
135 | name: element.text,
136 | value: element.text,
137 | }
138 | }
139 | }
140 |
141 | export function parse(string, options: JsonTsOptions): {stack: any[], inputKind: ts.SyntaxKind} {
142 | const input = `const ROOTOBJ = ${string}`;
143 | let stack;
144 | let sourceFile : ts.SourceFile = ts.createSourceFile('json.ts', input, ts.ScriptTarget.ES2015, /*setParentNodes */ true);
145 | // delint it
146 | const _json = sourceFile.statements[0] as any;
147 | const init = _json.declarationList.declarations[0].initializer;
148 |
149 | switch (init.kind) {
150 | case ts.SyntaxKind.TrueKeyword:
151 | case ts.SyntaxKind.FalseKeyword:
152 | case ts.SyntaxKind.NullKeyword:
153 | case ts.SyntaxKind.StringLiteral:
154 | case ts.SyntaxKind.NumericLiteral: {
155 | stack = [{
156 | kind: init.kind,
157 | }];
158 | break;
159 | }
160 | case ts.SyntaxKind.ArrayLiteralExpression: {
161 | stack = walk(init.elements, [{
162 | kind: ts.SyntaxKind.ArrayLiteralExpression,
163 | _kind: `ArrayLiteralExpression`,
164 | name: options.rootName,
165 | body: [],
166 | }]);
167 | break;
168 | }
169 | default: stack = walk(init.properties);
170 | }
171 |
172 | return {stack, inputKind: init.kind};
173 | }
174 |
--------------------------------------------------------------------------------
/src/printer.ts:
--------------------------------------------------------------------------------
1 | import * as ts from 'typescript';
2 | import {InterfaceNode, kindMap, log, MemberNode, namedProp} from "./transformer";
3 | import needsQuotes = require('needsquotes');
4 | import {JsonTsOptions} from "./index";
5 |
6 | export function print(interfaceNodes, inputKind: ts.SyntaxKind, options: JsonTsOptions): string {
7 |
8 | const result = (ts.createSourceFile as any)('module', '');
9 |
10 | const printer = ts.createPrinter({
11 | newLine: ts.NewLineKind.LineFeed,
12 | });
13 |
14 | if (inputKind === ts.SyntaxKind.ArrayLiteralExpression) {
15 | const first = interfaceNodes[0];
16 | const newNode : any = ts.createNode(ts.SyntaxKind.TypeAliasDeclaration);
17 | newNode.type = first.members[0].type;
18 | newNode.name = ts.createIdentifier(`${options.prefix}${options.rootName}`);
19 | interfaceNodes[0] = newNode;
20 | }
21 |
22 | if (options.flow) {
23 | const modified = interfaceNodes.map(x => {
24 | const newNode : any = ts.createNode(ts.SyntaxKind.TypeAliasDeclaration);
25 | newNode.modifiers = [ts.createToken(ts.SyntaxKind.ExportKeyword)];
26 | newNode.type = ts.createTypeLiteralNode(x.members);
27 | newNode.name = x.name;
28 | return newNode;
29 | });
30 |
31 | const items = modified.map(x => {
32 | return printer.printNode(ts.EmitHint.Unspecified, x, result);
33 | }).join('\n') + '\n';
34 |
35 | return ['// @flow', items].join('\n');
36 | }
37 |
38 | if (options.namespace) {
39 | interfaceNodes.forEach(x => {
40 | x.modifiers = [ts.createToken(ts.SyntaxKind.ExportKeyword)];
41 | });
42 | const ns = ts.createModuleDeclaration(
43 | undefined,
44 | [ts.createToken(ts.SyntaxKind.DeclareKeyword)],
45 | ts.createIdentifier(options.namespace),
46 | ts.createModuleBlock(interfaceNodes),
47 | ts.NodeFlags.Namespace
48 | );
49 | return printer.printNode(ts.EmitHint.Unspecified, ns, result) + '\n';
50 | }
51 |
52 | return interfaceNodes.map(x => {
53 | return printer.printNode(ts.EmitHint.Unspecified, x, result);
54 | })
55 | .join('\n') + '\n';
56 | }
57 |
58 | export function printLiteral(node, kind, options) {
59 | const result = (ts.createSourceFile as any)('module', '');
60 |
61 | const printer = ts.createPrinter({
62 | newLine: ts.NewLineKind.LineFeed,
63 | });
64 |
65 | const newNode : any = ts.createNode(ts.SyntaxKind.TypeAliasDeclaration);
66 | newNode.type = ts.createNode(kindMap[kind]);
67 | newNode.name = ts.createIdentifier(`${options.prefix}${options.rootName}`);
68 |
69 | return printer.printNode(ts.EmitHint.Unspecified, newNode, result);
70 | }
71 |
72 | function wrapper(blocks, options) {
73 | if (options.namespace) {
74 | const lines = [
75 | `declare namespace ${options.namespace} {`,
76 | ...blocks.split('\n').map(x => ` ${x}`),
77 | `}`,
78 | ];
79 | return lines.join('\n');
80 | }
81 | return blocks;
82 | }
83 |
--------------------------------------------------------------------------------
/src/transformer.ts:
--------------------------------------------------------------------------------
1 | import * as ts from 'typescript';
2 | import {ParsedNode} from "./parser";
3 | import {Set as ImmutableSet} from "immutable";
4 | import needsQuotes = require('needsquotes');
5 | import {JsonTsOptions} from "./index";
6 | import {collapseInterfaces} from "./collapse-interfaces";
7 | import {Node} from "typescript";
8 |
9 | const {startCase, toLower} = require('../_');
10 |
11 | export const log = (input) => console.log('--\n', JSON.stringify(input, null, 2));
12 |
13 | export interface MemberNode {
14 | types: ImmutableSet
15 | members: MemberNode[]
16 | name: string
17 | optional: boolean
18 | }
19 |
20 | export interface InterfaceNode {
21 | name: string;
22 | original: string;
23 | members: MemberNode[];
24 | }
25 |
26 | export const kindMap = {
27 | [ts.SyntaxKind.NullKeyword]: ts.SyntaxKind.NullKeyword,
28 | [ts.SyntaxKind.StringLiteral]: ts.SyntaxKind.StringKeyword,
29 | [ts.SyntaxKind.FirstLiteralToken]: ts.SyntaxKind.NumberKeyword,
30 | [ts.SyntaxKind.TrueKeyword]: ts.SyntaxKind.BooleanKeyword,
31 | [ts.SyntaxKind.FalseKeyword]: ts.SyntaxKind.BooleanKeyword,
32 | [ts.SyntaxKind.NumericLiteral]: ts.SyntaxKind.NumberKeyword,
33 | };
34 |
35 | export function namedProp(member) {
36 | const qs = needsQuotes(member.name);
37 |
38 | const output = qs.needsQuotes ? qs.quotedValue : member.name;
39 |
40 | const prop: any = ts.createNode(ts.SyntaxKind.PropertySignature);
41 | prop.name = ts.createIdentifier(output);
42 |
43 | if (member.optional) {
44 | prop.questionToken = ts.createNode(ts.SyntaxKind.QuestionToken);
45 | }
46 |
47 | return prop;
48 | }
49 |
50 | const safeUnions = ImmutableSet([
51 | ts.SyntaxKind.TrueKeyword,
52 | ts.SyntaxKind.FalseKeyword,
53 | ts.SyntaxKind.StringLiteral,
54 | ts.SyntaxKind.NumericLiteral,
55 | ts.SyntaxKind.PrefixUnaryExpression,
56 | ts.SyntaxKind.NullKeyword,
57 | ]);
58 |
59 | export function transform(stack: ParsedNode[], options: JsonTsOptions): InterfaceNode[] {
60 |
61 | const wrapper = [{
62 | kind: ts.SyntaxKind.ObjectLiteralExpression,
63 | _kind: 'ObjectLiteralExpression',
64 | name: options.rootName,
65 | interfaceCandidate: true,
66 | body: stack
67 | }];
68 |
69 | const interfaces = getInterfaces(wrapper);
70 |
71 | return collapseInterfaces(interfaces);
72 |
73 | function createOne(node: ParsedNode): InterfaceNode {
74 |
75 | const thisMembers = getMembers(node.body);
76 | const item: any = ts.createNode(ts.SyntaxKind.InterfaceDeclaration);
77 | item.name = ts.createIdentifier(newInterfaceName(node));
78 | item.members = ts.createNodeArray(thisMembers, false);
79 |
80 | return item;
81 | }
82 |
83 | function getInterfaces(nodes: ParsedNode[]): any[] {
84 | return nodes.reduce((acc, node) => {
85 |
86 | if (node.kind === ts.SyntaxKind.ObjectLiteralExpression) {
87 |
88 | const newInterface = createOne(node);
89 | // const asMap = fromJS(newInterface);
90 |
91 | if (node.interfaceCandidate) {
92 | return acc.concat([newInterface], getInterfaces(node.body));
93 | }
94 |
95 | return acc.concat(getInterfaces(node.body));
96 | }
97 |
98 | if (node.kind === ts.SyntaxKind.ArrayLiteralExpression) {
99 |
100 | const decorated = node.body.map(arrayNode => {
101 | arrayNode.name = getArrayItemName(node.name);
102 | return arrayNode;
103 | });
104 |
105 | const other = getInterfaces(decorated);
106 |
107 | return acc.concat(other);
108 | }
109 |
110 | return acc;
111 |
112 | }, []);
113 | }
114 |
115 | function getMembers(stack: ParsedNode[]) {
116 | const members = stack.map(node => {
117 | switch(node.kind) {
118 | case ts.SyntaxKind.FalseKeyword:
119 | case ts.SyntaxKind.TrueKeyword: {
120 | const item = namedProp({name: node.name});
121 | item.type = ts.createNode(ts.SyntaxKind.BooleanKeyword);
122 | return item;
123 | }
124 | case ts.SyntaxKind.StringLiteral: {
125 | const item = namedProp({name: node.name});
126 | item.type = ts.createNode(ts.SyntaxKind.StringKeyword);
127 | return item;
128 | }
129 | case ts.SyntaxKind.NullKeyword: {
130 | const item = namedProp({name: node.name});
131 | item.type = ts.createNode(ts.SyntaxKind.NullKeyword);
132 | return item;
133 | }
134 | case ts.SyntaxKind.NumericLiteral: {
135 | const item = namedProp({name: node.name});
136 | item.type = ts.createNode(ts.SyntaxKind.NumberKeyword);
137 | return item;
138 | }
139 | case ts.SyntaxKind.ObjectLiteralExpression: {
140 | if (node.interfaceCandidate) {
141 | const item = namedProp({name: node.name});
142 | item.type = ts.createTypeReferenceNode(newInterfaceName(node), undefined);
143 | return item;
144 | } else {
145 | const item = namedProp({name: node.name});
146 | item.type = ts.createTypeLiteralNode(getMembers(node.body));
147 | return item;
148 | }
149 | }
150 | case ts.SyntaxKind.ArrayLiteralExpression: {
151 | if (node.body.length) {
152 | const item = namedProp({name: node.name});
153 | const elements = getArrayElementsType(node);
154 | item.type = ts.createArrayTypeNode(elements);
155 | return item;
156 | } else {
157 | const item = namedProp({name: node.name});
158 | const anyNode: any = ts.createNode(ts.SyntaxKind.AnyKeyword);
159 | item.type = ts.createArrayTypeNode(anyNode);
160 | return item;
161 | }
162 | }
163 | }
164 | });
165 | return members
166 | }
167 |
168 | function getArrayElementsType(node: ParsedNode): any {
169 | const kinds = ImmutableSet(node.body.map(x => x.kind));
170 | if (kinds.size === 1) { // if there's only 1 kind in the array, it's safe to use type[];
171 | const kind = kinds.first();
172 | switch(kind) {
173 | case ts.SyntaxKind.NullKeyword:
174 | case ts.SyntaxKind.StringLiteral:
175 | case ts.SyntaxKind.TrueKeyword:
176 | case ts.SyntaxKind.FalseKeyword:
177 | case ts.SyntaxKind.NumericLiteral:
178 | return ts.createNode(kindMap[kind]);
179 | case ts.SyntaxKind.ObjectLiteralExpression:
180 | const item = ts.createTypeReferenceNode(getArrayInterfaceItemName(node.name), undefined);
181 | return item;
182 | default: return ts.createNode(ts.SyntaxKind.AnyKeyword);
183 | }
184 | } else if (kinds.size === 2) { // a mix of true/false is still a boolean[];
185 | if (kinds.has(ts.SyntaxKind.TrueKeyword) && kinds.has(ts.SyntaxKind.FalseKeyword)) {
186 | return ts.createNode(ts.SyntaxKind.BooleanKeyword);
187 | }
188 | }
189 | // console.log(node.body);
190 | if (kinds.every(kind => safeUnions.has(kind))) {
191 |
192 | // console.log(node.body);
193 | const types = kinds.map(x => {
194 | return ts.createNode(kindMap[x]);
195 | }).toJS();
196 |
197 | const item = ts.createNode(ts.SyntaxKind.ParenthesizedType);
198 | (item as any).type = ts.createUnionOrIntersectionTypeNode(ts.SyntaxKind.UnionType, types);
199 |
200 | return item;
201 | } else {
202 | // console.log('Not creating union as this array contains mixed complexr types');
203 | }
204 |
205 | return ts.createNode(ts.SyntaxKind.AnyKeyword);
206 | }
207 | function newInterfaceName(node: ParsedNode) {
208 | const base = node.name[0].toUpperCase() + node.name.slice(1);
209 | if (options.prefix) {
210 | return options.prefix + base;
211 | }
212 | const qs = needsQuotes(base);
213 | if (qs.needsQuotes) {
214 | return `_` + base;
215 | }
216 | return base;
217 | }
218 | function upper(string) {
219 | return string[0].toUpperCase() + string.slice(1);
220 | }
221 | function pascalCase(input): string {
222 | return startCase(input).replace(/ /g, '');
223 | }
224 | function getArrayInterfaceItemName(input): string {
225 | if (options.prefix) {
226 | return pascalCase(`${options.prefix}_${input}_Item`);
227 | }
228 | const qs = needsQuotes(input);
229 | if (qs.needsQuotes) {
230 | return '_' + pascalCase(`${input}_Item`);
231 | }
232 | return pascalCase(`${input}_Item`)
233 | }
234 | function getArrayItemName(input) {
235 | return pascalCase(`${input}_Item`)
236 | }
237 | // function getArrayInterfaceItemName(input) {
238 | // return pascalCase(`I_${input}_Item`)
239 | // }
240 | }
241 |
242 |
243 |
244 |
--------------------------------------------------------------------------------
/src/util.ts:
--------------------------------------------------------------------------------
1 | import * as ts from "typescript";
2 |
3 | export function membersMatch(first, second) {
4 | if (first.kind !== second.kind) {
5 | return false;
6 | }
7 | if (first.name.text !== second.name.text) {
8 | return false;
9 | }
10 | if (first.type.kind !== second.type.kind) {
11 | return false;
12 | }
13 | if (first.type.kind === ts.SyntaxKind.ArrayType && second.type.kind === ts.SyntaxKind.ArrayType) {
14 | if (first.type.elementType.kind !== second.type.elementType.kind) {
15 | return false;
16 | }
17 | }
18 | return true;
19 | }
20 |
21 | export function isEmptyArrayType(member) {
22 | if (member.type.kind === ts.SyntaxKind.ArrayType) {
23 | if (member.type.elementType.kind === ts.SyntaxKind.AnyKeyword) {
24 | return true;
25 | }
26 | }
27 | return false;
28 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
5 | "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */
6 | "lib": ["es2015"],
7 | /* Specify library files to be included in the compilation: */
8 | // "allowJs": true, /* Allow javascript files to be compiled. */
9 | // "checkJs": true, /* Report errors in .js files. */
10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
11 | "declaration": true, /* Generates corresponding '.d.ts' file. */
12 | // "sourceMap": true, /* Generates corresponding '.map' file. */
13 | "outDir": "./dist", /* Redirect output structure to the directory. */
14 | // "removeComments": true, /* Do not emit comments to output. */
15 | // "noEmit": true, /* Do not emit outputs. */
16 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
17 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
18 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
19 |
20 | /* Strict Type-Checking Options */
21 | // "strict": false, /* Enable all strict type-checking options. */
22 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
23 | // "strictNullChecks": true, /* Enable strict null checks. */
24 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
25 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
26 |
27 | /* Additional Checks */
28 | // "noUnusedLocals": true, /* Report errors on unused locals. */
29 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
30 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
31 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
32 |
33 | /* Module Resolution Options */
34 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
35 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
36 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
37 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
38 | // "typeRoots": [], /* List of folders to include type definitions from. */
39 | // "types": [], /* Type declaration files to be included in compilation. */
40 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
41 |
42 | /* Source Map Options */
43 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
44 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
45 | "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
46 | "inlineSources": true /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
47 |
48 | /* Experimental Options */
49 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
50 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
51 | },
52 | "include": [
53 | "src/*.ts"
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------