Repository: nodejs/node
Pull request: lib: fix inspect deduplication logic

Fix deduplication logic for `inspect` output when dealing with constructor names that contain custom tags, but checking for tag followed by lowercase characters, e.g: avoiding unnecessary tag `Set` from a constructor extended like `FooSet(3) { 1, 2, 3 }` from a `util.inspect(new FooSet([1, 2, 3]))` but keeping it for `Settings(3) [Set] { 1, 2, 3 }` from a `util.inspect(new Settings([1, 2, 3]))`

cc @BridgeAR 

Changed files:
filename: lib/internal/util/inspect.js
patch: @@ -1031,10 +1031,20 @@ function getPrefix(constructor, tag, fallback, size = '') {
     return `[${fallback}${size}: null prototype] `;
   }
 
-  if (tag !== '' && constructor !== tag) {
-    return `${constructor}${size} [${tag}] `;
+  let result = `${constructor}${size} `;
+  if (tag !== '') {
+    const position = constructor.indexOf(tag);
+    if (position === -1) {
+      result += `[${tag}] `;
+    } else {
+      const endPos = position + tag.length;
+      if (endPos !== constructor.length &&
+        constructor[endPos] === constructor[endPos].toLowerCase()) {
+        result += `[${tag}] `;
+      }
+    }
   }
-  return `${constructor}${size} `;
+  return result;
 }
 
 // Look up the keys of the object.

filename: test/es-module/test-esm-loader-with-syntax-error.mjs
patch: @@ -13,7 +13,7 @@ describe('ESM: loader with syntax error', { concurrency: !process.env.TEST_PARAL
       path('print-error-message.js'),
     ]);
 
-    match(stderr, /SyntaxError \[Error\]:/);
+    match(stderr, /SyntaxError/);
     ok(!stderr.includes('Bad command or file name'));
     notStrictEqual(code, 0);
   });

filename: test/parallel/test-util-inspect.js
patch: @@ -1415,11 +1415,11 @@ if (typeof Symbol !== 'undefined') {
   assert.strictEqual(util.inspect(new ArraySubclass(1, 2, 3)),
                      'ArraySubclass(3) [ 1, 2, 3 ]');
   assert.strictEqual(util.inspect(new SetSubclass([1, 2, 3])),
-                     'SetSubclass(3) [Set] { 1, 2, 3 }');
+                     'SetSubclass(3) { 1, 2, 3 }');
   assert.strictEqual(util.inspect(new MapSubclass([['foo', 42]])),
-                     "MapSubclass(1) [Map] { 'foo' => 42 }");
+                     "MapSubclass(1) { 'foo' => 42 }");
   assert.strictEqual(util.inspect(new PromiseSubclass(() => {})),
-                     'PromiseSubclass [Promise] { <pending> }');
+                     'PromiseSubclass { <pending> }');
   assert.strictEqual(util.inspect(new SymbolNameClass()),
                      'Symbol(name) {}');
   assert.strictEqual(
@@ -1430,6 +1430,29 @@ if (typeof Symbol !== 'undefined') {
     util.inspect(Object.setPrototypeOf(x, null)),
     '[ObjectSubclass: null prototype] { foo: 42 }'
   );
+
+  class MiddleErrorPart extends Error {}
+  assert(util.inspect(new MiddleErrorPart('foo')).includes('MiddleErrorPart: foo'));
+
+  class MapClass extends Map {}
+  assert.strictEqual(util.inspect(new MapClass([['key', 'value']])),
+                     "MapClass(1) { 'key' => 'value' }");
+
+  class AbcMap extends Map {}
+  assert.strictEqual(util.inspect(new AbcMap([['key', 'value']])),
+                     "AbcMap(1) { 'key' => 'value' }");
+
+  class SetAbc extends Set {}
+  assert.strictEqual(util.inspect(new SetAbc([1, 2, 3])),
+                     'SetAbc(3) { 1, 2, 3 }');
+
+  class FooSet extends Set {}
+  assert.strictEqual(util.inspect(new FooSet([1, 2, 3])),
+                     'FooSet(3) { 1, 2, 3 }');
+
+  class Settings extends Set {}
+  assert.strictEqual(util.inspect(new Settings([1, 2, 3])),
+                     'Settings(3) [Set] { 1, 2, 3 }');
 }
 
 // Empty and circular before depth.