stringifyAll.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. var escapeJsStr = require('./escapeJsStr');
  2. var type = require('./type');
  3. var toStr = require('./toStr');
  4. var endWith = require('./endWith');
  5. var toSrc = require('./toSrc');
  6. var keys = require('./keys');
  7. var each = require('./each');
  8. var Class = require('./Class');
  9. var getProto = require('./getProto');
  10. var difference = require('./difference');
  11. var extend = require('./extend');
  12. var isPromise = require('./isPromise');
  13. var filter = require('./filter');
  14. var now = require('./now');
  15. var allKeys = require('./allKeys');
  16. var contain = require('./contain');
  17. var isObj = require('./isObj');
  18. var isMiniProgram = require('./isMiniProgram');
  19. var create = require('./create');
  20. var startWith = require('./startWith');
  21. var safeSet = require('./safeSet');
  22. var defineProp = require('./defineProp');
  23. var pick = require('./pick');
  24. var isArrLike = require('./isArrLike');
  25. exports = function(obj) {
  26. var _ref =
  27. arguments.length > 1 && arguments[1] !== undefined
  28. ? arguments[1]
  29. : {},
  30. self = _ref.self,
  31. _ref$startTime = _ref.startTime,
  32. startTime = _ref$startTime === void 0 ? now() : _ref$startTime,
  33. _ref$timeout = _ref.timeout,
  34. timeout = _ref$timeout === void 0 ? 0 : _ref$timeout,
  35. _ref$depth = _ref.depth,
  36. depth = _ref$depth === void 0 ? 0 : _ref$depth,
  37. _ref$curDepth = _ref.curDepth,
  38. curDepth = _ref$curDepth === void 0 ? 1 : _ref$curDepth,
  39. _ref$visitor = _ref.visitor,
  40. visitor = _ref$visitor === void 0 ? new Visitor() : _ref$visitor,
  41. _ref$unenumerable = _ref.unenumerable,
  42. unenumerable = _ref$unenumerable === void 0 ? false : _ref$unenumerable,
  43. _ref$symbol = _ref.symbol,
  44. symbol = _ref$symbol === void 0 ? false : _ref$symbol,
  45. _ref$accessGetter = _ref.accessGetter,
  46. accessGetter = _ref$accessGetter === void 0 ? false : _ref$accessGetter,
  47. _ref$ignore = _ref.ignore,
  48. ignore = _ref$ignore === void 0 ? [] : _ref$ignore;
  49. var json = '';
  50. var options = {
  51. visitor: visitor,
  52. unenumerable: unenumerable,
  53. symbol: symbol,
  54. accessGetter: accessGetter,
  55. depth: depth,
  56. curDepth: curDepth + 1,
  57. timeout: timeout,
  58. startTime: startTime,
  59. ignore: ignore
  60. };
  61. var t = type(obj, false);
  62. if (t === 'String') {
  63. json = wrapStr(obj);
  64. } else if (t === 'Number') {
  65. json = toStr(obj);
  66. if (endWith(json, 'Infinity')) {
  67. json = '{"value":"'.concat(json, '","type":"Number"}');
  68. }
  69. } else if (t === 'NaN') {
  70. json = '{"value":"NaN","type":"Number"}';
  71. } else if (t === 'Boolean') {
  72. json = obj ? 'true' : 'false';
  73. } else if (t === 'Null') {
  74. json = 'null';
  75. } else if (t === 'Undefined') {
  76. json = '{"type":"Undefined"}';
  77. } else if (t === 'Symbol') {
  78. var val = 'Symbol';
  79. try {
  80. val = toStr(obj);
  81. } catch (e) {}
  82. json = '{"value":'.concat(wrapStr(val), ',"type":"Symbol"}');
  83. } else {
  84. if (timeout && now() - startTime > timeout) {
  85. return wrapStr('Timeout');
  86. }
  87. if (depth && curDepth > depth) {
  88. return wrapStr('{...}');
  89. }
  90. json = '{';
  91. var parts = [];
  92. var visitedObj = visitor.get(obj);
  93. var id;
  94. if (visitedObj) {
  95. id = visitedObj.id;
  96. parts.push('"reference":'.concat(id));
  97. } else {
  98. id = visitor.set(obj);
  99. parts.push('"id":'.concat(id));
  100. }
  101. parts.push('"type":"'.concat(t, '"'));
  102. if (endWith(t, 'Function')) {
  103. parts.push('"value":'.concat(wrapStr(toSrc(obj))));
  104. } else if (t === 'RegExp') {
  105. parts.push('"value":'.concat(wrapStr(obj)));
  106. }
  107. if (!visitedObj) {
  108. var enumerableKeys = keys(obj);
  109. if (enumerableKeys.length) {
  110. parts.push(
  111. iterateObj(
  112. 'enumerable',
  113. enumerableKeys,
  114. self || obj,
  115. options
  116. )
  117. );
  118. }
  119. if (unenumerable) {
  120. var unenumerableKeys = difference(
  121. allKeys(obj, {
  122. prototype: false,
  123. unenumerable: true
  124. }),
  125. enumerableKeys
  126. );
  127. if (unenumerableKeys.length) {
  128. parts.push(
  129. iterateObj(
  130. 'unenumerable',
  131. unenumerableKeys,
  132. self || obj,
  133. options
  134. )
  135. );
  136. }
  137. }
  138. if (symbol) {
  139. var symbolKeys = filter(
  140. allKeys(obj, {
  141. prototype: false,
  142. symbol: true
  143. }),
  144. function(key) {
  145. return typeof key === 'symbol';
  146. }
  147. );
  148. if (symbolKeys.length) {
  149. parts.push(
  150. iterateObj('symbol', symbolKeys, self || obj, options)
  151. );
  152. }
  153. }
  154. var prototype = getProto(obj);
  155. if (prototype && !contain(ignore, prototype)) {
  156. var proto = '"proto":'.concat(
  157. exports(
  158. prototype,
  159. extend(options, {
  160. self: self || obj
  161. })
  162. )
  163. );
  164. parts.push(proto);
  165. }
  166. }
  167. json += parts.join(',') + '}';
  168. }
  169. return json;
  170. };
  171. function iterateObj(name, keys, obj, options) {
  172. var parts = [];
  173. each(keys, function(key) {
  174. var val;
  175. var descriptor = Object.getOwnPropertyDescriptor(obj, key);
  176. var hasGetter = descriptor && descriptor.get;
  177. var hasSetter = descriptor && descriptor.set;
  178. if (!options.accessGetter && hasGetter) {
  179. val = '(...)';
  180. } else {
  181. try {
  182. val = obj[key];
  183. if (contain(options.ignore, val)) {
  184. return;
  185. }
  186. if (isPromise(val)) {
  187. val.catch(function() {});
  188. }
  189. } catch (e) {
  190. val = e.message;
  191. }
  192. }
  193. parts.push(''.concat(wrapKey(key), ':').concat(exports(val, options)));
  194. if (hasGetter) {
  195. parts.push(
  196. ''
  197. .concat(wrapKey('get ' + toStr(key)), ':')
  198. .concat(exports(descriptor.get, options))
  199. );
  200. }
  201. if (hasSetter) {
  202. parts.push(
  203. ''
  204. .concat(wrapKey('set ' + toStr(key)), ':')
  205. .concat(exports(descriptor.set, options))
  206. );
  207. }
  208. });
  209. return '"'.concat(name, '":{') + parts.join(',') + '}';
  210. }
  211. function wrapKey(key) {
  212. return '"'.concat(escapeJsonStr(key), '"');
  213. }
  214. function wrapStr(str) {
  215. return '"'.concat(escapeJsonStr(toStr(str)), '"');
  216. }
  217. function escapeJsonStr(str) {
  218. return escapeJsStr(str)
  219. .replace(/\\'/g, "'")
  220. .replace(/\t/g, '\\t');
  221. }
  222. var Visitor = Class({
  223. initialize: function() {
  224. this.id = 1;
  225. this.visited = [];
  226. },
  227. set: function(val) {
  228. var visited = this.visited,
  229. id = this.id;
  230. var obj = {
  231. id: id,
  232. val: val
  233. };
  234. visited.push(obj);
  235. this.id++;
  236. return id;
  237. },
  238. get: function(val) {
  239. var visited = this.visited;
  240. for (var i = 0, len = visited.length; i < len; i++) {
  241. var obj = visited[i];
  242. if (val === obj.val) return obj;
  243. }
  244. return false;
  245. }
  246. });
  247. exports.parse = function(str) {
  248. var map = {};
  249. var obj = parse(JSON.parse(str), {
  250. map: map
  251. });
  252. correctReference(map);
  253. return obj;
  254. };
  255. function correctReference(map) {
  256. each(map, function(obj) {
  257. var enumerableKeys = keys(obj);
  258. for (var i = 0, len = enumerableKeys.length; i < len; i++) {
  259. var key = enumerableKeys[i];
  260. if (isObj(obj[key])) {
  261. var reference = obj[key].reference;
  262. if (reference && map[reference]) {
  263. obj[key] = map[reference];
  264. }
  265. }
  266. }
  267. var proto = getProto(obj);
  268. if (proto && proto.reference) {
  269. if (map[proto.reference]) {
  270. Object.setPrototypeOf(obj, map[proto.reference]);
  271. }
  272. }
  273. });
  274. }
  275. function parse(obj, options) {
  276. var map = options.map;
  277. if (!isObj(obj)) {
  278. return obj;
  279. }
  280. var id = obj.id,
  281. type = obj.type,
  282. value = obj.value,
  283. proto = obj.proto,
  284. reference = obj.reference;
  285. var enumerable = obj.enumerable,
  286. unenumerable = obj.unenumerable;
  287. if (reference) {
  288. return obj;
  289. }
  290. if (type === 'Number') {
  291. if (value === 'Infinity') {
  292. return Number.POSITIVE_INFINITY;
  293. } else if (value === '-Infinity') {
  294. return Number.NEGATIVE_INFINITY;
  295. }
  296. return NaN;
  297. } else if (type === 'Undefined') {
  298. return undefined;
  299. }
  300. var newObj;
  301. if (type === 'Function') {
  302. newObj = function() {};
  303. newObj.toString = function() {
  304. return value;
  305. };
  306. if (proto) {
  307. Object.setPrototypeOf(newObj, parse(proto, options));
  308. }
  309. } else if (type === 'RegExp') {
  310. newObj = strToRegExp(value);
  311. } else {
  312. if (type !== 'Object') {
  313. var Fn;
  314. if (!isMiniProgram) {
  315. Fn = new Function(type, '');
  316. } else {
  317. Fn = function() {};
  318. }
  319. if (proto) {
  320. Fn.prototype = parse(proto, options);
  321. }
  322. newObj = new Fn();
  323. } else {
  324. if (proto) {
  325. newObj = create(parse(proto, options));
  326. } else {
  327. newObj = create(null);
  328. }
  329. }
  330. }
  331. var defineProps = {};
  332. if (enumerable) {
  333. var len;
  334. if (isArrLike(enumerable)) {
  335. len = enumerable.length;
  336. delete enumerable.length;
  337. }
  338. enumerable = pick(enumerable, function(value, key) {
  339. return !handleGetterSetter(enumerable, value, key);
  340. });
  341. each(enumerable, function(value, key) {
  342. var defineProp = defineProps[key] || {};
  343. if (!defineProp.get) {
  344. newObj[key] = parse(value, options);
  345. }
  346. });
  347. if (len) {
  348. newObj.length = len;
  349. }
  350. }
  351. if (unenumerable) {
  352. unenumerable = pick(unenumerable, function(value, key) {
  353. return !handleGetterSetter(unenumerable, value, key);
  354. });
  355. each(unenumerable, function(value, key) {
  356. var defineProp = defineProps[key] || {};
  357. if (!defineProp.get) {
  358. value = parse(value, options);
  359. if (isObj(value) && value.reference) {
  360. var _reference = value.reference;
  361. value = function() {
  362. return map[_reference];
  363. };
  364. defineProp.get = value;
  365. } else {
  366. defineProp.value = value;
  367. }
  368. }
  369. defineProp.enumerable = false;
  370. defineProps[key] = defineProp;
  371. });
  372. }
  373. defineProp(newObj, defineProps);
  374. function handleGetterSetter(obj, val, key) {
  375. key = toStr(key);
  376. var isGetterAndSetter = false;
  377. each(['get', 'set'], function(type) {
  378. if (startWith(key, type + ' ')) {
  379. var realKey = key.replace(type + ' ', '');
  380. if (obj[realKey]) {
  381. val = parse(val, options);
  382. if (val === 'Timeout') {
  383. val = retTimeout;
  384. }
  385. safeSet(defineProps, [realKey, type], val);
  386. isGetterAndSetter = true;
  387. }
  388. }
  389. });
  390. return isGetterAndSetter;
  391. }
  392. map[id] = newObj;
  393. return newObj;
  394. }
  395. function retTimeout() {
  396. return 'Timeout';
  397. }
  398. function strToRegExp(str) {
  399. var lastSlash = str.lastIndexOf('/');
  400. return new RegExp(str.slice(1, lastSlash), str.slice(lastSlash + 1));
  401. }
  402. module.exports = exports;