Skip to content

Commit

Permalink
Use Object.hasOwn when available (#16248)
Browse files Browse the repository at this point in the history
* Use `Object.hasOwn` when available

* Also polyfill in standalone

* Update comments
  • Loading branch information
nicolo-ribaudo committed Feb 1, 2024
1 parent ee7ef0b commit 08a057c
Show file tree
Hide file tree
Showing 19 changed files with 54 additions and 60 deletions.
19 changes: 14 additions & 5 deletions babel.config.js
Expand Up @@ -114,6 +114,7 @@ module.exports = function (api) {
"packages/babel-runtime/regenerator"
);
targets = { ie: 7 };
needsPolyfillsForOldNode = true;
break;
case "rollup":
convertESM = false;
Expand Down Expand Up @@ -183,10 +184,6 @@ module.exports = function (api) {
["@babel/transform-object-rest-spread", { useBuiltIns: true }],

convertESM ? "@babel/transform-export-namespace-from" : null,

pluginPackageJsonMacro,

needsPolyfillsForOldNode && pluginPolyfillsOldNode,
].filter(Boolean),
overrides: [
{
Expand Down Expand Up @@ -250,6 +247,10 @@ module.exports = function (api) {
},
"flag-BABEL_8_BREAKING",
],

pluginPackageJsonMacro,

needsPolyfillsForOldNode && pluginPolyfillsOldNode,
].filter(Boolean),
},
convertESM && {
Expand Down Expand Up @@ -391,7 +392,6 @@ function bool(value) {
// copy and paste it.
// `((v,w)=>(v=v.split("."),w=w.split("."),+v[0]>+w[0]||v[0]==w[0]&&+v[1]>=+w[1]))`;

// TODO(Babel 8) This polyfills are only needed for Node.js 6 and 8
/** @param {import("@babel/core")} api */
function pluginPolyfillsOldNode({ template, types: t }) {
const polyfills = [
Expand Down Expand Up @@ -468,6 +468,15 @@ function pluginPolyfillsOldNode({ template, types: t }) {
process.allowedNodeEnvironmentFlags || require("node-environment-flags")
`,
},
{
name: "Object.hasOwn",
necessary: () => true,
supported: path =>
path.parentPath.isCallExpression({ callee: path.node }),
// Object.hasOwn has been introduced in Node.js 16.9.0
// https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V16.md#v8-93
replacement: template`hasOwnProperty.call`,
},
];

return {
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-core/src/config/config-chain.ts
Expand Up @@ -754,7 +754,7 @@ function normalizeOptions(opts: ValidatedOptions): ValidatedOptions {

// "sourceMap" is just aliased to sourceMap, so copy it over as
// we merge the options together.
if (Object.prototype.hasOwnProperty.call(options, "sourceMap")) {
if (Object.hasOwn(options, "sourceMap")) {
options.sourceMaps = options.sourceMap;
delete options.sourceMap;
}
Expand Down
Expand Up @@ -427,7 +427,7 @@ export function assertTargets(

if (key === "esmodules") assertBoolean(subLoc, val);
else if (key === "browsers") assertBrowsersList(subLoc, val);
else if (!Object.hasOwnProperty.call(TargetNames, key)) {
else if (!Object.hasOwn(TargetNames, key)) {
const validTargets = Object.keys(TargetNames).join(", ");
throw new Error(
`${msg(
Expand Down
6 changes: 1 addition & 5 deletions packages/babel-core/src/config/validation/options.ts
Expand Up @@ -396,12 +396,8 @@ function throwUnknownError(loc: OptionPath) {
}
}

function has(obj: {}, key: string) {
return Object.prototype.hasOwnProperty.call(obj, key);
}

function assertNoDuplicateSourcemap(opts: {}): void {
if (has(opts, "sourceMap") && has(opts, "sourceMaps")) {
if (Object.hasOwn(opts, "sourceMap") && Object.hasOwn(opts, "sourceMaps")) {
throw new Error(".sourceMap is an alias for .sourceMaps, cannot use both");
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-helper-fixtures/src/index.ts
Expand Up @@ -399,7 +399,7 @@ export function resolveOptionPluginOrPreset(
}
if (options.env) {
for (const envName in options.env) {
if (!{}.hasOwnProperty.call(options.env, envName)) continue;
if (!Object.hasOwn(options.env, envName)) continue;
resolveOptionPluginOrPreset(options.env[envName], optionsDir);
}
}
Expand Down
12 changes: 4 additions & 8 deletions packages/babel-helper-plugin-utils/src/index.ts
Expand Up @@ -78,10 +78,10 @@ function copyApiObject(api: PluginAPI): PluginAPI {
proto = Object.getPrototypeOf(api);
if (
proto &&
(!has(proto, "version") ||
!has(proto, "transform") ||
!has(proto, "template") ||
!has(proto, "types"))
(!Object.hasOwn(proto, "version") ||
!Object.hasOwn(proto, "transform") ||
!Object.hasOwn(proto, "template") ||
!Object.hasOwn(proto, "types"))
) {
proto = null;
}
Expand All @@ -93,10 +93,6 @@ function copyApiObject(api: PluginAPI): PluginAPI {
};
}

function has(obj: {}, key: string) {
return Object.prototype.hasOwnProperty.call(obj, key);
}

function throwVersionError(range: string | number, version: string) {
if (typeof range === "number") {
if (!Number.isInteger(range)) {
Expand Down
Expand Up @@ -733,7 +733,7 @@ const assertTest = function (
// saveInFiles always creates an empty .babelrc, so lets exclude for now
filename !== ".babelrc" &&
filename !== ".babelignore" &&
!Object.prototype.hasOwnProperty.call(opts.inFiles, filename)
!Object.hasOwn(opts.inFiles, filename)
) {
const expected = opts.outFiles[filename];
const actual = actualFiles[filename];
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-parser/src/parser/lval.ts
Expand Up @@ -32,7 +32,7 @@ import { Errors, type LValAncestor } from "../parse-error.ts";
import type Parser from "./index.ts";

const getOwn = <T extends {}>(object: T, key: keyof T) =>
Object.hasOwnProperty.call(object, key) && object[key];
Object.hasOwn(object, key) && object[key];

const unwrapParenthesizedExpression = (node: Node): Node => {
return node.type === "ParenthesizedExpression"
Expand Down
4 changes: 2 additions & 2 deletions packages/babel-parser/src/plugins/typescript/index.ts
Expand Up @@ -31,7 +31,7 @@ import type { IJSXParserMixin } from "../jsx/index.ts";
import { ParseBindingListFlags } from "../../parser/lval.ts";

const getOwn = <T extends {}>(object: T, key: keyof T) =>
Object.hasOwnProperty.call(object, key) && object[key];
Object.hasOwn(object, key) && object[key];

type TsModifier =
| "readonly"
Expand Down Expand Up @@ -409,7 +409,7 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>

enforceOrder(startLoc, modifier, "in", "out");
} else {
if (Object.hasOwnProperty.call(modified, modifier)) {
if (Object.hasOwn(modified, modifier)) {
this.raise(TSErrors.DuplicateModifier, startLoc, { modifier });
} else {
enforceOrder(startLoc, modifier, "static", "readonly");
Expand Down
14 changes: 5 additions & 9 deletions packages/babel-plugin-transform-runtime/src/index.ts
Expand Up @@ -55,11 +55,7 @@ export default declare((api, options: Options, dirname) => {
var supportsCJSDefault = hasMinVersion(DUAL_MODE_RUNTIME, runtimeVersion);
}

function has(obj: {}, key: string) {
return Object.prototype.hasOwnProperty.call(obj, key);
}

if (has(options, "useBuiltIns")) {
if (Object.hasOwn(options, "useBuiltIns")) {
// @ts-expect-error deprecated options
if (options["useBuiltIns"]) {
throw new Error(
Expand All @@ -74,7 +70,7 @@ export default declare((api, options: Options, dirname) => {
}
}

if (has(options, "polyfill")) {
if (Object.hasOwn(options, "polyfill")) {
// @ts-expect-error deprecated options
if (options["polyfill"] === false) {
throw new Error(
Expand All @@ -89,7 +85,7 @@ export default declare((api, options: Options, dirname) => {
}
}

if (has(options, "moduleName")) {
if (Object.hasOwn(options, "moduleName")) {
throw new Error(
"The 'moduleName' option has been removed. @babel/transform-runtime " +
"no longer supports arbitrary runtimes. If you were using this to " +
Expand All @@ -99,7 +95,7 @@ export default declare((api, options: Options, dirname) => {
}

if (process.env.BABEL_8_BREAKING) {
if (has(options, "regenerator")) {
if (Object.hasOwn(options, "regenerator")) {
throw new Error(
"The 'regenerator' option has been removed. The generators transform " +
"no longers relies on a 'regeneratorRuntime' global. " +
Expand All @@ -110,7 +106,7 @@ export default declare((api, options: Options, dirname) => {
}

if (process.env.BABEL_8_BREAKING) {
if (has(options, "useESModules")) {
if (Object.hasOwn(options, "useESModules")) {
throw new Error(
"The 'useESModules' option has been removed. @babel/runtime now uses " +
"package.json#exports to support both CommonJS and ESM helpers.",
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-preset-env/src/debug.ts
Expand Up @@ -22,7 +22,7 @@ export const logPlugin = (
const proposalName = `proposal-${item.slice(10)}`;
if (
proposalName === "proposal-dynamic-import" ||
Object.prototype.hasOwnProperty.call(compatData, proposalName)
Object.hasOwn(compatData, proposalName)
) {
item = proposalName;
}
Expand Down
4 changes: 1 addition & 3 deletions packages/babel-preset-env/src/filter-items.ts
@@ -1,8 +1,6 @@
import semver from "semver";
import { minVersions } from "./available-plugins.ts";

const has = Function.call.bind(Object.hasOwnProperty);

export function addProposalSyntaxPlugins(
items: Set<string>,
proposalSyntaxPlugins: readonly string[],
Expand All @@ -25,7 +23,7 @@ export function removeUnsupportedItems(
) {
items.forEach(item => {
if (
has(minVersions, item) &&
Object.hasOwn(minVersions, item) &&
semver.lt(
babelVersion,
// @ts-expect-error we have checked minVersions[item] in has call
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-preset-env/src/plugins-compat-data.ts
Expand Up @@ -18,7 +18,7 @@ function filterAvailable<Data extends { [name: string]: unknown }>(
): { [Name in keyof Data & keyof typeof availablePlugins]: Data[Name] } {
const result = {} as any;
for (const plugin of keys(data)) {
if (Object.hasOwnProperty.call(availablePlugins, plugin)) {
if (Object.hasOwn(availablePlugins, plugin)) {
result[plugin] = data[plugin];
}
}
Expand Down
8 changes: 4 additions & 4 deletions packages/babel-standalone/src/index.ts
Expand Up @@ -94,7 +94,7 @@ const isArray =
*/
function loadBuiltin(builtinTable: Record<string, unknown>, name: any) {
if (isArray(name) && typeof name[0] === "string") {
if (Object.prototype.hasOwnProperty.call(builtinTable, name[0])) {
if (Object.hasOwn(builtinTable, name[0])) {
return [builtinTable[name[0]]].concat(name.slice(1));
}
return;
Expand All @@ -120,7 +120,7 @@ function processOptions(options: InputOptions) {
if (
isArray(preset) &&
typeof preset[0] === "object" &&
Object.prototype.hasOwnProperty.call(preset[0], "buildPreset")
Object.hasOwn(preset[0], "buildPreset")
) {
preset[0] = { ...preset[0], buildPreset: preset[0].buildPreset };
}
Expand Down Expand Up @@ -169,7 +169,7 @@ export const buildExternalHelpers = babelBuildExternalHelpers;
* Registers a named plugin for use with Babel.
*/
export function registerPlugin(name: string, plugin: () => PluginObject): void {
if (Object.prototype.hasOwnProperty.call(availablePlugins, name)) {
if (Object.hasOwn(availablePlugins, name)) {
console.warn(
`A plugin named "${name}" is already registered, it will be overridden`,
);
Expand All @@ -192,7 +192,7 @@ export function registerPlugins(newPlugins: {
* Registers a named preset for use with Babel.
*/
export function registerPreset(name: string, preset: () => PresetObject): void {
if (Object.prototype.hasOwnProperty.call(availablePresets, name)) {
if (Object.hasOwn(availablePresets, name)) {
if (name === "env") {
console.warn(
"@babel/preset-env is now included in @babel/standalone, please remove @babel/preset-env-standalone",
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-template/src/literal.ts
Expand Up @@ -22,7 +22,7 @@ export default function literalTemplate<T>(

if (replacements) {
Object.keys(replacements).forEach(key => {
if (Object.prototype.hasOwnProperty.call(defaultReplacements, key)) {
if (Object.hasOwn(defaultReplacements, key)) {
throw new Error("Unexpected replacement overlap.");
}
});
Expand Down
4 changes: 1 addition & 3 deletions packages/babel-template/src/populate.ts
Expand Up @@ -22,9 +22,7 @@ export default function populatePlaceholders(

if (replacements) {
metadata.placeholders.forEach(placeholder => {
if (
!Object.prototype.hasOwnProperty.call(replacements, placeholder.name)
) {
if (!Object.hasOwn(replacements, placeholder.name)) {
const placeholderName = placeholder.name;

throw new Error(
Expand Down
3 changes: 1 addition & 2 deletions packages/babel-traverse/src/path/evaluation.ts
Expand Up @@ -445,8 +445,7 @@ function _evaluate(path: NodePath, state: State): any {
) {
context = global[object.node.name];
const key = property.node.name;
// TODO(Babel 8): Use Object.hasOwn
if (Object.hasOwnProperty.call(context, key)) {
if (Object.hasOwn(context, key)) {
func = context[key as keyof typeof context];
}
}
Expand Down
22 changes: 12 additions & 10 deletions packages/babel-types/src/clone/cloneNode.ts
Expand Up @@ -2,7 +2,9 @@ import { NODE_FIELDS } from "../definitions/index.ts";
import type * as t from "../index.ts";
import { isFile, isIdentifier } from "../validators/generated/index.ts";

const has = Function.call.bind(Object.prototype.hasOwnProperty);
const { hasOwn } = process.env.BABEL_8_BREAKING
? Object
: { hasOwn: Function.call.bind(Object.prototype.hasOwnProperty) };

type CommentCache = Map<t.Comment, t.Comment>;

Expand Down Expand Up @@ -60,11 +62,11 @@ function cloneNodeInternal<T extends t.Node>(
if (isIdentifier(node)) {
newNode.name = node.name;

if (has(node, "optional") && typeof node.optional === "boolean") {
if (hasOwn(node, "optional") && typeof node.optional === "boolean") {
newNode.optional = node.optional;
}

if (has(node, "typeAnnotation")) {
if (hasOwn(node, "typeAnnotation")) {
newNode.typeAnnotation = deep
? cloneIfNodeOrArray(
node.typeAnnotation,
Expand All @@ -74,11 +76,11 @@ function cloneNodeInternal<T extends t.Node>(
)
: node.typeAnnotation;
}
} else if (!has(NODE_FIELDS, type)) {
} else if (!hasOwn(NODE_FIELDS, type)) {
throw new Error(`Unknown node type: "${type}"`);
} else {
for (const field of Object.keys(NODE_FIELDS[type])) {
if (has(node, field)) {
if (hasOwn(node, field)) {
if (deep) {
newNode[field] =
isFile(node) && field === "comments"
Expand All @@ -104,38 +106,38 @@ function cloneNodeInternal<T extends t.Node>(
}
}

if (has(node, "loc")) {
if (hasOwn(node, "loc")) {
if (withoutLoc) {
newNode.loc = null;
} else {
newNode.loc = node.loc;
}
}
if (has(node, "leadingComments")) {
if (hasOwn(node, "leadingComments")) {
newNode.leadingComments = maybeCloneComments(
node.leadingComments,
deep,
withoutLoc,
commentsCache,
);
}
if (has(node, "innerComments")) {
if (hasOwn(node, "innerComments")) {
newNode.innerComments = maybeCloneComments(
node.innerComments,
deep,
withoutLoc,
commentsCache,
);
}
if (has(node, "trailingComments")) {
if (hasOwn(node, "trailingComments")) {
newNode.trailingComments = maybeCloneComments(
node.trailingComments,
deep,
withoutLoc,
commentsCache,
);
}
if (has(node, "extra")) {
if (hasOwn(node, "extra")) {
newNode.extra = {
...node.extra,
};
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-types/src/definitions/placeholders.ts
Expand Up @@ -25,7 +25,7 @@ export const PLACEHOLDERS_FLIPPED_ALIAS: Record<string, string[]> = {};

Object.keys(PLACEHOLDERS_ALIAS).forEach(type => {
PLACEHOLDERS_ALIAS[type].forEach(alias => {
if (!Object.hasOwnProperty.call(PLACEHOLDERS_FLIPPED_ALIAS, alias)) {
if (!Object.hasOwn(PLACEHOLDERS_FLIPPED_ALIAS, alias)) {
PLACEHOLDERS_FLIPPED_ALIAS[alias] = [];
}
PLACEHOLDERS_FLIPPED_ALIAS[alias].push(type);
Expand Down

0 comments on commit 08a057c

Please sign in to comment.