LCOV - code coverage report
Current view: top level - modules/esm - console.js (source / functions) Hit Total Coverage
Test: gjs- Code Coverage Lines: 199 237 84.0 %
Date: 2023-09-17 02:39:54 Functions: 24 38 63.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 128 190 67.4 %

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

Generated by: LCOV version 1.14