Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swap to use internal isPlainObject utility #2219

Merged
merged 4 commits into from
Feb 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/ComputedDataProxy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const lodashSet = require("lodash/set");
const lodashGet = require("lodash/get");
const lodashIsPlainObject = require("lodash/isPlainObject");
const isPlainObject = require("./Util/IsPlainObject");

/* Calculates computed data using Proxies */
class ComputedDataProxy {
Expand All @@ -13,7 +13,7 @@ class ComputedDataProxy {
}

isArrayOrPlainObject(data) {
return Array.isArray(data) || lodashIsPlainObject(data);
return Array.isArray(data) || isPlainObject(data);
}

getProxyData(data, keyRef) {
Expand Down Expand Up @@ -97,7 +97,7 @@ class ComputedDataProxy {
}

_getProxyData(data, keyRef, parentKey = "") {
if (lodashIsPlainObject(data)) {
if (isPlainObject(data)) {
return this._getProxyForObject(data, keyRef, parentKey);
} else if (Array.isArray(data)) {
return this._getProxyForArray(data, keyRef, parentKey);
Expand Down
6 changes: 3 additions & 3 deletions src/Plugins/RenderPlugin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const fs = require("fs");
const fsp = fs.promises;
const lodashIsPlainObject = require("lodash/isPlainObject");
const isPlainObject = require("../Util/IsPlainObject");

// TODO add a first-class Markdown component to expose this using Markdown-only syntax (will need to be synchronous for markdown-it)

Expand Down Expand Up @@ -276,7 +276,7 @@ function EleventyPlugin(eleventyConfig, options = {}) {
}

// if the user passes a string or other literal, remap to an object.
if (!lodashIsPlainObject(data)) {
if (!isPlainObject(data)) {
data = {
_: data,
};
Expand Down Expand Up @@ -309,7 +309,7 @@ function EleventyPlugin(eleventyConfig, options = {}) {
}

// if the user passes a string or other literal, remap to an object.
if (!lodashIsPlainObject(data)) {
if (!isPlainObject(data)) {
data = {
_: data,
};
Expand Down
2 changes: 1 addition & 1 deletion src/Template.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const mkdir = util.promisify(fs.mkdir);
const os = require("os");
const path = require("path");
const normalize = require("normalize-path");
const isPlainObject = require("lodash/isPlainObject");
const isPlainObject = require("./Util/IsPlainObject");
const lodashGet = require("lodash/get");
const lodashSet = require("lodash/set");
const { DateTime } = require("luxon");
Expand Down
2 changes: 1 addition & 1 deletion src/TemplateBehavior.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const isPlainObject = require("lodash/isPlainObject");
const isPlainObject = require("./Util/IsPlainObject");

class TemplateBehavior {
constructor(config) {
Expand Down
2 changes: 1 addition & 1 deletion src/TemplateMap.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const isPlainObject = require("lodash/isPlainObject");
const isPlainObject = require("./Util/IsPlainObject");
const DependencyGraph = require("dependency-graph").DepGraph;
const TemplateCollection = require("./TemplateCollection");
const EleventyErrorUtil = require("./EleventyErrorUtil");
Expand Down
2 changes: 1 addition & 1 deletion src/TemplatePermalink.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const path = require("path");
const TemplatePath = require("./TemplatePath");
const normalize = require("normalize-path");
const isPlainObject = require("lodash/isPlainObject");
const isPlainObject = require("./Util/IsPlainObject");
const serverlessUrlFilter = require("./Filters/ServerlessUrl");

class TemplatePermalink {
Expand Down
24 changes: 24 additions & 0 deletions src/Util/IsPlainObject.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* Prior art: this utility was created for https://github.com/11ty/eleventy/issues/2214

* Inspired by implementations from `is-what`, `typechecker`, `jQuery`, and `lodash`

* `is-what`
* More reading at https://www.npmjs.com/package/is-what#user-content-isplainobject-vs-isanyobject
* if (Object.prototype.toString.call(value).slice(8, -1) !== 'Object') return false;
* return value.constructor === Object && Object.getPrototypeOf(value) === Object.prototype;

* `typechecker`
* return value !== null && typeof value === 'object' && value.__proto__ === Object.prototype;

* Notably jQuery and lodash have very similar implementations.

* For later, remember the `value === Object(value)` trick
*/

module.exports = function (value) {
if (value === null || typeof value !== "object") {
return false;
}
let proto = Object.getPrototypeOf(value);
return !proto || proto === Object.prototype;
};
2 changes: 1 addition & 1 deletion src/Util/Merge.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const isPlainObject = require("lodash/isPlainObject");
const isPlainObject = require("./IsPlainObject");
const OVERRIDE_PREFIX = "override:";

function getMergedItem(target, source, parentKey) {
Expand Down
80 changes: 80 additions & 0 deletions test/IsPlainObjectTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const test = require("ava");
const isPlainObject = require("../src/Util/IsPlainObject");

test("isPlainObject", (t) => {
t.is(isPlainObject(null), false);
t.is(isPlainObject(undefined), false);
t.is(isPlainObject(1), false);
t.is(isPlainObject(true), false);
t.is(isPlainObject(false), false);
t.is(isPlainObject("string"), false);
t.is(isPlainObject([]), false);
t.is(isPlainObject(Symbol("a")), false);
t.is(
isPlainObject(function () {}),
false
);
});

// https://github.com/lodash/lodash/blob/ddfd9b11a0126db2302cb70ec9973b66baec0975/test/test.js#L11447
// Notably, did not include the test for DOM Elements.
test("Test from lodash.isPlainObject", (t) => {
t.is(isPlainObject({}), true);
t.is(isPlainObject({ a: 1 }), true);

function Foo(a) {
this.a = 1;
}

t.is(isPlainObject({ constructor: Foo }), true);
t.is(isPlainObject([1, 2, 3]), false);
t.is(isPlainObject(new Foo(1)), false);
});

test("Test from lodash.isPlainObject: should return `true` for objects with a `[[Prototype]]` of `null`", (t) => {
let obj = Object.create(null);
t.is(isPlainObject(obj), true);

obj.constructor = Object.prototype.constructor;
t.is(isPlainObject(obj), true);
});

test("Test from lodash.isPlainObject: should return `true` for objects with a `valueOf` property", (t) => {
t.is(isPlainObject({ valueOf: 0 }), true);
});

test("Test from lodash.isPlainObject: should return `true` for objects with a writable `Symbol.toStringTag` property", (t) => {
let obj = {};
obj[Symbol.toStringTag] = "X";

t.is(isPlainObject(obj), true);
});

test("Test from lodash.isPlainObject: should return `false` for objects with a custom `[[Prototype]]`", (t) => {
let obj = Object.create({ a: 1 });
t.is(isPlainObject(obj), false);
});

test("Test from lodash.isPlainObject (modified): should return `false` for non-Object objects", (t) => {
t.is(isPlainObject(arguments), true); // WARNING: lodash was false
t.is(isPlainObject(Error), false);
t.is(isPlainObject(Math), true); // WARNING: lodash was false
});

test("Test from lodash.isPlainObject: should return `false` for non-objects", (t) => {
t.is(isPlainObject(true), false);
t.is(isPlainObject("a"), false);
t.is(isPlainObject(Symbol("a")), false);
});

test("Test from lodash.isPlainObject (modified): should return `true` for objects with a read-only `Symbol.toStringTag` property", (t) => {
var object = {};
Object.defineProperty(object, Symbol.toStringTag, {
configurable: true,
enumerable: false,
writable: false,
value: "X",
});

t.is(isPlainObject(object), true); // WARNING: lodash was false
});