Source: utils/dump.js

/**
 * This file is part of SesaJsTools.
 *   Copyright 2014-2015, Association Sésamath
 *
 * SesaJsTools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation.
 *
 * SesaJsTools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with SesaJsTools (LICENCE.txt).
 * @see http://www.gnu.org/licenses/agpl.txt
 *
 *
 * Ce fichier fait partie de l'application Sésathèque, créée par l'association Sésamath.
 *
 * Sésathèque est un logiciel libre ; vous pouvez le redistribuer ou le modifier suivant
 * les termes de la GNU Affero General Public License version 3 telle que publiée par la
 * Free Software Foundation.
 * Sésathèque est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE,
 * sans même la garantie tacite de QUALITÉ MARCHANDE ou d'ADÉQUATION à UN BUT PARTICULIER.
 * Consultez la GNU Affero General Public License pour plus de détails.
 * Vous devez avoir reçu une copie de la GNU General Public License en même temps que Sésathèque
 * (cf LICENCE.txt et http://vvlibri.org/fr/Analyse/gnu-affero-general-public-license-v3-analyse
 * pour une explication en français)
 */

'use strict'

var tools = require('../index')
var hasProp = tools.hasProp

/**
 * dump un objet de manière plus complète qu'un stringify
 * @param obj
 * @param indent
 * @returns {*}
 */
module.exports = function dump (obj, indent) {
  // on traite déjà null
  if (obj === null) return 'null'
  // puis les types natifs basiques
  var type = typeof obj
  switch (type) {
    case 'undefined':
      return obj
    case 'number':
    case 'boolean':
      return String(obj)
    case 'string':
      return '"' + obj.replace('"', '\\"') + '"'
    case 'symbol':
      return '[Symbol ' + obj.toString() + ']'

    default:
      if (!indent) indent = 0
      // decalage de base
      var prefix = (' ').repeat(indent)
      indent += 2
      var innerPrefix = prefix + '  '
      var pile = []
      var buffer
      // object et symbol
      // on regarde d'abord si on a une méthode toSource toute prête qui existe sur cet objet
      // (sauf pour les date qui posent pb sous node)
      if (tools.isDate(obj)) {
        return JSON.stringify(obj)
      } else if (tools.isArray(obj)) {
        buffer = '[\n'
        obj.forEach(function (elt, index) {
          buffer += innerPrefix + dump(elt, indent) + (index === obj.length - 1 ? '\n' : ',\n')
        })
        if (obj.length) buffer += prefix + ']'
        else buffer += ']'
        return buffer
      } else if (tools.isRegExp(obj) || tools.isFunction(obj)) {
        // dans un navigateur toSource existe pour eux, mais pas dans node
        return (typeof obj.toSource === 'function') ? obj.toSource() : obj.toString()
      } else if (tools.isObject(obj)) {
        var nbp = 0
        for (var p in obj) {
          if (hasProp(obj, p)) {
            nbp++
            // pour détecter les refs circulaires, on regarde si JSON.stringify plante,
            // pas génial mais évite de lancer une récursion infinie, à pas si cher (et dump est vraiment rarement utilisé en prod)
            try {
              JSON.stringify(obj[p])
              pile.push(p + ': ' + dump(obj[p], indent))
            } catch (error) {
              pile.push(p + ': "' + error.toString().replace('"', '\\"') + '"')
            }
          }
        }
        if (nbp) return '{\n' + innerPrefix + pile.join(',\n' + innerPrefix) + '\n' + prefix + '}'
        else if (tools.isObjectPlain(obj)) return prefix + '{}'
        else return prefix + obj.toString()
      } else {
        console.error('dump est tombé sur un truc qui ne ressemble à rien de connu', obj)
        return tools.stringify(obj, 2)
      }
  }
}