From 23b26260752c37a50f915f762148855d4d7bb209 Mon Sep 17 00:00:00 2001 From: Jacob Ley Date: Sun, 22 May 2022 15:54:50 -0400 Subject: [PATCH 1/5] fix: handle canonicalizing Module --- lib/utils.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index fc7271d019..897dadd90c 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -213,7 +213,7 @@ exports.type = function type(value) { exports.stringify = function (value) { var typeHint = canonicalType(value); - if (!~['object', 'array', 'function'].indexOf(typeHint)) { + if (!~['object', 'array', 'function', 'module'].indexOf(typeHint)) { if (typeHint === 'buffer') { var json = Buffer.prototype.toJSON.call(value); // Based on the toJSON result @@ -399,6 +399,12 @@ exports.canonicalize = function canonicalize(value, stack, typeHint) { break; } /* falls through */ + case 'module': + if (value[Symbol.toStringTag] === 'Module') { + canonicalizedObj = canonicalizedObj || {}; + canonicalizedObj['[Symbol.toStringTag]'] = 'Module'; + } + /* falls through */ case 'object': canonicalizedObj = canonicalizedObj || {}; withStack(value, function () { From 45d3f5a91e68b2910326be50517e49275d36c126 Mon Sep 17 00:00:00 2001 From: Jacob Ley Date: Sun, 22 May 2022 15:57:55 -0400 Subject: [PATCH 2/5] fix: add tests for canonicalizing a module --- test/unit/fixtures/module.mjs | 4 ++++ test/unit/utils.spec.js | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 test/unit/fixtures/module.mjs diff --git a/test/unit/fixtures/module.mjs b/test/unit/fixtures/module.mjs new file mode 100644 index 0000000000..bec42311d7 --- /dev/null +++ b/test/unit/fixtures/module.mjs @@ -0,0 +1,4 @@ +export default 123; + +export const foo = 'abc'; +export const bar = true; diff --git a/test/unit/utils.spec.js b/test/unit/utils.spec.js index 46f27b57ad..685771a832 100644 --- a/test/unit/utils.spec.js +++ b/test/unit/utils.spec.js @@ -2,6 +2,8 @@ 'use strict'; var utils = require('../../lib/utils'); +const esmUtils = require('../../lib/nodejs/esm-utils'); +const Path = require('node:path'); var sinon = require('sinon'); describe('lib/utils', function () { @@ -288,6 +290,22 @@ describe('lib/utils', function () { ].join('\n'); expect(stringify(expected), 'to be', actual); }); + + it('should represent modules', async function () { + const expected = await esmUtils.requireOrImport( + Path.join(__dirname, './fixtures/module.mjs') + ); + const actual = [ + '{', + ' "[Symbol.toStringTag]": "Module"', + ' "bar": true', + ' "default": 123', + ' "foo": "abc"', + '}' + ].join('\n'); + + expect(stringify(expected), 'to be', actual); + }); }); it('should canonicalize the object', function () { From 8ccc02b20ff33d0a71c6462115774b63486c196a Mon Sep 17 00:00:00 2001 From: Jacob Ley Date: Sun, 3 Dec 2023 23:39:43 -0600 Subject: [PATCH 3/5] fix: skip module serialization on browser --- test/unit/utils.spec.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/unit/utils.spec.js b/test/unit/utils.spec.js index 685771a832..74020ccf0c 100644 --- a/test/unit/utils.spec.js +++ b/test/unit/utils.spec.js @@ -292,6 +292,11 @@ describe('lib/utils', function () { }); it('should represent modules', async function () { + if (process.browser) { + // Current rollup config cannot `import()` + this.skip(); + return; + } const expected = await esmUtils.requireOrImport( Path.join(__dirname, './fixtures/module.mjs') ); From b4b70661c7cd97f570c8352d12dd3d9a33fca817 Mon Sep 17 00:00:00 2001 From: Jacob Ley Date: Thu, 18 Apr 2024 01:14:35 -0500 Subject: [PATCH 4/5] fix: handle more generic null prototype instead of Module --- lib/utils.js | 21 ++++++++----- test/unit/fixtures/module.mjs | 4 --- test/unit/utils.spec.js | 55 ++++++++++++++++++++++------------- 3 files changed, 48 insertions(+), 32 deletions(-) delete mode 100644 test/unit/fixtures/module.mjs diff --git a/lib/utils.js b/lib/utils.js index 897dadd90c..94b2f1bd49 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -138,7 +138,7 @@ function emptyRepresentation(value, typeHint) { * canonicalType(global) // 'global' * canonicalType(new String('foo') // 'object' * canonicalType(async function() {}) // 'asyncfunction' - * canonicalType(await import(name)) // 'module' + * canonicalType(Object.create(null)) // 'null-prototype' */ var canonicalType = (exports.canonicalType = function canonicalType(value) { if (value === undefined) { @@ -147,7 +147,14 @@ var canonicalType = (exports.canonicalType = function canonicalType(value) { return 'null'; } else if (Buffer.isBuffer(value)) { return 'buffer'; + } else if ( + typeof value === 'object' && + // eslint-disable-next-line no-prototype-builtins + !Object.prototype.isPrototypeOf(value) + ) { + return 'null-prototype'; } + return Object.prototype.toString .call(value) .replace(/^\[.+\s(.+?)]$/, '$1') @@ -213,7 +220,7 @@ exports.type = function type(value) { exports.stringify = function (value) { var typeHint = canonicalType(value); - if (!~['object', 'array', 'function', 'module'].indexOf(typeHint)) { + if (!~['object', 'array', 'function', 'null-prototype'].indexOf(typeHint)) { if (typeHint === 'buffer') { var json = Buffer.prototype.toJSON.call(value); // Based on the toJSON result @@ -399,14 +406,12 @@ exports.canonicalize = function canonicalize(value, stack, typeHint) { break; } /* falls through */ - case 'module': - if (value[Symbol.toStringTag] === 'Module') { - canonicalizedObj = canonicalizedObj || {}; - canonicalizedObj['[Symbol.toStringTag]'] = 'Module'; - } - /* falls through */ + case 'null-prototype': case 'object': canonicalizedObj = canonicalizedObj || {}; + if (typeHint === 'null-prototype' && Symbol.toStringTag in value) { + canonicalizedObj['[Symbol.toStringTag]'] = value[Symbol.toStringTag]; + } withStack(value, function () { Object.keys(value) .sort() diff --git a/test/unit/fixtures/module.mjs b/test/unit/fixtures/module.mjs deleted file mode 100644 index bec42311d7..0000000000 --- a/test/unit/fixtures/module.mjs +++ /dev/null @@ -1,4 +0,0 @@ -export default 123; - -export const foo = 'abc'; -export const bar = true; diff --git a/test/unit/utils.spec.js b/test/unit/utils.spec.js index 74020ccf0c..0e54bf394e 100644 --- a/test/unit/utils.spec.js +++ b/test/unit/utils.spec.js @@ -2,8 +2,6 @@ 'use strict'; var utils = require('../../lib/utils'); -const esmUtils = require('../../lib/nodejs/esm-utils'); -const Path = require('node:path'); var sinon = require('sinon'); describe('lib/utils', function () { @@ -291,25 +289,42 @@ describe('lib/utils', function () { expect(stringify(expected), 'to be', actual); }); - it('should represent modules', async function () { - if (process.browser) { - // Current rollup config cannot `import()` - this.skip(); - return; - } - const expected = await esmUtils.requireOrImport( - Path.join(__dirname, './fixtures/module.mjs') - ); - const actual = [ - '{', - ' "[Symbol.toStringTag]": "Module"', - ' "bar": true', - ' "default": 123', - ' "foo": "abc"', - '}' - ].join('\n'); + describe('should represent null prototypes', function () { + it('With explicit names', function () { + const foo = Object.create(null, { + [Symbol.toStringTag]: {value: 'Foo'}, + bing: {get: () => 'bong', enumerable: true} + }); + const expected = [ + '{', + ' "[Symbol.toStringTag]": "Foo"', + ' "bing": "bong"', + '}' + ].join('\n'); + + expect(stringify(foo), 'to be', expected); + }); - expect(stringify(expected), 'to be', actual); + it('Without names', function () { + const unnamed = { + bing: 'bong', + abc: 123 + }; + unnamed.self = unnamed; + const expected = [ + '{', + ' "abc": 123', + ' "bing": "bong"', + ' "self": [Circular]', + '}' + ].join('\n'); + + expect( + stringify(Object.setPrototypeOf(unnamed, null)), + 'to be', + expected + ); + }); }); }); From 3fbdd97c9b4f3468039f151b358783cb1cf10b25 Mon Sep 17 00:00:00 2001 From: Jacob Ley Date: Tue, 16 Jul 2024 19:30:04 -0500 Subject: [PATCH 5/5] fix: simplify null prototype check --- lib/utils.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 94b2f1bd49..36607c788e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -147,11 +147,7 @@ var canonicalType = (exports.canonicalType = function canonicalType(value) { return 'null'; } else if (Buffer.isBuffer(value)) { return 'buffer'; - } else if ( - typeof value === 'object' && - // eslint-disable-next-line no-prototype-builtins - !Object.prototype.isPrototypeOf(value) - ) { + } else if (Object.getPrototypeOf(value) === null) { return 'null-prototype'; }