LCOV - code coverage report
Current view: top level - modules/esm - console.js (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 84.1 % 239 201
Test Date: 2024-04-16 04:37:39 Functions: 63.2 % 38 24
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 67.7 % 192 130

             Branch data     Line data    Source code
       1                 :         240 : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
       2                 :             : // SPDX-FileCopyrightText: 2021 Evan Welsh <contact@evanwelsh.com>
       3                 :             : 
       4                 :          48 : const DEFAULT_LOG_DOMAIN = 'Gjs-Console';
       5                 :             : 
       6                 :             : // A line-by-line implementation of https://console.spec.whatwg.org/.
       7                 :             : 
       8                 :             : // 2.2.1. Formatting specifiers
       9                 :             : // https://console.spec.whatwg.org/#formatting-specifiers
      10                 :             : //
      11                 :             : // %s - string
      12                 :             : // %d or %i - integer
      13                 :             : // %f - float
      14                 :             : // %o - "optimal" object formatting
      15                 :             : // %O - "generic" object formatting
      16                 :             : // %c - "CSS" formatting (unimplemented by GJS)
      17                 :             : 
      18                 :             : /**
      19                 :             :  * A simple regex to capture formatting specifiers
      20                 :             :  */
      21                 :          48 : const specifierTest = /%(d|i|s|f|o|O|c)/;
      22                 :             : 
      23                 :             : /**
      24                 :             :  * @param {string} str a string to check for format specifiers like %s or %i
      25                 :             :  * @returns {boolean}
      26                 :             :  */
      27                 :         100 : function hasFormatSpecifiers(str) {
      28                 :         100 :     return specifierTest.test(str);
      29                 :             : }
      30                 :             : 
      31                 :             : /**
      32                 :             :  * @param {any} item an item to format
      33                 :             :  */
      34                 :           5 : function formatGenerically(item) {
      35                 :           5 :     return JSON.stringify(item, null, 4);
      36                 :             : }
      37                 :             : 
      38                 :             : /**
      39                 :             :  * @param {any} item an item to format
      40                 :             :  * @returns {string}
      41                 :             :  */
      42                 :          11 : function formatOptimally(item) {
      43                 :          11 :     const GLib = imports.gi.GLib;
      44                 :             :     // Handle optimal error formatting.
      45 [ +  + ][ +  + ]:          11 :     if (item instanceof Error || item instanceof GLib.Error) {
      46 [ -  + ][ -  + ]:           5 :         return `${item.toString()}${item.stack ? '\n' : ''}${item.stack
      47                 :           5 :             ?.split('\n')
      48                 :             :             // Pad each stacktrace line.
      49                 :          55 :             .map(line => line.padStart(2, ' '))
      50                 :           5 :             .join('\n')}`;
      51                 :             :     }
      52                 :             : 
      53                 :             :     // TODO: Enhance 'optimal' formatting.
      54                 :             :     // There is a current work on a better object formatter for GJS in
      55                 :             :     // https://gitlab.gnome.org/GNOME/gjs/-/merge_requests/587
      56 [ -  + ][ -  + ]:           6 :     if (typeof item === 'object' && item !== null) {
      57 [ +  + ][ +  + ]:           6 :         if (item.constructor?.name !== 'Object')
      58         [ +  + ]:           3 :             return `${item.constructor?.name} ${JSON.stringify(item, null, 4)}`;
      59         [ +  + ]:           3 :         else if (item[Symbol.toStringTag] === 'GIRepositoryNamespace')
      60                 :           1 :             return `[${item[Symbol.toStringTag]} ${item.__name__}]`;
      61                 :             :     }
      62                 :           2 :     return JSON.stringify(item, null, 4);
      63                 :          11 : }
      64                 :             : 
      65                 :             : /**
      66                 :             :  * Implementation of the WHATWG Console object.
      67                 :             :  */
      68                 :          96 : class Console {
      69                 :          48 :     #groupIndentation = '';
      70                 :          48 :     #countLabels = {};
      71                 :          48 :     #timeLabels = {};
      72                 :          48 :     #logDomain = DEFAULT_LOG_DOMAIN;
      73                 :             : 
      74                 :          48 :     get [Symbol.toStringTag]() {
      75                 :           0 :         return 'Console';
      76                 :             :     }
      77                 :             : 
      78                 :             :     // 1.1 Logging functions
      79                 :             :     // https://console.spec.whatwg.org/#logging
      80                 :             : 
      81                 :             :     /**
      82                 :             :      * Logs a critical message if the condition is not truthy.
      83                 :             :      * {@see console.error()} for additional information.
      84                 :             :      *
      85                 :             :      * @param {boolean} condition a boolean condition which, if false, causes
      86                 :             :      *   the log to print
      87                 :             :      * @param  {...any} data formatting substitutions, if applicable
      88                 :             :      * @returns {void}
      89                 :             :      */
      90                 :          51 :     assert(condition, ...data) {
      91         [ +  + ]:           3 :         if (condition)
      92                 :           1 :             return;
      93                 :             : 
      94                 :           2 :         const message = 'Assertion failed';
      95                 :             : 
      96         [ +  + ]:           2 :         if (data.length === 0)
      97                 :           1 :             data.push(message);
      98                 :             : 
      99         [ +  - ]:           2 :         if (typeof data[0] !== 'string') {
     100                 :           0 :             data.unshift(message);
     101                 :           2 :         } else {
     102                 :           2 :             const first = data.shift();
     103                 :           2 :             data.unshift(`${message}: ${first}`);
     104                 :             :         }
     105                 :           2 :         this.#logger('assert', data);
     106                 :           1 :     }
     107                 :             : 
     108                 :             :     /**
     109                 :             :      * Resets grouping and clears the terminal on systems supporting ANSI
     110                 :             :      * terminal control sequences.
     111                 :             :      *
     112                 :             :      * In file-based stdout or systems which do not support clearing,
     113                 :             :      * console.clear() has no visual effect.
     114                 :             :      *
     115                 :             :      * @returns {void}
     116                 :             :      */
     117                 :          48 :     clear() {
     118                 :           2 :         this.#groupIndentation = '';
     119                 :           2 :         imports.gi.GjsPrivate.clear_terminal();
     120                 :             :     }
     121                 :             : 
     122                 :             :     /**
     123                 :             :      * Logs a message with severity equal to {@see GLib.LogLevelFlags.DEBUG}.
     124                 :             :      *
     125                 :             :      * @param  {...any} data formatting substitutions, if applicable
     126                 :             :      */
     127                 :          48 :     debug(...data) {
     128                 :           0 :         this.#logger('debug', data);
     129                 :             :     }
     130                 :             : 
     131                 :             :     /**
     132                 :             :      * Logs a message with severity equal to {@see GLib.LogLevelFlags.CRITICAL}.
     133                 :             :      * Does not use {@see GLib.LogLevelFlags.ERROR} to avoid asserting and
     134                 :             :      * forcibly shutting down the application.
     135                 :             :      *
     136                 :             :      * @param  {...any} data formatting substitutions, if applicable
     137                 :             :      */
     138                 :          58 :     error(...data) {
     139                 :          10 :         this.#logger('error', data);
     140                 :             :     }
     141                 :             : 
     142                 :             :     /**
     143                 :             :      * Logs a message with severity equal to {@see GLib.LogLevelFlags.INFO}.
     144                 :             :      *
     145                 :             :      * @param  {...any} data formatting substitutions, if applicable
     146                 :             :      */
     147                 :          59 :     info(...data) {
     148                 :          11 :         this.#logger('info', data);
     149                 :             :     }
     150                 :             : 
     151                 :             :     /**
     152                 :             :      * Logs a message with severity equal to {@see GLib.LogLevelFlags.MESSAGE}.
     153                 :             :      *
     154                 :             :      * @param  {...any} data formatting substitutions, if applicable
     155                 :             :      */
     156                 :          67 :     log(...data) {
     157                 :          19 :         this.#logger('log', data);
     158                 :             :     }
     159                 :             : 
     160                 :             :     // 1.1.7 table(tabularData, properties)
     161                 :          48 :     table(tabularData, _properties) {
     162                 :           1 :         this.log(tabularData);
     163                 :             :     }
     164                 :             : 
     165                 :             :     /**
     166                 :             :      * @param  {...any} data formatting substitutions, if applicable
     167                 :             :      * @returns {void}
     168                 :             :      */
     169                 :          60 :     trace(...data) {
     170         [ +  + ]:          12 :         if (data.length === 0)
     171                 :           1 :             data = ['Trace'];
     172                 :             : 
     173                 :          12 :         this.#logger('trace', data);
     174                 :             :     }
     175                 :             : 
     176                 :             :     /**
     177                 :             :      * Logs a message with severity equal to {@see GLib.LogLevelFlags.WARNING}.
     178                 :             :      *
     179                 :             :      * @param  {...any} data formatting substitutions, if applicable
     180                 :             :      * @returns {void}
     181                 :             :      */
     182                 :          59 :     warn(...data) {
     183                 :          11 :         this.#logger('warn', data);
     184                 :             :     }
     185                 :             : 
     186                 :             :     /**
     187                 :             :      * @param {object} item an item to format generically
     188                 :             :      * @param {never} [options] any additional options for the formatter. Unused
     189                 :             :      *   in our implementation.
     190                 :             :      */
     191                 :          48 :     dir(item, options) {
     192                 :           0 :         const object = formatGenerically(item);
     193                 :           0 :         this.#printer('dir', [object], options);
     194                 :             :     }
     195                 :             : 
     196                 :             :     /**
     197                 :             :      * @param  {...any} data formatting substitutions, if applicable
     198                 :             :      * @returns {void}
     199                 :             :      */
     200                 :          48 :     dirxml(...data) {
     201 [ #  # ][ #  # ]:           0 :         this.log(...data);
     202                 :             :     }
     203                 :             : 
     204                 :             :     // 1.2 Counting functions
     205                 :             :     // https://console.spec.whatwg.org/#counting
     206                 :             : 
     207                 :             :     /**
     208                 :             :      * Logs how many times console.count(label) has been called with a given
     209                 :             :      * label.
     210                 :             :      * {@see console.countReset()} for resetting a count.
     211                 :             :      *
     212                 :             :      * @param  {string} label unique identifier for this action
     213                 :             :      * @returns {void}
     214                 :             :      */
     215                 :          48 :     count(label) {
     216         [ #  # ]:           0 :         this.#countLabels[label] ??= 0;
     217                 :           0 :         const count = ++this.#countLabels[label];
     218                 :           0 :         const concat = `${label}: ${count}`;
     219                 :             : 
     220                 :           0 :         this.#logger('count', [concat]);
     221                 :             :     }
     222                 :             : 
     223                 :             :     /**
     224                 :             :      * @param  {string} label the unique label to reset the count for
     225                 :             :      * @returns {void}
     226                 :             :      */
     227                 :          48 :     countReset(label) {
     228                 :           0 :         const count = this.#countLabels[label];
     229         [ #  # ]:           0 :         if (typeof count !== 'number')
     230                 :           0 :             this.#printer('reportWarning', [`No count found for label: '${label}'.`]);
     231                 :             :         else
     232                 :           0 :             this.#countLabels[label] = 0;
     233                 :             :     }
     234                 :             : 
     235                 :             :     // 1.3 Grouping functions
     236                 :             :     // https://console.spec.whatwg.org/#grouping
     237                 :             : 
     238                 :             :     /**
     239                 :             :      * @param  {...any} data formatting substitutions, if applicable
     240                 :             :      * @returns {void}
     241                 :             :      */
     242                 :          49 :     group(...data) {
     243                 :           1 :         this.#logger('group', data);
     244                 :           1 :         this.#groupIndentation += '  ';
     245                 :             :     }
     246                 :             : 
     247                 :             :     /**
     248                 :             :      * Alias for console.group()
     249                 :             :      *
     250                 :             :      * @param  {...any} data formatting substitutions, if applicable
     251                 :             :      * @returns {void}
     252                 :             :      */
     253                 :          48 :     groupCollapsed(...data) {
     254                 :             :         // We can't 'collapse' output in a terminal, so we alias to
     255                 :             :         // group()
     256 [ #  # ][ #  # ]:           0 :         this.group(...data);
     257                 :             :     }
     258                 :             : 
     259                 :             :     /**
     260                 :             :      * @returns {void}
     261                 :             :      */
     262                 :          48 :     groupEnd() {
     263                 :           0 :         this.#groupIndentation = this.#groupIndentation.slice(0, -2);
     264                 :             :     }
     265                 :             : 
     266                 :             :     // 1.4 Timing functions
     267                 :             :     // https://console.spec.whatwg.org/#timing
     268                 :             : 
     269                 :             :     /**
     270                 :             :      * @param {string} label unique identifier for this action, pass to
     271                 :             :      *   console.timeEnd() to complete
     272                 :             :      * @returns {void}
     273                 :             :      */
     274                 :          48 :     time(label) {
     275                 :           2 :         this.#timeLabels[label] = imports.gi.GLib.get_monotonic_time();
     276                 :             :     }
     277                 :             : 
     278                 :             :     /**
     279                 :             :      * Logs the time since the last call to console.time(label) where label is
     280                 :             :      * the same.
     281                 :             :      *
     282                 :             :      * @param {string} label unique identifier for this action, pass to
     283                 :             :      *   console.timeEnd() to complete
     284                 :             :      * @param {...any} data string substitutions, if applicable
     285                 :             :      * @returns {void}
     286                 :             :      */
     287                 :          50 :     timeLog(label, ...data) {
     288                 :           2 :         const startTime = this.#timeLabels[label];
     289                 :             : 
     290         [ +  + ]:           2 :         if (typeof startTime !== 'number') {
     291                 :           2 :             this.#printer('reportWarning', [
     292                 :           1 :                 `No time log found for label: '${label}'.`,
     293                 :             :             ]);
     294                 :           1 :         } else {
     295                 :           1 :             const durationMs = (imports.gi.GLib.get_monotonic_time() - startTime) / 1000;
     296                 :           1 :             const concat = `${label}: ${durationMs.toFixed(3)} ms`;
     297                 :           1 :             data.unshift(concat);
     298                 :             : 
     299                 :           1 :             this.#printer('timeLog', data);
     300                 :             :         }
     301                 :             :     }
     302                 :             : 
     303                 :             :     /**
     304                 :             :      * Logs the time since the last call to console.time(label) and completes
     305                 :             :      * the action.
     306                 :             :      * Call console.time(label) again to re-measure.
     307                 :             :      *
     308                 :             :      * @param {string} label unique identifier for this action
     309                 :             :      * @returns {void}
     310                 :             :      */
     311                 :          50 :     timeEnd(label) {
     312                 :           2 :         const startTime = this.#timeLabels[label];
     313                 :             : 
     314         [ +  - ]:           2 :         if (typeof startTime !== 'number') {
     315                 :           0 :             this.#printer('reportWarning', [
     316                 :           0 :                 `No time log found for label: '${label}'.`,
     317                 :             :             ]);
     318                 :           2 :         } else {
     319                 :           2 :             delete this.#timeLabels[label];
     320                 :             : 
     321                 :           2 :             const durationMs = (imports.gi.GLib.get_monotonic_time() - startTime) / 1000;
     322                 :           2 :             const concat = `${label}: ${durationMs.toFixed(3)} ms`;
     323                 :             : 
     324                 :           2 :             this.#printer('timeEnd', [concat]);
     325                 :             :         }
     326                 :             :     }
     327                 :             : 
     328                 :             :     // Non-standard functions which are de-facto standards.
     329                 :             :     // Similar to Node, we define these as no-ops for now.
     330                 :             : 
     331                 :             :     /**
     332                 :             :      * @deprecated Not implemented in GJS
     333                 :             :      *
     334                 :             :      * @param {string} _label unique identifier for this action, pass to
     335                 :             :      *   console.profileEnd to complete
     336                 :             :      * @returns {void}
     337                 :             :      */
     338                 :          48 :     profile(_label) {}
     339                 :             : 
     340                 :             :     /**
     341                 :             :      * @deprecated Not implemented in GJS
     342                 :             :      *
     343                 :             :      * @param {string} _label unique identifier for this action
     344                 :             :      * @returns {void}
     345                 :             :      */
     346                 :          48 :     profileEnd(_label) {}
     347                 :             : 
     348                 :             :     /**
     349                 :             :      * @deprecated Not implemented in GJS
     350                 :             :      *
     351                 :             :      * @param {string} _label unique identifier for this action
     352                 :             :      * @returns {void}
     353                 :             :      */
     354                 :          48 :     timeStamp(_label) {}
     355                 :             : 
     356                 :             :     // GJS-specific extensions for integrating with GLib structured logging
     357                 :             : 
     358                 :             :     /**
     359                 :             :      * @param {string} logDomain the GLib log domain this Console should print
     360                 :             :      *   with. Defaults to 'Gjs-Console'.
     361                 :             :      * @returns {void}
     362                 :             :      */
     363                 :          48 :     setLogDomain(logDomain) {
     364                 :           0 :         this.#logDomain = String(logDomain);
     365                 :             :     }
     366                 :             : 
     367                 :             :     /**
     368                 :             :      * @returns {string}
     369                 :             :      */
     370                 :          48 :     get logDomain() {
     371                 :           0 :         return this.#logDomain;
     372                 :             :     }
     373                 :             : 
     374                 :             :     // 2. Supporting abstract operations
     375                 :             :     // https://console.spec.whatwg.org/#supporting-ops
     376                 :             : 
     377                 :             :     /**
     378                 :             :      * 2.1. Logger
     379                 :             :      * https://console.spec.whatwg.org/#logger
     380                 :             :      *
     381                 :             :      * Conditionally applies formatting based on the inputted arguments,
     382                 :             :      * and prints at the provided severity (logLevel)
     383                 :             :      *
     384                 :             :      * @param {string} logLevel the severity (log level) the args should be
     385                 :             :      *   emitted with
     386                 :             :      * @param {unknown[]} args the arguments to pass to the printer
     387                 :             :      * @returns {void}
     388                 :             :      */
     389                 :          66 :     #logger(logLevel, args) {
     390         [ +  - ]:          66 :         if (args.length === 0)
     391                 :           0 :             return;
     392                 :             : 
     393 [ +  - ][ +  - ]:          66 :         const [first, ...rest] = args;
         [ +  + ][ -  + ]
     394                 :             : 
     395         [ +  + ]:          66 :         if (rest.length === 0) {
     396                 :          21 :             this.#printer(logLevel, [first]);
     397                 :          21 :             return undefined;
     398                 :             :         }
     399                 :             : 
     400                 :             :         // If first does not contain any format specifiers, don't call Formatter
     401 [ -  + ][ +  - ]:          45 :         if (typeof first !== 'string' || !hasFormatSpecifiers(first)) {
     402                 :           0 :             this.#printer(logLevel, args);
     403                 :           0 :             return undefined;
     404                 :             :         }
     405                 :             : 
     406                 :             :         // Otherwise, perform print the result of Formatter.
     407         [ +  + ]:          45 :         this.#printer(logLevel, this.#formatter([first, ...rest]));
     408                 :             : 
     409                 :          45 :         return undefined;
     410                 :          66 :     }
     411                 :             : 
     412                 :             :     /**
     413                 :             :      * 2.2. Formatter
     414                 :             :      * https://console.spec.whatwg.org/#formatter
     415                 :             :      *
     416                 :             :      * @param {[string, ...any[]]} args an array of format strings followed by
     417                 :             :      *   their arguments
     418                 :             :      */
     419                 :          55 :     #formatter(args) {
     420                 :             :         // The initial formatting string is the first arg
     421                 :          55 :         let target = args[0];
     422                 :             : 
     423         [ +  - ]:          55 :         if (args.length === 1)
     424                 :           0 :             return target;
     425                 :             : 
     426                 :          55 :         const current = args[1];
     427                 :             : 
     428                 :             :         // Find the index of the first format specifier.
     429                 :          55 :         const specifierIndex = specifierTest.exec(target).index;
     430                 :          55 :         const specifier = target.slice(specifierIndex, specifierIndex + 2);
     431                 :          55 :         let converted = null;
     432 [ +  + ][ +  + ]:          55 :         switch (specifier) {
         [ +  + ][ +  + ]
         [ +  + ][ +  + ]
                 [ +  - ]
     433                 :             :         case '%s':
     434                 :          10 :             converted = String(current);
     435                 :             :             break;
     436                 :             :         case '%d':
     437                 :             :         case '%i':
     438         [ +  - ]:          20 :             if (typeof current === 'symbol')
     439                 :           0 :                 converted = Number.NaN;
     440                 :             :             else
     441                 :          20 :                 converted = parseInt(current, 10);
     442                 :             :             break;
     443                 :             :         case '%f':
     444         [ +  - ]:          10 :             if (typeof current === 'symbol')
     445                 :           0 :                 converted = Number.NaN;
     446                 :             :             else
     447                 :          10 :                 converted = parseFloat(current);
     448                 :             :             break;
     449                 :             :         case '%o':
     450                 :           5 :             converted = formatOptimally(current);
     451                 :             :             break;
     452                 :             :         case '%O':
     453                 :           5 :             converted = formatGenerically(current);
     454                 :             :             break;
     455                 :             :         case '%c':
     456                 :           5 :             converted = '';
     457                 :             :             break;
     458                 :             :         }
     459                 :             :         // If any of the previous steps set converted, replace the specifier in
     460                 :             :         // target with the converted value.
     461         [ -  + ]:          55 :         if (converted !== null) {
     462                 :             :             target =
     463                 :          55 :                 target.slice(0, specifierIndex) +
     464                 :          55 :                 converted +
     465                 :          55 :                 target.slice(specifierIndex + 2);
     466                 :             :         }
     467                 :             : 
     468                 :             :         /**
     469                 :             :          * Create the next format input...
     470                 :             :          *
     471                 :             :          * @type {[string, ...any[]]}
     472                 :             :          */
     473         [ +  + ]:          55 :         const result = [target, ...args.slice(2)];
     474                 :             : 
     475         [ +  + ]:          55 :         if (!hasFormatSpecifiers(target))
     476                 :          45 :             return result;
     477                 :             : 
     478         [ +  - ]:          10 :         if (result.length === 1)
     479                 :           0 :             return result;
     480                 :             : 
     481                 :          10 :         return this.#formatter(result);
     482                 :          55 :     }
     483                 :             : 
     484                 :             :     /**
     485                 :             :      * @typedef {object} PrinterOptions
     486                 :             :      * @param {Array.<string[]>} [stackTrace] an error stacktrace to append
     487                 :             :      * @param {Record<string, any>} [fields] fields to include in the structured
     488                 :             :      *   logging call
     489                 :             :      */
     490                 :             : 
     491                 :             :     /**
     492                 :             :      * 2.3. Printer
     493                 :             :      * https://console.spec.whatwg.org/#printer
     494                 :             :      *
     495                 :             :      * This implementation of Printer maps WHATWG log severity to
     496                 :             :      * {@see GLib.LogLevelFlags} and outputs using GLib structured logging.
     497                 :             :      *
     498                 :             :      * @param {string} logLevel the log level (log tag) the args should be
     499                 :             :      *   emitted with
     500                 :             :      * @param {unknown[]} args the arguments to print, either a format string
     501                 :             :      *   with replacement args or multiple strings
     502                 :             :      * @param {PrinterOptions} [options] additional options for the
     503                 :             :      *   printer
     504                 :             :      * @returns {void}
     505                 :             :      */
     506                 :          70 :     #printer(logLevel, args, options) {
     507                 :          70 :         const GLib = imports.gi.GLib;
     508                 :          70 :         let severity;
     509                 :             : 
     510 [ +  + ][ -  + ]:          70 :         switch (logLevel) {
         [ -  + ][ +  + ]
         [ +  + ][ -  + ]
         [ +  + ][ +  + ]
         [ -  + ][ -  + ]
         [ +  + ][ +  + ]
         [ -  + ][ +  + ]
         [ +  + ][ +  - ]
     511                 :             :         case 'log':
     512                 :             :         case 'dir':
     513                 :             :         case 'dirxml':
     514                 :             :         case 'trace':
     515                 :             :         case 'group':
     516                 :             :         case 'groupCollapsed':
     517                 :             :         case 'timeLog':
     518                 :             :         case 'timeEnd':
     519                 :          35 :             severity = GLib.LogLevelFlags.LEVEL_MESSAGE;
     520                 :             :             break;
     521                 :             :         case 'debug':
     522                 :           0 :             severity = GLib.LogLevelFlags.LEVEL_DEBUG;
     523                 :             :             break;
     524                 :             :         case 'count':
     525                 :             :         case 'info':
     526                 :          11 :             severity = GLib.LogLevelFlags.LEVEL_INFO;
     527                 :             :             break;
     528                 :             :         case 'warn':
     529                 :             :         case 'countReset':
     530                 :             :         case 'reportWarning':
     531                 :          12 :             severity = GLib.LogLevelFlags.LEVEL_WARNING;
     532                 :             :             break;
     533                 :             :         case 'error':
     534                 :             :         case 'assert':
     535                 :          12 :             severity = GLib.LogLevelFlags.LEVEL_CRITICAL;
     536                 :             :             break;
     537                 :             :         default:
     538                 :           0 :             severity = GLib.LogLevelFlags.LEVEL_MESSAGE;
     539                 :             :         }
     540                 :             : 
     541                 :          70 :         const output = args
     542                 :          70 :             .map(a => {
     543         [ +  - ]:          70 :                 if (a === null)
     544                 :           0 :                     return 'null';
     545         [ +  + ]:          70 :                 else if (typeof a === 'object')
     546                 :           6 :                     return formatOptimally(a);
     547         [ +  - ]:          64 :                 else if (typeof a === 'function')
     548                 :           0 :                     return a.toString();
     549         [ +  - ]:          64 :                 else if (typeof a === 'undefined')
     550                 :           0 :                     return 'undefined';
     551         [ +  - ]:          64 :                 else if (typeof a === 'bigint')
     552                 :           0 :                     return `${a}n`;
     553                 :             :                 else
     554                 :          64 :                     return String(a);
     555                 :             :             })
     556                 :          70 :             .join(' ');
     557                 :             : 
     558                 :          70 :         let formattedOutput = this.#groupIndentation + output;
     559                 :          70 :         const extraFields = {};
     560                 :             : 
     561         [ +  - ]:          70 :         let stackTrace = options?.stackTrace;
     562         [ -  + ]:          70 :         if (!stackTrace &&
     563 [ +  + ][ +  + ]:          70 :             (logLevel === 'trace' || severity <= GLib.LogLevelFlags.LEVEL_WARNING)) {
     564                 :          36 :             stackTrace = new Error().stack;
     565         [ -  + ]:          36 :             const currentFile = stackTrace.match(/^[^@]*@(.*):\d+:\d+$/m)?.at(1);
     566                 :          36 :             const index = stackTrace.lastIndexOf(currentFile) + currentFile.length;
     567                 :             : 
     568                 :          36 :             stackTrace = stackTrace.substring(index).split('\n');
     569                 :             :             // Remove the remainder of the first line
     570                 :          36 :             stackTrace.shift();
     571                 :             :         }
     572                 :             : 
     573         [ +  + ]:          70 :         if (logLevel === 'trace') {
     574 [ -  + ][ -  + ]:          12 :             if (stackTrace?.length) {
     575                 :          12 :                 formattedOutput += `\n${stackTrace.map(s =>
     576                 :         132 :                     `${this.#groupIndentation}${s}`).join('\n')}`;
     577                 :             :             } else {
     578                 :           0 :                 formattedOutput +=
     579                 :           0 :                     `\n${this.#groupIndentation}No trace available`;
     580                 :             :             }
     581                 :             :         }
     582                 :             : 
     583 [ +  + ][ +  + ]:          70 :         if (stackTrace?.length) {
     584 [ +  - ][ +  - ]:          36 :             const [stackLine] = stackTrace;
     585                 :          36 :             const match = stackLine.match(/^([^@]*)@(.*):(\d+):\d+$/);
     586                 :             : 
     587         [ -  + ]:          36 :             if (match) {
     588 [ +  - ][ +  - ]:          36 :                 const [_, func, file, line] = match;
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
     589                 :             : 
     590         [ +  - ]:          36 :                 if (func)
     591                 :           0 :                     extraFields.CODE_FUNC = func;
     592         [ -  + ]:          36 :                 if (file)
     593                 :          36 :                     extraFields.CODE_FILE = file;
     594         [ -  + ]:          36 :                 if (line)
     595                 :          36 :                     extraFields.CODE_LINE = line;
     596                 :             :             }
     597                 :             :         }
     598                 :             : 
     599                 :         140 :         GLib.log_structured(this.#logDomain, severity, {
     600                 :          70 :             MESSAGE: formattedOutput,
     601                 :          70 :             ...extraFields,
     602 [ +  - ][ -  + ]:          70 :             ...options?.fields ?? {},
     603                 :             :         });
     604                 :             :     }
     605                 :             : }
     606                 :             : 
     607                 :          48 : const console = new Console();
     608                 :             : 
     609                 :             : /**
     610                 :             :  * @param {string} domain set the GLib log domain for the global console object.
     611                 :             :  */
     612                 :           0 : function setConsoleLogDomain(domain) {
     613                 :           0 :     console.setLogDomain(domain);
     614                 :             : }
     615                 :             : 
     616                 :             : /**
     617                 :             :  * @returns {string}
     618                 :             :  */
     619                 :           0 : function getConsoleLogDomain() {
     620                 :           0 :     return console.logDomain;
     621                 :             : }
     622                 :             : 
     623                 :             : /**
     624                 :             :  * For historical web-compatibility reasons, the namespace object for
     625                 :             :  * console must have {} as its [[Prototype]].
     626                 :             :  *
     627                 :             :  * @type {Omit<Console, 'setLogDomain' | 'logDomain'>}
     628                 :             :  */
     629                 :          48 : const globalConsole = Object.create({});
     630                 :             : 
     631                 :             : const propertyNames =
     632                 :             :     /** @type {['constructor', ...Array<string & keyof Console>]} */
     633                 :             :     // eslint-disable-next-line no-extra-parens
     634                 :          48 :     (Object.getOwnPropertyNames(Console.prototype));
     635                 :          48 : const propertyDescriptors = Object.getOwnPropertyDescriptors(Console.prototype);
     636         [ +  + ]:        1248 : for (const key of propertyNames) {
     637         [ +  + ]:        1200 :     if (key === 'constructor')
     638                 :          48 :         continue;
     639                 :             : 
     640                 :             :     // This non-standard function shouldn't be included.
     641         [ +  + ]:        1152 :     if (key === 'setLogDomain')
     642                 :          48 :         continue;
     643                 :             : 
     644                 :        1104 :     const descriptor = propertyDescriptors[key];
     645         [ +  + ]:        1104 :     if (typeof descriptor.value !== 'function')
     646                 :          48 :         continue;
     647                 :             : 
     648                 :        2112 :     Object.defineProperty(globalConsole, key, {
     649                 :        1056 :         ...descriptor,
     650                 :        1056 :         value: descriptor.value.bind(console),
     651                 :             :     });
     652                 :             : }
     653                 :          96 : Object.defineProperties(globalConsole, {
     654                 :          48 :     [Symbol.toStringTag]: {
     655                 :          48 :         configurable: false,
     656                 :          48 :         enumerable: true,
     657                 :          48 :         get() {
     658                 :           1 :             return 'console';
     659                 :             :         },
     660                 :             :     },
     661                 :             : });
     662                 :          48 : Object.freeze(globalConsole);
     663                 :             : 
     664                 :          96 : Object.defineProperty(globalThis, 'console', {
     665                 :          48 :     configurable: false,
     666                 :          48 :     enumerable: true,
     667                 :          48 :     writable: false,
     668                 :          48 :     value: globalConsole,
     669                 :             : });
     670                 :             : 
     671                 :             : export {
     672                 :             :     getConsoleLogDomain,
     673                 :             :     setConsoleLogDomain,
     674                 :             :     DEFAULT_LOG_DOMAIN
     675                 :             : };
     676                 :             : 
     677                 :          48 : export default {
     678                 :          48 :     getConsoleLogDomain,
     679                 :          48 :     setConsoleLogDomain,
     680                 :          48 :     DEFAULT_LOG_DOMAIN,
     681                 :             : };
        

Generated by: LCOV version 2.0-1