From 9bacc757723291f16ccdc0ea99524f42978e8921 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Sun, 18 Sep 2022 17:50:14 +0800 Subject: [PATCH] Use `.at()` and `Object.hasOwn()` (#13482) --- .eslintrc.cjs | 8 +- jest.config.mjs | 5 +- package.json | 1 + scripts/build/bundler.mjs | 6 + scripts/build/esbuild-plugins/evaluate.mjs | 2 +- .../build/esbuild-plugins/interop-default.mjs | 4 +- .../build/esbuild-plugins/replace-module.mjs | 2 +- scripts/build/shims/at.js | 13 ++ scripts/build/transform/index.js | 113 ++++++++++++++++++ .../consistent-negative-index-access.js | 71 ----------- .../index.js | 1 - .../test.js | 44 ------- scripts/utils/index.mjs | 2 + src/cli/context.js | 3 +- src/cli/utils.js | 3 +- src/common/ast-path.js | 19 ++- src/common/util.js | 4 - src/document/invalid-doc-error.js | 3 +- src/document/printer.js | 7 +- src/document/utils.js | 9 +- src/index.js | 2 - src/language-css/clean.js | 3 +- src/language-css/loc.js | 3 +- src/language-css/parser-postcss.js | 7 +- src/language-css/printer-postcss.js | 7 +- src/language-css/utils/print-unit.js | 4 +- src/language-handlebars/utils.js | 8 +- src/language-html/parser-html.js | 3 +- src/language-js/comments/handle-comments.js | 5 +- src/language-js/parse/postprocess/index.js | 7 +- src/language-js/print/array.js | 4 +- src/language-js/print/binaryish.js | 3 +- src/language-js/print/call-arguments.js | 5 +- src/language-js/print/function.js | 3 +- src/language-js/print/jsx.js | 12 +- src/language-js/print/member-chain.js | 11 +- src/language-js/print/object.js | 5 +- src/language-js/print/template-literal.js | 3 +- src/language-js/utils/index.js | 7 +- src/language-markdown/print-preprocess.js | 3 +- src/language-markdown/printer-markdown.js | 9 +- .../unified-plugins/loose-items.js | 2 +- src/language-markdown/utils.js | 20 +++- .../print/flow-mapping-sequence.js | 4 +- src/language-yaml/utils.js | 29 ++--- src/main/massage-ast.js | 5 +- src/main/options-normalizer.js | 3 +- src/main/parser.js | 2 +- src/utils/get-last.js | 3 - website/pages/en/index.js | 2 +- yarn.lock | 13 +- 51 files changed, 254 insertions(+), 263 deletions(-) create mode 100644 scripts/build/shims/at.js create mode 100644 scripts/build/transform/index.js delete mode 100644 scripts/tools/eslint-plugin-prettier-internal-rules/consistent-negative-index-access.js delete mode 100644 src/utils/get-last.js diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 17f07742f..5e7840478 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -82,6 +82,7 @@ module.exports = { enforceForRenamedProperties: false, }, ], + "prefer-object-has-own": "error", "prefer-object-spread": "error", "prefer-rest-params": "error", "prefer-spread": "error", @@ -164,6 +165,12 @@ module.exports = { ], "unicorn/prefer-array-flat-map": "error", "unicorn/prefer-array-some": "error", + "unicorn/prefer-at": [ + "error", + { + getLastElementFunctions: ["getLast"], + }, + ], "unicorn/prefer-export-from": [ "error", { @@ -301,7 +308,6 @@ module.exports = { { files: "src/**/*.js", rules: { - "prettier-internal-rules/consistent-negative-index-access": "error", "prettier-internal-rules/flat-ast-path-call": "error", "prettier-internal-rules/no-conflicting-comment-check-flags": "error", "prettier-internal-rules/no-doc-index-import": "error", diff --git a/jest.config.mjs b/jest.config.mjs index 7c83ce15d..59bf58c96 100644 --- a/jest.config.mjs +++ b/jest.config.mjs @@ -25,7 +25,10 @@ const testPathIgnorePatterns = []; if (TEST_STANDALONE) { testPathIgnorePatterns.push("/tests/integration/"); } -if (!isProduction) { +if (isProduction) { + // Only run unit test for development + testPathIgnorePatterns.push("/tests/unit/"); +} else { // Only test bundles for production testPathIgnorePatterns.push( "/tests/integration/__tests__/bundle.js" diff --git a/package.json b/package.json index 8f6bb5781..c4edb9e03 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "yaml-unist-parser": "1.3.1" }, "devDependencies": { + "@babel/generator": "7.19.0", "@esbuild-plugins/node-modules-polyfill": "0.1.4", "@glimmer/reference": "0.84.2", "@types/estree": "1.0.0", diff --git a/scripts/build/bundler.mjs b/scripts/build/bundler.mjs index 326eb603e..5e5d527e7 100644 --- a/scripts/build/bundler.mjs +++ b/scripts/build/bundler.mjs @@ -14,6 +14,7 @@ import esbuildPluginStripNodeProtocol from "./esbuild-plugins/strip-node-protoco import esbuildPluginThrowWarnings from "./esbuild-plugins/throw-warnings.mjs"; import esbuildPluginShimCommonjsObjects from "./esbuild-plugins/shim-commonjs-objects.mjs"; import bundles from "./config.mjs"; +import transform from "./transform/index.js"; const { dirname, readJsonSync, require } = createEsmUtils(import.meta); const packageJson = readJsonSync("../../package.json"); @@ -42,6 +43,11 @@ function* getEsbuildOptions(bundle, buildOptions) { find: "const __dirname = path.dirname(fileURLToPath(import.meta.url));", replacement: "", }, + // Transform `.at` and `Object.hasOwn` + { + module: "*", + process: transform, + }, // #12493, not sure what the problem is, but replace the cjs version with esm version seems fix it { module: require.resolve("tslib"), diff --git a/scripts/build/esbuild-plugins/evaluate.mjs b/scripts/build/esbuild-plugins/evaluate.mjs index ef9d02c07..df58f7d17 100644 --- a/scripts/build/esbuild-plugins/evaluate.mjs +++ b/scripts/build/esbuild-plugins/evaluate.mjs @@ -10,7 +10,7 @@ export default function esbuildPluginEvaluate() { build.onLoad({ filter: /\.evaluate\.js$/ }, async ({ path }) => { let data = await importModule(path); - if (Object.prototype.hasOwnProperty.call(data, "default")) { + if (Object.hasOwn(data, "default")) { if (Object.keys(data).length !== 1) { throw new TypeError("Mixed export not allowed."); } diff --git a/scripts/build/esbuild-plugins/interop-default.mjs b/scripts/build/esbuild-plugins/interop-default.mjs index e2df70f49..d9970bdaa 100644 --- a/scripts/build/esbuild-plugins/interop-default.mjs +++ b/scripts/build/esbuild-plugins/interop-default.mjs @@ -16,9 +16,9 @@ export default function esbuildPluginInteropDefault() { const text = fs.readFileSync(file, "utf8").trim(); const lines = text.split("\n"); if ( - lines[lines.length - 2] !== + lines.at(-2) !== "// Annotate the CommonJS export names for ESM import in node:" || - lines[lines.length - 1] !== "0 && (module.exports = {});" + lines.at(-1) !== "0 && (module.exports = {});" ) { throw new Error("Unexpected output."); } diff --git a/scripts/build/esbuild-plugins/replace-module.mjs b/scripts/build/esbuild-plugins/replace-module.mjs index de129a524..5351c5afe 100644 --- a/scripts/build/esbuild-plugins/replace-module.mjs +++ b/scripts/build/esbuild-plugins/replace-module.mjs @@ -174,7 +174,7 @@ function setupOnLoadListener(build, { concepts, replacements, processors }) { let text = original; for (const process of processFunctions) { - text = process(text); + text = process(text, file); } // For files not JavaScript, we need add correct `loader` to the result, diff --git a/scripts/build/shims/at.js b/scripts/build/shims/at.js new file mode 100644 index 000000000..f3f7ba2ff --- /dev/null +++ b/scripts/build/shims/at.js @@ -0,0 +1,13 @@ +/* +Note: + +1. `acorn` contains `RegExpValidationState` class with `.at` method (2 arguments) +2. `postcss` contains `Container` class with `.at` method +*/ + +const at = (object, index, ...args) => + object.at + ? object.at(index, ...args) + : object[index < 0 ? object.length + index : index]; + +export default at; diff --git a/scripts/build/transform/index.js b/scripts/build/transform/index.js new file mode 100644 index 000000000..d5a3b4a80 --- /dev/null +++ b/scripts/build/transform/index.js @@ -0,0 +1,113 @@ +import { fileURLToPath } from "node:url"; +import { parse } from "@babel/parser"; +import { traverseFast as traverse } from "@babel/types"; +import babelGenerator from "@babel/generator"; +import { outdent } from "outdent"; +import { SOURCE_DIR } from "../../utils/index.mjs"; + +const generate = babelGenerator.default; +const atHelperPath = fileURLToPath(new URL("../shims/at.js", import.meta.url)); + +/* Doesn't work for dependencies, optional chaining, and spread arguments */ + +// `Object.hasOwn(foo, "bar")` -> `Object.prototype.hasOwnProperty.call(foo, "bar")` +function transformObjectHasOwnCall(node) { + if ( + !( + node.type === "CallExpression" && + !node.optional && + node.arguments.length === 2 && + node.arguments.every(({ type }) => type !== "SpreadElement") && + node.callee.type === "MemberExpression" && + !node.callee.optional && + !node.callee.computed && + node.callee.object.type === "Identifier" && + node.callee.object.name === "Object" && + node.callee.property.type === "Identifier" && + node.callee.property.name === "hasOwn" + ) + ) { + return false; + } + + node.callee = { + type: "MemberExpression", + object: { + type: "MemberExpression", + object: { + type: "MemberExpression", + object: { type: "Identifier", name: "Object" }, + property: { type: "Identifier", name: "prototype" }, + }, + property: { type: "Identifier", name: "hasOwnProperty" }, + }, + property: { type: "Identifier", name: "call" }, + }; + + return true; +} + +// `foo.at(index)` -> `__at(foo, index)` +function transformRelativeIndexingCall(node) { + if ( + !( + node.type === "CallExpression" && + !node.optional && + node.arguments.length === 1 && + node.arguments.every(({ type }) => type !== "SpreadElement") && + node.callee.type === "MemberExpression" && + !node.callee.optional && + !node.callee.computed && + node.callee.object.type !== "ThisExpression" && + node.callee.property.type === "Identifier" && + node.callee.property.name === "at" + ) + ) { + return false; + } + + node.arguments.unshift(node.callee.object); + node.callee = { type: "Identifier", name: "__at" }; + + return true; +} + +function transform(original, file) { + if ( + file === atHelperPath || + !file.startsWith(SOURCE_DIR) || + (!original.includes(".at(") && !original.includes("Object.hasOwn(")) + ) { + return original; + } + + let changed = false; + let shouldInjectRelativeIndexingHelper = false; + const ast = parse(original, { sourceType: "module" }); + traverse(ast, (node) => { + const hasObjectHasOwnCall = transformObjectHasOwnCall(node); + changed ||= hasObjectHasOwnCall; + + const hasRelativeIndexingCall = transformRelativeIndexingCall(node); + shouldInjectRelativeIndexingHelper ||= hasRelativeIndexingCall; + changed ||= hasRelativeIndexingCall; + }); + + if (!changed) { + return original; + } + + let { code } = generate(ast); + + if (shouldInjectRelativeIndexingHelper) { + code = outdent` + import __at from ${JSON.stringify(atHelperPath)}; + + ${code} + `; + } + + return code; +} + +export default transform; diff --git a/scripts/tools/eslint-plugin-prettier-internal-rules/consistent-negative-index-access.js b/scripts/tools/eslint-plugin-prettier-internal-rules/consistent-negative-index-access.js deleted file mode 100644 index 50f304941..000000000 --- a/scripts/tools/eslint-plugin-prettier-internal-rules/consistent-negative-index-access.js +++ /dev/null @@ -1,71 +0,0 @@ -"use strict"; - -const selector = [ - "MemberExpression", - "[computed=true]", - "[optional=false]", - '[property.type="BinaryExpression"]', - '[property.operator="-"]', - '[property.left.type="MemberExpression"]', - "[property.left.optional=false]", - "[property.left.computed=false]", - '[property.left.property.type="Identifier"]', - '[property.left.property.name="length"]', - '[property.right.type="Literal"]', - `:not(${[ - "AssignmentExpression > .left", - "UpdateExpression > .argument", - // Ignore `getPenultimate` and `getLast` function self - 'VariableDeclarator[id.name="getPenultimate"] > ArrowFunctionExpression.init *', - 'VariableDeclarator[id.name="getLast"] > ArrowFunctionExpression.init *', - ].join(", ")})`, -].join(""); - -const messageId = "consistent-negative-index-access"; - -module.exports = { - meta: { - type: "suggestion", - docs: { - url: "https://github.com/prettier/prettier/blob/main/scripts/tools/eslint-plugin-prettier-internal-rules/consistent-negative-index-access.js", - }, - messages: { - [messageId]: "Prefer `{{method}}(…)` over `…[….length - {{index}}]`.", - }, - fixable: "code", - }, - create(context) { - const sourceCode = context.getSourceCode(); - - return { - [selector](node) { - const { value: index } = node.property.right; - - if (index !== 1 && index !== 2) { - return; - } - - const { object } = node; - const lengthObject = node.property.left.object; - - const objectText = sourceCode.getText(object); - // Simply use text to compare object - if (sourceCode.getText(lengthObject) !== objectText) { - return; - } - - const method = ["getLast", "getPenultimate"][index - 1]; - - context.report({ - node, - messageId, - data: { - index, - method, - }, - fix: (fixer) => fixer.replaceText(node, `${method}(${objectText})`), - }); - }, - }; - }, -}; diff --git a/scripts/tools/eslint-plugin-prettier-internal-rules/index.js b/scripts/tools/eslint-plugin-prettier-internal-rules/index.js index a144df845..11b8117c0 100644 --- a/scripts/tools/eslint-plugin-prettier-internal-rules/index.js +++ b/scripts/tools/eslint-plugin-prettier-internal-rules/index.js @@ -4,7 +4,6 @@ module.exports = { rules: { "await-cli-tests": require("./await-cli-tests.js"), "better-parent-property-check-in-needs-parens": require("./better-parent-property-check-in-needs-parens.js"), - "consistent-negative-index-access": require("./consistent-negative-index-access.js"), "directly-loc-start-end": require("./directly-loc-start-end.js"), "flat-ast-path-call": require("./flat-ast-path-call.js"), "jsx-identifier-case": require("./jsx-identifier-case.js"), diff --git a/scripts/tools/eslint-plugin-prettier-internal-rules/test.js b/scripts/tools/eslint-plugin-prettier-internal-rules/test.js index d984ea566..c16fd8aa6 100644 --- a/scripts/tools/eslint-plugin-prettier-internal-rules/test.js +++ b/scripts/tools/eslint-plugin-prettier-internal-rules/test.js @@ -79,50 +79,6 @@ test("better-parent-property-check-in-needs-parens", { })), }); -test("consistent-negative-index-access", { - valid: [ - "getLast(foo)", - "getPenultimate(foo)", - "foo[foo.length]", - "foo[foo.length - 3]", - "foo[foo.length + 1]", - "foo[foo.length + -1]", - "foo[foo.length * -1]", - "foo.length - 1", - "foo?.[foo.length - 1]", - "foo[foo?.length - 1]", - "foo[foo['length'] - 1]", - "foo[bar.length - 1]", - "foo.bar[foo. bar.length - 1]", - "foo[foo.length - 1]++", - "--foo[foo.length - 1]", - "foo[foo.length - 1] += 1", - "foo[foo.length - 1] = 1", - ], - invalid: [ - { - code: "foo[foo.length - 1]", - output: "getLast(foo)", - errors: 1, - }, - { - code: "foo[foo.length - 2]", - output: "getPenultimate(foo)", - errors: 1, - }, - { - code: "foo[foo.length - 0b10]", - output: "getPenultimate(foo)", - errors: 1, - }, - { - code: "foo()[foo().length - 1]", - output: "getLast(foo())", - errors: 1, - }, - ], -}); - test("directly-loc-start-end", { valid: [], invalid: [ diff --git a/scripts/utils/index.mjs b/scripts/utils/index.mjs index 9e9e1c5a5..3691051ce 100644 --- a/scripts/utils/index.mjs +++ b/scripts/utils/index.mjs @@ -32,11 +32,13 @@ async function writeFile(file, content) { const PROJECT_ROOT = url.fileURLToPath(new URL("../../", import.meta.url)); const DIST_DIR = path.join(PROJECT_ROOT, "dist"); const WEBSITE_DIR = path.join(PROJECT_ROOT, "website"); +const SOURCE_DIR = path.join(PROJECT_ROOT, "src"); export { PROJECT_ROOT, DIST_DIR, WEBSITE_DIR, + SOURCE_DIR, readJson, writeJson, writeFile, diff --git a/src/cli/context.js b/src/cli/context.js index 324e0991d..77854a29d 100644 --- a/src/cli/context.js +++ b/src/cli/context.js @@ -1,4 +1,3 @@ -import { getLast } from "./utils.js"; import { getContextOptions } from "./options/get-context-options.js"; import { parseArgv, @@ -55,7 +54,7 @@ class Context { popContextPlugins() { this.#stack.pop(); - Object.assign(this, getLast(this.#stack)); + Object.assign(this, this.#stack.at(-1)); } // eslint-disable-next-line getter-return diff --git a/src/cli/utils.js b/src/cli/utils.js index 46b412508..df86621e2 100644 --- a/src/cli/utils.js +++ b/src/cli/utils.js @@ -2,7 +2,7 @@ import fs from "node:fs/promises"; import sdbm from "sdbm"; import { __internal as sharedWithCli } from "../index.js"; -const { arrayify, getLast, isNonEmptyArray, partition } = sharedWithCli.utils; +const { arrayify, isNonEmptyArray, partition } = sharedWithCli.utils; // eslint-disable-next-line no-console const printToScreen = console.log.bind(console); @@ -81,7 +81,6 @@ function isJson(value) { export { arrayify, - getLast, isNonEmptyArray, partition, printToScreen, diff --git a/src/common/ast-path.js b/src/common/ast-path.js index d4a3bf7e6..cd8b725a0 100644 --- a/src/common/ast-path.js +++ b/src/common/ast-path.js @@ -1,6 +1,3 @@ -import getLast from "../utils/get-last.js"; -import { getPenultimate } from "./util.js"; - class AstPath { constructor(value) { this.stack = [value]; @@ -8,15 +5,15 @@ class AstPath { get key() { const { stack, siblings } = this; - return stack[stack.length - (siblings === null ? 2 : 4)] ?? null; + return stack.at(siblings === null ? -2 : -4) ?? null; } get index() { - return this.siblings === null ? null : getPenultimate(this.stack); + return this.siblings === null ? null : this.stack.at(-2); } get node() { - return getLast(this.stack); + return this.stack.at(-1); } get parent() { @@ -33,7 +30,7 @@ class AstPath { get siblings() { const { stack } = this; - const maybeArray = stack[stack.length - 3]; + const maybeArray = stack.at(-3); return Array.isArray(maybeArray) ? maybeArray : null; } @@ -66,7 +63,7 @@ class AstPath { const { stack } = this; const { length } = stack; if (length > 1) { - return stack[length - 2]; + return stack.at(-2); } // Since the name is a string/number/symbol, null is a safe sentinel value // to return if we do not know the name of the (root) value. @@ -77,7 +74,7 @@ class AstPath { // The value of the current property is always the final element of // this.stack. getValue() { - return getLast(this.stack); + return this.stack.at(-1); } getNode(count = 0) { @@ -107,7 +104,7 @@ class AstPath { call(callback, ...names) { const { stack } = this; const { length } = stack; - let value = getLast(stack); + let value = stack.at(-1); for (const name of names) { value = value[name]; @@ -137,7 +134,7 @@ class AstPath { each(callback, ...names) { const { stack } = this; const { length } = stack; - let value = getLast(stack); + let value = stack.at(-1); for (const name of names) { value = value[name]; diff --git a/src/common/util.js b/src/common/util.js index ac2b2b015..4571581d1 100644 --- a/src/common/util.js +++ b/src/common/util.js @@ -11,8 +11,6 @@ import skipTrailingComment from "../utils/text/skip-trailing-comment.js"; import skipNewline from "../utils/text/skip-newline.js"; import getNextNonSpaceNonCommentCharacterIndexWithStartIndex from "../utils/text/get-next-non-space-non-comment-character-index-with-start-index.js"; -const getPenultimate = (arr) => arr[arr.length - 2]; - /** * @typedef {{backwards?: boolean}} SkipOptions */ @@ -478,7 +476,6 @@ export { inferParserByLanguage, getMaxContinuousCount, getMinNotPresentContinuousCount, - getPenultimate, getNextNonSpaceNonCommentCharacterIndexWithStartIndex, getNextNonSpaceNonCommentCharacterIndex, getNextNonSpaceNonCommentCharacter, @@ -508,6 +505,5 @@ export { isFrontMatterNode, createGroupIdMapper, }; -export { default as getLast } from "../utils/get-last.js"; export { default as isNonEmptyArray } from "../utils/is-non-empty-array.js"; export { default as getStringWidth } from "../utils/get-string-width.js"; diff --git a/src/document/invalid-doc-error.js b/src/document/invalid-doc-error.js index 9cb303d66..d30b358eb 100644 --- a/src/document/invalid-doc-error.js +++ b/src/document/invalid-doc-error.js @@ -1,11 +1,10 @@ -import getLast from "../utils/get-last.js"; import { VALID_OBJECT_DOC_TYPES } from "./constants.js"; import getDocType from "./utils/get-doc-type.js"; // TODO: Use `new Intl.ListFormat("en-US", { type: "disjunction" }).format()` when we drop support for iOS 12.5 // Only works for array with more than 1 elements const disjunctionListFormat = (list) => - [...list.slice(0, -1), `or ${getLast(list)}`].join(", "); + [...list.slice(0, -1), `or ${list.at(-1)}`].join(", "); function getDocErrorMessage(doc) { const type = doc === null ? null : typeof doc; diff --git a/src/document/printer.js b/src/document/printer.js index 0a9adec3d..64773f299 100644 --- a/src/document/printer.js +++ b/src/document/printer.js @@ -1,5 +1,4 @@ import { convertEndOfLineToChars } from "../common/end-of-line.js"; -import getLast from "../utils/get-last.js"; import getStringWidth from "../utils/get-string-width.js"; import { DOC_TYPE_STRING, @@ -253,7 +252,7 @@ function fits( // The most expanded state takes up the least space on the current line. const contents = doc.expandedStates && groupMode === MODE_BREAK - ? getLast(doc.expandedStates) + ? doc.expandedStates.at(-1) : doc.contents; cmds.push({ mode: groupMode, doc: contents }); break; @@ -389,7 +388,7 @@ function printDocToString(doc, options) { // group has these, we need to manually go through // these states and find the first one that fits. if (doc.expandedStates) { - const mostExpanded = getLast(doc.expandedStates); + const mostExpanded = doc.expandedStates.at(-1); if (doc.break) { cmds.push({ ind, mode: MODE_BREAK, doc: mostExpanded }); @@ -423,7 +422,7 @@ function printDocToString(doc, options) { } if (doc.id) { - groupModeMap[doc.id] = getLast(cmds).mode; + groupModeMap[doc.id] = cmds.at(-1).mode; } break; // Fills each line with as much code as possible before moving to a new diff --git a/src/document/utils.js b/src/document/utils.js index d26cdc8b5..be8abcae6 100644 --- a/src/document/utils.js +++ b/src/document/utils.js @@ -1,4 +1,3 @@ -import getLast from "../utils/get-last.js"; import { DOC_TYPE_STRING, DOC_TYPE_ARRAY, @@ -141,7 +140,7 @@ function willBreak(doc) { function breakParentGroup(groupStack) { if (groupStack.length > 0) { - const parentGroup = getLast(groupStack); + const parentGroup = groupStack.at(-1); // Breaks are not propagated through conditional groups because // the user is expected to manually handle what breaks. if (!parentGroup.expandedStates && !parentGroup.break) { @@ -223,7 +222,7 @@ function stripDocTrailingHardlineFromDoc(doc) { } if (parts.length > 0) { - const lastPart = stripDocTrailingHardlineFromDoc(getLast(parts)); + const lastPart = stripDocTrailingHardlineFromDoc(parts.at(-1)); parts[parts.length - 1] = lastPart; } return Array.isArray(doc) ? parts : { ...doc, parts }; @@ -302,7 +301,7 @@ function cleanDocFn(doc) { const [currentPart, ...restParts] = Array.isArray(part) ? part : [part]; if ( typeof currentPart === "string" && - typeof getLast(parts) === "string" + typeof parts.at(-1) === "string" ) { parts[parts.length - 1] += currentPart; } else { @@ -362,7 +361,7 @@ function normalizeParts(parts) { if ( newParts.length > 0 && - typeof getLast(newParts) === "string" && + typeof newParts.at(-1) === "string" && typeof part === "string" ) { newParts[newParts.length - 1] += part; diff --git a/src/index.js b/src/index.js index 8e67bb3e1..4e3ce0f8e 100644 --- a/src/index.js +++ b/src/index.js @@ -20,7 +20,6 @@ import { normalizeCliOptions, } from "./main/options-normalizer.js"; import arrayify from "./utils/arrayify.js"; -import getLast from "./utils/get-last.js"; import partition from "./utils/partition.js"; import { isNonEmptyArray } from "./common/util.js"; @@ -86,7 +85,6 @@ const sharedWithCli = { getSupportInfoWithoutPlugins, utils: { arrayify, - getLast, isNonEmptyArray, partition, }, diff --git a/src/language-css/clean.js b/src/language-css/clean.js index 3ab984a08..71c52aaed 100644 --- a/src/language-css/clean.js +++ b/src/language-css/clean.js @@ -1,5 +1,4 @@ import { isFrontMatterNode } from "../common/util.js"; -import getLast from "../utils/get-last.js"; const ignoredProperties = new Set([ "raw", // front-matter @@ -42,7 +41,7 @@ function clean(ast, newObj, parent) { } // Last comment is not parsed, when omitting semicolon, #8675 - if (parent.type === "css-root" && getLast(parent.nodes) === ast) { + if (parent.type === "css-root" && parent.nodes.at(-1) === ast) { return null; } } diff --git a/src/language-css/loc.js b/src/language-css/loc.js index 4344d1eec..1cfed06a0 100644 --- a/src/language-css/loc.js +++ b/src/language-css/loc.js @@ -1,5 +1,4 @@ import { skipEverythingButNewLine } from "../utils/text/skip.js"; -import getLast from "../utils/get-last.js"; import isNonEmptyArray from "../utils/is-non-empty-array.js"; import lineColumnToIndex from "../utils/line-column-to-index.js"; @@ -50,7 +49,7 @@ function calculateLocEnd(node, text) { } if (isNonEmptyArray(node.nodes)) { - return calculateLocEnd(getLast(node.nodes), text); + return calculateLocEnd(node.nodes.at(-1), text); } } diff --git a/src/language-css/parser-postcss.js b/src/language-css/parser-postcss.js index 0efe505ba..835a19ad3 100644 --- a/src/language-css/parser-postcss.js +++ b/src/language-css/parser-postcss.js @@ -1,5 +1,4 @@ import createError from "../common/parser-create-error.js"; -import getLast from "../utils/get-last.js"; import parseFrontMatter from "../utils/front-matter/parse.js"; import { hasPragma } from "./pragma.js"; import { locStart, locEnd } from "./loc.js"; @@ -41,7 +40,7 @@ async function parseValueNode(valueNode, options) { options.parser === "scss" && node.type === "number" && node.unit === ".." && - getLast(node.value) === "." + node.value.endsWith(".") ) { // Work around postcss bug parsing `50...` as `50.` with unit `..` // Set the unit to `...` to "accidentally" have arbitrary arguments work in the same way that cases where the node already had a unit work. @@ -113,11 +112,11 @@ async function parseValueNode(valueNode, options) { } commaGroupStack.pop(); - commaGroup = getLast(commaGroupStack); + commaGroup = commaGroupStack.at(-1); commaGroup.groups.push(parenGroup); parenGroupStack.pop(); - parenGroup = getLast(parenGroupStack); + parenGroup = parenGroupStack.at(-1); } else if (node.type === "comma") { parenGroup.groups.push(commaGroup); commaGroup = { diff --git a/src/language-css/printer-postcss.js b/src/language-css/printer-postcss.js index a4b75d5da..828519c88 100644 --- a/src/language-css/printer-postcss.js +++ b/src/language-css/printer-postcss.js @@ -1,4 +1,3 @@ -import getLast from "../utils/get-last.js"; import { printNumber, printString, @@ -891,7 +890,7 @@ function genericPrint(path, options, print) { const isSCSSMapItem = isSCSSMapItemNode(path, options); - const lastItem = getLast(node.groups); + const lastItem = node.groups.at(-1); const isLastItemComment = lastItem && lastItem.type === "value-comment"; const isKey = isKeyInValuePairNode(node, parentNode); const isConfiguration = isConfigurationNode(node, parentNode); @@ -941,7 +940,7 @@ function genericPrint(path, options, print) { child.type === "value-comma_group" && isNonEmptyArray(child.groups) ) { - const last = getLast(child.groups); + const last = child.groups.at(-1); if ( // `value-paren_group` missing location info last.source && @@ -1007,7 +1006,7 @@ function genericPrint(path, options, print) { // Don't add spaces on escaped colon `:`, e.g: grid-template-rows: [row-1-00\:00] auto; (prevNode && typeof prevNode.value === "string" && - getLast(prevNode.value) === "\\") || + prevNode.value.endsWith("\\")) || // Don't add spaces on `:` in `url` function (i.e. `url(fbglyph: cross-outline, fig-white)`) insideValueFunctionNode(path, "url") ? "" diff --git a/src/language-css/utils/print-unit.js b/src/language-css/utils/print-unit.js index 05ffa5d04..d90fa0c58 100644 --- a/src/language-css/utils/print-unit.js +++ b/src/language-css/utils/print-unit.js @@ -3,9 +3,7 @@ import CSS_UNITS from "./css-units.evaluate.js"; function printUnit(unit) { const lowercased = unit.toLowerCase(); - return Object.prototype.hasOwnProperty.call(CSS_UNITS, lowercased) - ? CSS_UNITS[lowercased] - : unit; + return Object.hasOwn(CSS_UNITS, lowercased) ? CSS_UNITS[lowercased] : unit; } export default printUnit; diff --git a/src/language-handlebars/utils.js b/src/language-handlebars/utils.js index 1ca435cf3..16b9d832c 100644 --- a/src/language-handlebars/utils.js +++ b/src/language-handlebars/utils.js @@ -1,5 +1,4 @@ import { htmlVoidElements } from "html-void-elements"; -import getLast from "../utils/get-last.js"; function isLastNodeOfSiblings(path) { const { node } = path; @@ -7,15 +6,12 @@ function isLastNodeOfSiblings(path) { if ( isParentOfSomeType(path, ["ElementNode"]) && - getLast(parentNode.children) === node + parentNode.children.at(-1) === node ) { return true; } - if ( - isParentOfSomeType(path, ["Block"]) && - getLast(parentNode.body) === node - ) { + if (isParentOfSomeType(path, ["Block"]) && parentNode.body.at(-1) === node) { return true; } diff --git a/src/language-html/parser-html.js b/src/language-html/parser-html.js index 47ed42cec..1a359e443 100644 --- a/src/language-html/parser-html.js +++ b/src/language-html/parser-html.js @@ -1,5 +1,4 @@ import parseFrontMatter from "../utils/front-matter/parse.js"; -import getLast from "../utils/get-last.js"; import createError from "../common/parser-create-error.js"; import { inferParserByLanguage } from "../common/util.js"; import HTML_TAGS from "./utils/html-tag-names.js"; @@ -342,7 +341,7 @@ async function _parse( subAst.sourceSpan = new ParseSourceSpan( startSpan, // @ts-expect-error - getLast(subAst.children).sourceSpan.end + subAst.children.at(-1).sourceSpan.end ); // @ts-expect-error const firstText = subAst.children[0]; diff --git a/src/language-js/comments/handle-comments.js b/src/language-js/comments/handle-comments.js index a41948cf2..14984e4c0 100644 --- a/src/language-js/comments/handle-comments.js +++ b/src/language-js/comments/handle-comments.js @@ -1,5 +1,4 @@ import { - getLast, hasNewline, getNextNonSpaceNonCommentCharacterIndexWithStartIndex, getNextNonSpaceNonCommentCharacter, @@ -394,7 +393,7 @@ function handleClassComments({ isNonEmptyArray(enclosingNode.decorators) && !(followingNode && followingNode.type === "Decorator") ) { - addTrailingComment(getLast(enclosingNode.decorators), comment); + addTrailingComment(enclosingNode.decorators.at(-1), comment); return true; } @@ -589,7 +588,7 @@ function handleLastFunctionArgComments({ if (parameters.length > 0) { return getNextNonSpaceNonCommentCharacterIndexWithStartIndex( text, - locEnd(getLast(parameters)) + locEnd(parameters.at(-1)) ); } const functionParamLeftParenIndex = diff --git a/src/language-js/parse/postprocess/index.js b/src/language-js/parse/postprocess/index.js index 94b6d2bca..12d9f393d 100644 --- a/src/language-js/parse/postprocess/index.js +++ b/src/language-js/parse/postprocess/index.js @@ -1,7 +1,6 @@ import { locStart, locEnd } from "../../loc.js"; import isTsKeywordType from "../../utils/is-ts-keyword-type.js"; import isTypeCastComment from "../../utils/is-type-cast-comment.js"; -import getLast from "../../../utils/get-last.js"; import isNonEmptyArray from "../../../utils/is-non-empty-array.js"; import isBlockComment from "../../utils/is-block-comment.js"; import isIndentableBlockComment from "../../utils/is-indentable-block-comment.js"; @@ -75,7 +74,7 @@ function postprocess(ast, options) { } // fix unexpected locEnd caused by --no-semi style case "VariableDeclaration": { - const lastDeclaration = getLast(node.declarations); + const lastDeclaration = node.declarations.at(-1); if (lastDeclaration && lastDeclaration.init) { overrideLocEnd(node, lastDeclaration); } @@ -120,7 +119,7 @@ function postprocess(ast, options) { case "SequenceExpression": { // Babel (unlike other parsers) includes spaces and comments in the range. Let's unify this. - const lastExpression = getLast(node.expressions); + const lastExpression = node.expressions.at(-1); node.range = [ locStart(node), Math.min(locEnd(lastExpression), locEnd(node)), @@ -158,7 +157,7 @@ function postprocess(ast, options) { }); if (isNonEmptyArray(ast.comments)) { - let followingComment = getLast(ast.comments); + let followingComment = ast.comments.at(-1); for (let i = ast.comments.length - 2; i >= 0; i--) { const comment = ast.comments[i]; if ( diff --git a/src/language-js/print/array.js b/src/language-js/print/array.js index 4ecb26944..4b6d658af 100644 --- a/src/language-js/print/array.js +++ b/src/language-js/print/array.js @@ -8,7 +8,7 @@ import { ifBreak, fill, } from "../../document/builders.js"; -import { getLast, hasNewline } from "../../common/util.js"; +import { hasNewline } from "../../common/util.js"; import { shouldPrintComma, hasComment, @@ -44,7 +44,7 @@ function printArray(path, options, print) { ); } } else { - const lastElem = getLast(node.elements); + const lastElem = node.elements.at(-1); const canHaveTrailingComma = !(lastElem && lastElem.type === "RestElement"); // JavaScript allows you to have empty elements in an array which diff --git a/src/language-js/print/binaryish.js b/src/language-js/print/binaryish.js index 9a22d25b0..fe9de443e 100644 --- a/src/language-js/print/binaryish.js +++ b/src/language-js/print/binaryish.js @@ -1,5 +1,4 @@ import { printComments } from "../../main/comments.js"; -import { getLast } from "../../common/util.js"; import { DOC_TYPE_FILL, DOC_TYPE_GROUP } from "../../document/constants.js"; import { join, @@ -170,7 +169,7 @@ function printBinaryishExpression(path, options, print) { return chain; } - const jsxPart = getLast(parts); + const jsxPart = parts.at(-1); return group([chain, indentIfBreak(jsxPart, { groupId })]); } diff --git a/src/language-js/print/call-arguments.js b/src/language-js/print/call-arguments.js index 60e7a7535..b68c7dd61 100644 --- a/src/language-js/print/call-arguments.js +++ b/src/language-js/print/call-arguments.js @@ -1,5 +1,4 @@ import { printDanglingComments } from "../../main/comments.js"; -import { getLast, getPenultimate } from "../../common/util.js"; import { getFunctionParameters, hasComment, @@ -223,8 +222,8 @@ function couldExpandArg(arg, arrowChainRecursion = false) { } function shouldExpandLastArg(args, options) { - const lastArg = getLast(args); - const penultimateArg = getPenultimate(args); + const lastArg = args.at(-1); + const penultimateArg = args.at(-2); return ( !hasComment(lastArg, CommentCheckFlags.Leading) && !hasComment(lastArg, CommentCheckFlags.Trailing) && diff --git a/src/language-js/print/function.js b/src/language-js/print/function.js index 83e3b77dc..9a6118a57 100644 --- a/src/language-js/print/function.js +++ b/src/language-js/print/function.js @@ -5,7 +5,6 @@ import { printDanglingComments, printCommentsSeparately, } from "../../main/comments.js"; -import getLast from "../../utils/get-last.js"; import { getNextNonSpaceNonCommentCharacterIndex } from "../../common/util.js"; import { line, @@ -477,7 +476,7 @@ function printReturnOrThrowArgument(path, options, print) { } const comments = getComments(node); - const lastComment = getLast(comments); + const lastComment = comments.at(-1); const isLastCommentLine = lastComment && isLineComment(lastComment); if (isLastCommentLine) { diff --git a/src/language-js/print/jsx.js b/src/language-js/print/jsx.js index b9808dffb..6823847d3 100644 --- a/src/language-js/print/jsx.js +++ b/src/language-js/print/jsx.js @@ -14,7 +14,7 @@ import { import { willBreak } from "../../document/utils.js"; import UnexpectedNodeError from "../../utils/unexpected-node-error.js"; -import { getLast, getPreferredQuote } from "../../common/util.js"; +import { getPreferredQuote } from "../../common/util.js"; import { isJsxNode, rawText, @@ -174,7 +174,7 @@ function printJsxElementInternal(path, options, print) { } // Trim trailing lines (or empty strings) - while (children.length > 0 && isEmptyStringOrAnyLine(getLast(children))) { + while (children.length > 0 && isEmptyStringOrAnyLine(children.at(-1))) { children.pop(); } @@ -301,7 +301,7 @@ function printJsxChildren( let endWhitespace; // Ends with whitespace - if (getLast(words) === "") { + if (words.at(-1) === "") { words.pop(); endWhitespace = words.pop(); } @@ -325,7 +325,7 @@ function printJsxChildren( parts.push( separatorWithWhitespace( isFacebookTranslationTag, - getLast(parts), + parts.at(-1), child, next ) @@ -338,7 +338,7 @@ function printJsxChildren( parts.push( separatorNoWhitespace( isFacebookTranslationTag, - getLast(parts), + parts.at(-1), child, next ) @@ -614,7 +614,7 @@ function printEndOfOpeningTag(node, options, nameHasComments) { function shouldPrintBracketSameLine(node, options, nameHasComments) { const lastAttrHasTrailingComments = node.attributes.length > 0 && - hasComment(getLast(node.attributes), CommentCheckFlags.Trailing); + hasComment(node.attributes.at(-1), CommentCheckFlags.Trailing); return ( // Simple tags (no attributes and no comment in tag name) should be // kept unbroken regardless of `bracketSameLine`. diff --git a/src/language-js/print/member-chain.js b/src/language-js/print/member-chain.js index 9bece6dbf..eb088d75e 100644 --- a/src/language-js/print/member-chain.js +++ b/src/language-js/print/member-chain.js @@ -1,6 +1,5 @@ import { printComments } from "../../main/comments.js"; import { - getLast, isNextLineEmptyAfterIndex, getNextNonSpaceNonCommentCharacterIndex, } from "../../common/util.js"; @@ -286,7 +285,7 @@ function printMemberChain(path, options, print) { ); } - const lastNode = getLast(groups[0]).node; + const lastNode = groups[0].at(-1).node; return ( isMemberExpression(lastNode) && lastNode.property.type === "Identifier" && @@ -303,7 +302,7 @@ function printMemberChain(path, options, print) { const printed = printedGroup.map((tuple) => tuple.printed); // Checks if the last node (i.e. the parent node) needs parens and print // accordingly - if (printedGroup.length > 0 && getLast(printedGroup).needsParens) { + if (printedGroup.length > 0 && printedGroup.at(-1).needsParens) { return ["(", ...printed, ")"]; } return printed; @@ -344,7 +343,7 @@ function printMemberChain(path, options, print) { // Find out the last node in the first group and check if it has an // empty line after - const lastNodeBeforeIndent = getLast(groups[shouldMerge ? 1 : 0]).node; + const lastNodeBeforeIndent = groups[shouldMerge ? 1 : 0].at(-1).node; const shouldHaveEmptyLineBeforeIndent = !isCallExpression(lastNodeBeforeIndent) && shouldInsertEmptyLineAfter(lastNodeBeforeIndent); @@ -361,8 +360,8 @@ function printMemberChain(path, options, print) { .filter(isCallExpression); function lastGroupWillBreakAndOtherCallsHaveFunctionArguments() { - const lastGroupNode = getLast(getLast(groups)).node; - const lastGroupDoc = getLast(printedGroups); + const lastGroupNode = groups.at(-1).at(-1).node; + const lastGroupDoc = printedGroups.at(-1); return ( isCallExpression(lastGroupNode) && willBreak(lastGroupDoc) && diff --git a/src/language-js/print/object.js b/src/language-js/print/object.js index d0e550af5..0d345bb55 100644 --- a/src/language-js/print/object.js +++ b/src/language-js/print/object.js @@ -8,7 +8,6 @@ import { hardline, } from "../../document/builders.js"; import { - getLast, hasNewlineInRange, hasNewline, isNonEmptyArray, @@ -137,7 +136,7 @@ function printObject(path, options, print) { printed = [ printedDanglingComments, hasLineComments || - hasNewline(options.originalText, locEnd(getLast(getComments(node)))) + hasNewline(options.originalText, locEnd(getComments(node).at(-1))) ? hardline : line, "...", @@ -148,7 +147,7 @@ function printObject(path, options, print) { props.push([...separatorParts, ...printed]); } - const lastElem = getLast(propsAndLoc)?.node; + const lastElem = propsAndLoc.at(-1)?.node; const canHaveTrailingSeparator = !( node.inexact || diff --git a/src/language-js/print/template-literal.js b/src/language-js/print/template-literal.js index 6105ea415..7a578ceef 100644 --- a/src/language-js/print/template-literal.js +++ b/src/language-js/print/template-literal.js @@ -1,4 +1,3 @@ -import getLast from "../../utils/get-last.js"; import { getStringWidth, getIndentSize } from "../../common/util.js"; import { join, @@ -137,7 +136,7 @@ function printJestEachTemplateLiteral(path, options, print) { const tableBody = [{ hasLineBreak: false, cells: [] }]; for (let i = 1; i < node.quasis.length; i++) { - const row = getLast(tableBody); + const row = tableBody.at(-1); const correspondingExpression = stringifiedExpressions[i - 1]; row.cells.push(correspondingExpression); diff --git a/src/language-js/utils/index.js b/src/language-js/utils/index.js index 3c6f548b5..40bebe83c 100644 --- a/src/language-js/utils/index.js +++ b/src/language-js/utils/index.js @@ -1,6 +1,5 @@ import esutils from "esutils"; import { - getLast, hasNewline, skipWhitespace, isNonEmptyArray, @@ -768,8 +767,8 @@ function needsHardlineAfterDanglingComment(node) { if (!hasComment(node)) { return false; } - const lastDanglingComment = getLast( - getComments(node, CommentCheckFlags.Dangling) + const lastDanglingComment = getComments(node, CommentCheckFlags.Dangling).at( + -1 ); return lastDanglingComment && !isBlockComment(lastDanglingComment); } @@ -1106,7 +1105,7 @@ function hasRestParameter(node) { return true; } const parameters = getFunctionParameters(node); - return getLast(parameters)?.type === "RestElement"; + return parameters.at(-1)?.type === "RestElement"; } const functionParametersCache = new WeakMap(); diff --git a/src/language-markdown/print-preprocess.js b/src/language-markdown/print-preprocess.js index cfd903fe9..2e5c22846 100644 --- a/src/language-markdown/print-preprocess.js +++ b/src/language-markdown/print-preprocess.js @@ -1,4 +1,3 @@ -import getLast from "../utils/get-last.js"; import { getOrderedListItemInfo, mapAst, splitText } from "./utils.js"; // 0x0 ~ 0x10ffff @@ -76,7 +75,7 @@ function mergeChildren(ast, shouldMerge, mergeNode) { return node; } const children = node.children.reduce((current, child) => { - const lastChild = getLast(current); + const lastChild = current.at(-1); if (lastChild && shouldMerge(lastChild, child)) { current.splice(-1, 1, mergeNode(lastChild, child)); } else { diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js index 615d3147f..c31d13dbf 100644 --- a/src/language-markdown/printer-markdown.js +++ b/src/language-markdown/printer-markdown.js @@ -1,5 +1,4 @@ import { - getLast, getMinNotPresentContinuousCount, getMaxContinuousCount, getStringWidth, @@ -155,8 +154,8 @@ function genericPrint(path, options, print) { const { previous, next } = path; const hasPrevOrNextWord = // `1*2*3` is considered emphasis but `1_2_3` is not (previous?.type === "sentence" && - getLast(previous.children)?.type === "word" && - !getLast(previous.children).hasTrailingPunctuation) || + previous.children.at(-1)?.type === "word" && + !previous.children.at(-1).hasTrailingPunctuation) || (next?.type === "sentence" && next.children[0]?.type === "word" && !next.children[0].hasLeadingPunctuation); @@ -262,7 +261,7 @@ function genericPrint(path, options, print) { case "html": { const { parent } = path; const value = - parent.type === "root" && getLast(parent.children) === node + parent.type === "root" && parent.children.at(-1) === node ? node.value.trimEnd() : node.value; const isHtmlComment = /^$/s.test(value); @@ -739,7 +738,7 @@ function printChildren(path, options, print, events = {}) { function getLastDescendantNode(node) { let current = node; while (isNonEmptyArray(current.children)) { - current = getLast(current.children); + current = current.children.at(-1); } return current; } diff --git a/src/language-markdown/unified-plugins/loose-items.js b/src/language-markdown/unified-plugins/loose-items.js index c718a2462..01e98cfc9 100644 --- a/src/language-markdown/unified-plugins/loose-items.js +++ b/src/language-markdown/unified-plugins/loose-items.js @@ -4,7 +4,7 @@ function looseItems() { function fixListNodes(value, node, parent) { if (node.type === "listItem") { - node.loose = node.spread || value.charAt(value.length - 1) === "\n"; + node.loose = node.spread || value.endsWith("\n"); if (node.loose) { parent.loose = true; } diff --git a/src/language-markdown/utils.js b/src/language-markdown/utils.js index c8561376b..fa52c1b80 100644 --- a/src/language-markdown/utils.js +++ b/src/language-markdown/utils.js @@ -1,4 +1,3 @@ -import { getLast } from "../common/util.js"; import { locStart, locEnd } from "./loc.js"; import { cjkPattern, @@ -47,7 +46,18 @@ function splitText(text, options) { const KIND_K_LETTER = "k-letter"; const KIND_CJK_PUNCTUATION = "cjk-punctuation"; - /** @type {Array<{ type: "whitespace", value: " " | "\n" | "" } | { type: "word", value: string }>} */ + /** + * @type {Array< + * { type: "whitespace", value: " " | "\n" | "" } + * | { + * type: "word", + * value: string, + * kind: string, + * hasLeadingPunctuation: boolean, + * hasTrailingPunctuation: boolean + * } + * >} + */ const nodes = []; const tokens = ( @@ -91,7 +101,7 @@ function splitText(text, options) { value: innerToken, kind: KIND_NON_CJK, hasLeadingPunctuation: punctuationRegex.test(innerToken[0]), - hasTrailingPunctuation: punctuationRegex.test(getLast(innerToken)), + hasTrailingPunctuation: punctuationRegex.test(innerToken.at(-1)), }); } continue; @@ -121,7 +131,7 @@ function splitText(text, options) { return nodes; function appendNode(node) { - const lastNode = getLast(nodes); + const lastNode = nodes.at(-1); if (lastNode && lastNode.type === "word") { if ( (lastNode.kind === KIND_NON_CJK && @@ -144,7 +154,9 @@ function splitText(text, options) { function isBetween(kind1, kind2) { return ( + // @ts-expect-error (lastNode.kind === kind1 && node.kind === kind2) || + // @ts-expect-error (lastNode.kind === kind2 && node.kind === kind1) ); } diff --git a/src/language-yaml/print/flow-mapping-sequence.js b/src/language-yaml/print/flow-mapping-sequence.js index d1bf05629..cf2dac3dc 100644 --- a/src/language-yaml/print/flow-mapping-sequence.js +++ b/src/language-yaml/print/flow-mapping-sequence.js @@ -5,7 +5,7 @@ import { hardline, join, } from "../../document/builders.js"; -import { isEmptyNode, getLast, hasEndComments } from "../utils.js"; +import { isEmptyNode, hasEndComments } from "../utils.js"; import { printNextEmptyLine, alignWithSpaces } from "./misc.js"; function printFlowMapping(path, print, options) { @@ -19,7 +19,7 @@ function printFlowMapping(path, print, options) { if (isMapping && node.children.length > 0 && options.bracketSpacing) { bracketSpacing = line; } - const lastItem = getLast(node.children); + const lastItem = node.children.at(-1); const isLastItemEmptyMappingItem = lastItem && lastItem.type === "flowMappingItem" && diff --git a/src/language-yaml/utils.js b/src/language-yaml/utils.js index 0d8f4caa9..9d5c22f6e 100644 --- a/src/language-yaml/utils.js +++ b/src/language-yaml/utils.js @@ -1,4 +1,4 @@ -import { getLast, isNonEmptyArray } from "../common/util.js"; +import { isNonEmptyArray } from "../common/util.js"; function getAncestorCount(path, filter) { let counter = 0; @@ -97,7 +97,7 @@ function isLastDescendantNode(path) { function getLastDescendantNode(node) { return isNonEmptyArray(node.children) - ? getLastDescendantNode(getLast(node.children)) + ? getLastDescendantNode(node.children.at(-1)) : node; } @@ -112,12 +112,12 @@ function hasPrettierIgnore(path) { const document = path.getParentNode(); return ( hasEndComments(document.head) && - isPrettierIgnore(getLast(document.head.endComments)) + isPrettierIgnore(document.head.endComments.at(-1)) ); } return ( - hasLeadingComments(node) && isPrettierIgnore(getLast(node.leadingComments)) + hasLeadingComments(node) && isPrettierIgnore(node.leadingComments.at(-1)) ); } @@ -220,15 +220,9 @@ function getFlowScalarLineContents(nodeType, content, options) { lineContentWords.length > 0 && !( // trailing backslash in quoteDouble should be preserved - ( - nodeType === "quoteDouble" && - getLast(getLast(reduced)).endsWith("\\") - ) + (nodeType === "quoteDouble" && reduced.at(-1).at(-1).endsWith("\\")) ) - ? [ - ...reduced.slice(0, -1), - [...getLast(reduced), ...lineContentWords], - ] + ? [...reduced.slice(0, -1), [...reduced.at(-1), ...lineContentWords]] : [...reduced, lineContentWords], [] ) @@ -284,10 +278,10 @@ function getBlockValueLineContents( rawLineContents[index - 1].length > 0 && lineContentWords.length > 0 && !/^\s/.test(lineContentWords[0]) && - !/^\s|\s$/.test(getLast(reduced)) + !/^\s|\s$/.test(reduced.at(-1)) ? [ ...reduced.slice(0, -1), - [...getLast(reduced), ...lineContentWords], + [...reduced.at(-1), ...lineContentWords], ] : [...reduced, lineContentWords], [] @@ -296,8 +290,8 @@ function getBlockValueLineContents( lineContentWords.reduce( (reduced, word) => // disallow trailing spaces - reduced.length > 0 && /\s$/.test(getLast(reduced)) - ? [...reduced.slice(0, -1), getLast(reduced) + " " + word] + reduced.length > 0 && /\s$/.test(reduced.at(-1)) + ? [...reduced.slice(0, -1), reduced.at(-1) + " " + word] : [...reduced, word], [] ) @@ -311,7 +305,7 @@ function getBlockValueLineContents( function removeUnnecessaryTrailingNewlines(lineContents) { if (node.chomping === "keep") { - return getLast(lineContents).length === 0 + return lineContents.at(-1).length === 0 ? lineContents.slice(0, -1) : lineContents; } @@ -354,7 +348,6 @@ function isInlineNode(node) { } export { - getLast, getAncestorCount, isNode, isEmptyNode, diff --git a/src/main/massage-ast.js b/src/main/massage-ast.js index 858c392d7..6f5022c49 100644 --- a/src/main/massage-ast.js +++ b/src/main/massage-ast.js @@ -29,10 +29,7 @@ function massageAST(ast, options) { const newObj = {}; const childrenKeys = new Set(getVisitorKeys(node)); for (const key in node) { - if ( - !Object.prototype.hasOwnProperty.call(node, key) || - ignoredProperties.has(key) - ) { + if (!Object.hasOwn(node, key) || ignoredProperties.has(key)) { continue; } diff --git a/src/main/options-normalizer.js b/src/main/options-normalizer.js index 0f8c42344..0f7228eec 100644 --- a/src/main/options-normalizer.js +++ b/src/main/options-normalizer.js @@ -1,5 +1,4 @@ import vnopts from "vnopts"; -import getLast from "../utils/get-last.js"; /** * @typedef {import("./support").NamedOptionInfo} NamedOptionInfo @@ -272,7 +271,7 @@ function optionInfoToSchema( const originalPreprocess = parameters.preprocess || ((x) => x); parameters.preprocess = (value, schema, utils) => schema.preprocess( - originalPreprocess(Array.isArray(value) ? getLast(value) : value), + originalPreprocess(Array.isArray(value) ? value.at(-1) : value), utils ); } diff --git a/src/main/parser.js b/src/main/parser.js index 998d4962e..6410c06e1 100644 --- a/src/main/parser.js +++ b/src/main/parser.js @@ -2,7 +2,7 @@ import { ConfigError } from "../common/errors.js"; function resolveParser({ plugins, parser }) { for (const { parsers } of plugins) { - if (parsers && Object.prototype.hasOwnProperty.call(parsers, parser)) { + if (parsers && Object.hasOwn(parsers, parser)) { return parsers[parser]; } } diff --git a/src/utils/get-last.js b/src/utils/get-last.js deleted file mode 100644 index 170e21fe8..000000000 --- a/src/utils/get-last.js +++ /dev/null @@ -1,3 +0,0 @@ -const getLast = (arr) => arr[arr.length - 1]; - -export default getLast; diff --git a/website/pages/en/index.js b/website/pages/en/index.js index 389f50181..540100d3f 100755 --- a/website/pages/en/index.js +++ b/website/pages/en/index.js @@ -155,7 +155,7 @@ Language.propTypes = { const LanguagesSection = () => { const languageChunks = siteConfig.supportedLanguages.reduce( (acc, language) => { - const last = acc[acc.length - 1]; + const last = acc.at(-1); if ( last && last.length < 2 && diff --git a/yarn.lock b/yarn.lock index 8ad3273ac..06d32d25c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -79,14 +79,14 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.18.10, @babel/generator@npm:^7.7.2": - version: 7.18.12 - resolution: "@babel/generator@npm:7.18.12" +"@babel/generator@npm:7.19.0, @babel/generator@npm:^7.18.10, @babel/generator@npm:^7.7.2": + version: 7.19.0 + resolution: "@babel/generator@npm:7.19.0" dependencies: - "@babel/types": ^7.18.10 + "@babel/types": ^7.19.0 "@jridgewell/gen-mapping": ^0.3.2 jsesc: ^2.5.1 - checksum: 07dd71d255144bb703a80ab0156c35d64172ce81ddfb70ff24e2be687b052080233840c9a28d92fa2c33f7ecb8a8b30aef03b807518afc53b74c7908bf8859b1 + checksum: aa3d5785cf8f8e81672dcc61aef351188efeadb20d9f66d79113d82cbcf3bbbdeb829989fa14582108572ddbc4e4027bdceb06ccaf5ec40fa93c2dda8fbcd4aa languageName: node linkType: hard @@ -415,7 +415,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:7.19.0, @babel/types@npm:^7.0.0, @babel/types@npm:^7.18.10, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": +"@babel/types@npm:7.19.0, @babel/types@npm:^7.0.0, @babel/types@npm:^7.18.10, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.19.0, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": version: 7.19.0 resolution: "@babel/types@npm:7.19.0" dependencies: @@ -6579,6 +6579,7 @@ __metadata: dependencies: "@angular/compiler": 12.2.16 "@babel/code-frame": 7.18.6 + "@babel/generator": 7.19.0 "@babel/parser": 7.19.0 "@babel/types": 7.19.0 "@esbuild-plugins/node-modules-polyfill": 0.1.4