Repository: prettier/plugin-php
Pull request #2181 (2023-05-29 23:57:30)

build(deps-dev): bump strip-ansi from 6.0.1 to 7.1.0
Bumps [strip-ansi](https://github.com/chalk/strip-ansi) from 6.0.1 to 7.1.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/chalk/strip-ansi/releases">strip-ansi's releases</a>.</em></p>
<blockquote>
<h2>v7.1.0</h2>
<ul>
<li>Improve performance (<a href="https://redirect.github.com/chalk/strip-ansi/issues/49">#49</a>)  840a5ea</li>
</ul>
<p><a href="https://github.com/chalk/strip-ansi/compare/v7.0.1...v7.1.0">https://github.com/chalk/strip-ansi/compare/v7.0.1...v7.1.0</a></p>
<h2>v7.0.1</h2>
<ul>
<li>Upgrade dependencies  ed41f38</li>
</ul>
<p><a href="https://github.com/chalk/strip-ansi/compare/v7.0.0...v7.0.1">https://github.com/chalk/strip-ansi/compare/v7.0.0...v7.0.1</a></p>
<h2>v7.0.0</h2>
<h3>Breaking</h3>
<ul>
<li>Require Node.js 12  7cda68d</li>
<li>This package is now pure ESM. Please <a href="https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c">read this</a>.</li>
</ul>
<p><a href="https://github.com/chalk/strip-ansi/compare/v6.0.0...v7.0.0">https://github.com/chalk/strip-ansi/compare/v6.0.0...v7.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/chalk/strip-ansi/commit/1fdc531d4046cbaa830460f5c74452bf1f0a0884"><code>1fdc531</code></a> 7.1.0</li>
<li><a href="https://github.com/chalk/strip-ansi/commit/840a5ea748d34ca914ab2aaa9755c4fb8e9ac115"><code>840a5ea</code></a> Improve performance (<a href="https://redirect.github.com/chalk/strip-ansi/issues/49">#49</a>)</li>
<li><a href="https://github.com/chalk/strip-ansi/commit/2b8c961e75760059699373f9a69101065c3ded3a"><code>2b8c961</code></a> Fix CI</li>
<li><a href="https://github.com/chalk/strip-ansi/commit/dd40fa7ced678f14dfb43eb9b62b8e7313fb7011"><code>dd40fa7</code></a> 7.0.1</li>
<li><a href="https://github.com/chalk/strip-ansi/commit/ed41f3819c88c30bf04656bd759ab21f481293c3"><code>ed41f38</code></a> Upgrade dependencies</li>
<li><a href="https://github.com/chalk/strip-ansi/commit/316ff53b074b4b1465e18a35e5d792b27ecc2865"><code>316ff53</code></a> 7.0.0</li>
<li><a href="https://github.com/chalk/strip-ansi/commit/7cda68dcadde18b19bfa31b6223e9f0e60b3e319"><code>7cda68d</code></a> Require Node.js 12 and move to ESM</li>
<li><a href="https://github.com/chalk/strip-ansi/commit/d6d11286da3320f52e4c34106db04e353a0a5e4f"><code>d6d1128</code></a> Add <a href="https://github.com/Qix"><code>@​Qix</code></a>- to funding.yml</li>
<li><a href="https://github.com/chalk/strip-ansi/commit/601423d8b8e9b5613d0ed9eb1927a99be3042f15"><code>601423d</code></a> Move to GitHub Actions</li>
<li><a href="https://github.com/chalk/strip-ansi/commit/0a6e98da48c269bce7b69fbe287fc6f86d0c7b8b"><code>0a6e98d</code></a> Add Node.js 14 to testing matrix (<a href="https://redirect.github.com/chalk/strip-ansi/issues/35">#35</a>)</li>
<li>See full diff in <a href="https://github.com/chalk/strip-ansi/compare/v6.0.1...v7.1.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=strip-ansi&package-manager=npm_and_yarn&previous-version=6.0.1&new-version=7.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>
> **Note**
> Automatic rebases have been disabled on this pull request as it has been open for over 30 days.

fisker: @dependabot rebase

Patch:
diff --git a/.eslintrc.yml b/.eslintrc.yml
index f62d294..404667b 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -6,19 +6,17 @@ extends:
   - plugin:prettier-doc/recommended
 parserOptions:
   ecmaVersion: 2023
+  sourceType: "module"
 plugins:
   - import
 root: true
 env:
-  es6: true
+  es2024: true
   node: true
   jest: true
 rules:
   eqeqeq: error
   curly: error
-  import/no-extraneous-dependencies:
-    - error
-    - devDependencies: ["tests*/**", "scripts/**", "build/**"]
   no-else-return: error
   no-inner-declarations: error
   no-useless-return: error
@@ -38,13 +36,17 @@ rules:
     - error
     - never
     - exceptRange: true
+
+  import/extensions: 
+    - error
+    - ignorePackages
+  import/no-extraneous-dependencies:
+    - error
+    - devDependencies: ["tests*/**", "scripts/**", "build/**"]
 overrides:
-  - files: "{tests,tests_config}/**/*.js"
+  - files: "**/*.cjs"
     parserOptions:
-      sourceType: "module"
-    rules:
-      # Turned off temporarily since we add a package.json without dependencies in these dirs
-      import/no-extraneous-dependencies: off
+      sourceType: "script"
   - files: "tests/**/*.js"
     globals:
       run_spec: true
diff --git a/README.md b/README.md
index 2a15c55..938cbc1 100644
--- a/README.md
+++ b/README.md
@@ -139,7 +139,7 @@ First, grab both standalone scripts from an npm CDN like [unpkg](https://unpkg.c
 Then use Prettier with PHP, just like this:
 
 ```js
-prettier.format(YOUR_CODE, {
+await prettier.format(YOUR_CODE, {
   plugins: prettierPlugins,
   parser: "php",
 });
@@ -153,10 +153,10 @@ Bundlers like webpack, Rollup or browserify automatically recognize how to handl
 
 ```js
 import prettier from "prettier/standalone";
-import phpPlugin from "@prettier/plugin-php/standalone";
+import * as prettierPluginPhp from "@prettier/plugin-php/standalone";
 
-prettier.format(YOUR_CODE, {
-  plugins: [phpPlugin],
+await prettier.format(YOUR_CODE, {
+  plugins: [prettierPluginPhp],
   parser: "php",
 });
 ```
diff --git a/build/rollup.config.js b/build/rollup.config.js
index c6c483f..68171f9 100644
--- a/build/rollup.config.js
+++ b/build/rollup.config.js
@@ -1,7 +1,8 @@
-import { resolve } from "path";
+import { resolve, dirname } from "path";
+import url from "url";
 
-import nodeResolve from "rollup-plugin-node-resolve";
-import commonjs from "rollup-plugin-commonjs";
+import nodeResolve from "@rollup/plugin-node-resolve";
+import commonjs from "@rollup/plugin-commonjs";
 import alias from "rollup-plugin-alias";
 import inject from "rollup-plugin-inject";
 import replace from "rollup-plugin-replace";
@@ -9,6 +10,7 @@ import babel from "rollup-plugin-babel";
 import json from "rollup-plugin-json";
 import { terser } from "rollup-plugin-terser";
 
+const __dirname = dirname(url.fileURLToPath(import.meta.url));
 const SRC_DIR = resolve(__dirname, "..", "src");
 const BUILD_DIR = resolve(__dirname, "..", "build");
 
@@ -50,7 +52,7 @@ export default {
       compact: false,
       presets: [
         [
-          require.resolve("@babel/preset-env"),
+          "@babel/preset-env",
           {
             targets: { browsers: [">0.25%", "not ie 11", "not op_mini all"] },
             modules: false,
diff --git a/build/shims/assert.js b/build/shims/assert.js
index f774c3f..97ea2db 100644
--- a/build/shims/assert.js
+++ b/build/shims/assert.js
@@ -1,4 +1,3 @@
-"use strict";
-function assert() {}
-assert.strictEqual = function () {};
-module.exports = assert;
+const assert = new Proxy(() => {}, { get: () => assert });
+
+export default assert;
diff --git a/build/shims/buffer.js b/build/shims/buffer.js
index 933f9ee..789f28c 100644
--- a/build/shims/buffer.js
+++ b/build/shims/buffer.js
@@ -1,6 +1,5 @@
-"use strict";
-const Buffer = {};
-Buffer.isBuffer = function () {
-  return false;
+const Buffer = {
+  isBuffer: () => false,
 };
-module.exports = Buffer;
+
+export default Buffer;
diff --git a/jest.config.js b/jest.config.js
index 4c65d2f..f6ea52b 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,9 +1,7 @@
-"use strict";
-
 const ENABLE_COVERAGE = !!process.env.CI;
 const RUN_STANDALONE_TESTS = Boolean(process.env.RUN_STANDALONE_TESTS);
 
-module.exports = {
+export default {
   collectCoverage: ENABLE_COVERAGE,
   collectCoverageFrom: [
     "<rootDir>/src/**/*.js",
diff --git a/package.json b/package.json
index e01c72d..face1b7 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,17 @@
   "repository": "prettier/prettier-php",
   "author": "Lucas Azzola <@azz>",
   "license": "MIT",
-  "main": "src",
+  "type": "module",
+  "unpkg": "./standalone.js",
+  "browser": "./standalone.js",
+  "exports": {
+    ".": {
+      "browser": "./standalone.js",
+      "default": "./src/index.js"
+    },
+    "./standalone": "./standalone.js",
+    "./package.json": "./package.json"
+  },
   "files": [
     "src",
     "standalone.js"
@@ -17,6 +27,8 @@
   },
   "devDependencies": {
     "@babel/preset-env": "^7.22.10",
+    "@rollup/plugin-commonjs": "^25.0.4",
+    "@rollup/plugin-node-resolve": "^15.1.0",
     "codecov": "3.8.3",
     "cross-env": "^7.0.2",
     "eslint": "8.47.0",
@@ -34,13 +46,11 @@
     "rollup": "^2.75.7",
     "rollup-plugin-alias": "^2.0.0",
     "rollup-plugin-babel": "^4.3.2",
-    "rollup-plugin-commonjs": "^10.0.2",
     "rollup-plugin-inject": "^3.0.1",
     "rollup-plugin-json": "^4.0.0",
-    "rollup-plugin-node-resolve": "^5.2.0",
     "rollup-plugin-replace": "^2.1.0",
     "rollup-plugin-terser": "^7.0.2",
-    "strip-ansi": "^6.0.0"
+    "strip-ansi": "^7.1.0"
   },
   "peerDependencies": {
     "prettier": "^3.0.0"
diff --git a/src/clean.js b/src/clean.js
index 9d2d91f..dca8766 100644
--- a/src/clean.js
+++ b/src/clean.js
@@ -1,6 +1,4 @@
-"use strict";
-
-const util = require("./util");
+import { printNumber, normalizeMagicMethodName } from "./util.js";
 
 const ignoredProperties = new Set([
   "loc",
@@ -28,7 +26,7 @@ const ignoredProperties = new Set([
 function clean(node, newObj) {
   if (node.kind === "string") {
     // TODO if options are available in this method, replace with
-    // newObj.isDoubleQuote = !util.useSingleQuote(node, options);
+    // newObj.isDoubleQuote = !useSingleQuote(node, options);
     delete newObj.isDoubleQuote;
   }
 
@@ -66,7 +64,7 @@ function clean(node, newObj) {
 
   // Normalize numbers
   if (node.kind === "number") {
-    newObj.value = util.printNumber(node.value);
+    newObj.value = printNumber(node.value);
   }
 
   const statements = ["foreach", "for", "if", "while", "do"];
@@ -100,7 +98,7 @@ function clean(node, newObj) {
   }
 
   if (node.kind === "method" && node.name.kind === "identifier") {
-    newObj.name.name = util.normalizeMagicMethodName(newObj.name.name);
+    newObj.name.name = normalizeMagicMethodName(newObj.name.name);
   }
 
   if (node.kind === "noop") {
@@ -110,4 +108,4 @@ function clean(node, newObj) {
 
 clean.ignoredProperties = ignoredProperties;
 
-module.exports = clean;
+export default clean;
diff --git a/src/comments.js b/src/comments.js
index d0186fc..84db282 100644
--- a/src/comments.js
+++ b/src/comments.js
@@ -1,4 +1,10 @@
-"use strict";
+import { util as prettierUtil, doc } from "prettier";
+import {
+  getNextNonSpaceNonCommentCharacterIndex,
+  isNextLineEmpty,
+  isPreviousLineEmpty,
+  isLookupNode,
+} from "./util.js";
 
 const {
   addLeadingComment,
@@ -7,15 +13,9 @@ const {
   skipNewline,
   hasNewline,
   hasNewlineInRange,
-} = require("prettier").util;
+} = prettierUtil;
 const { join, indent, hardline, cursor, lineSuffix, breakParent } =
-  require("prettier").doc.builders;
-const {
-  getNextNonSpaceNonCommentCharacterIndex,
-  isNextLineEmpty,
-  isPreviousLineEmpty,
-  isLookupNode,
-} = require("./util");
+  doc.builders;
 
 /*
 Comment functions are meant to inspect various edge cases using given comment nodes,
@@ -822,11 +822,11 @@ function handleWhileComments(
   return false;
 }
 
-// https://github.com/prettier/prettier/blob/master/src/main/comments.js#L335
-function printComment(commentPath, options) {
-  const comment = commentPath.getValue();
+// https://github.com/prettier/prettier/blob/c01661f311a2e1e033f1f9cb127882cc13e293bd/src/main/comments/print.js#L23
+function printComment(path, options) {
+  const comment = path.node;
   comment.printed = true;
-  return options.printer.printComment(commentPath, options);
+  return options.printer.printComment(path, options);
 }
 
 // https://github.com/prettier/prettier/blob/master/src/main/comments.js#L440
@@ -838,15 +838,15 @@ function printDanglingComments(path, options, sameIndent, filter) {
     return "";
   }
 
-  path.each((commentPath) => {
-    const comment = commentPath.getValue();
+  path.each(() => {
+    const comment = path.node;
     if (
       comment &&
       !comment.leading &&
       !comment.trailing &&
       (!filter || filter(comment))
     ) {
-      parts.push(printComment(commentPath, options));
+      parts.push(printComment(path, options));
     }
   }, "comments");
 
@@ -917,21 +917,22 @@ function canAttachComment(node) {
 // Based on https://github.com/prettier/prettier/blob/master/src/main/comments.js
 // TODO remove after https://github.com/prettier/prettier/issues/5087
 function prependCursorPlaceholder(path, options, printed) {
-  if (path.getNode() === options.cursorNode && path.getValue()) {
+  const { node } = path;
+  if (node && node === options.cursorNode) {
     return [cursor, printed, cursor];
   }
 
   return printed;
 }
 
-function printLeadingComment(commentPath, print, options) {
-  const comment = commentPath.getValue();
-  const contents = printComment(commentPath, options);
+function printLeadingComment(path, print, options) {
+  const contents = printComment(path, options);
 
   if (!contents) {
     return "";
   }
 
+  const comment = path.node;
   const isBlock =
     options.printer.isBlockComment && options.printer.isBlockComment(comment);
 
@@ -949,12 +950,13 @@ function printLeadingComment(commentPath, print, options) {
   return [contents, hardline];
 }
 
-function printTrailingComment(commentPath, print, options) {
-  const comment = commentPath.getValue();
-  const contents = printComment(commentPath, options);
+function printTrailingComment(path, print, options) {
+  const contents = printComment(path, options);
   if (!contents) {
     return "";
   }
+
+  const comment = path.node;
   const isBlock =
     options.printer.isBlockComment && options.printer.isBlockComment(comment);
 
@@ -991,9 +993,9 @@ function printTrailingComment(commentPath, print, options) {
 }
 
 function printAllComments(path, print, options, needsSemi) {
-  const value = path.getValue();
+  const { node } = path;
   const printed = print(path);
-  const comments = value && value.comments;
+  const comments = node && node.comments;
 
   if (!comments || comments.length === 0) {
     return prependCursorPlaceholder(path, options, printed);
@@ -1002,12 +1004,11 @@ function printAllComments(path, print, options, needsSemi) {
   const leadingParts = [];
   const trailingParts = [needsSemi ? ";" : "", printed];
 
-  path.each((commentPath) => {
-    const comment = commentPath.getValue();
+  path.each(({ node: comment }) => {
     const { leading, trailing } = comment;
 
     if (leading) {
-      const contents = printLeadingComment(commentPath, print, options);
+      const contents = printLeadingComment(path, print, options);
       if (!contents) {
         return;
       }
@@ -1018,7 +1019,7 @@ function printAllComments(path, print, options, needsSemi) {
         leadingParts.push(hardline);
       }
     } else if (trailing) {
-      trailingParts.push(printTrailingComment(commentPath, print, options));
+      trailingParts.push(printTrailingComment(path, print, options));
     }
   }, "comments");
 
@@ -1029,7 +1030,7 @@ function printAllComments(path, print, options, needsSemi) {
   );
 }
 
-module.exports = {
+export {
   handleOwnLineComment,
   handleEndOfLineComment,
   handleRemainingComment,
diff --git a/src/index.js b/src/index.js
index 36e11cf..8a862e2 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,12 +1,23 @@
-"use strict";
-
-const parse = require("./parser");
-const print = require("./printer");
-const clean = require("./clean");
-const options = require("./options");
-const comments = require("./comments");
-const { join, hardline } = require("prettier").doc.builders;
-const { hasPragma, insertPragma } = require("./pragma");
+import { doc } from "prettier";
+import {
+  LINGUIST_LANGUAGES_PHP,
+  LINGUIST_LANGUAGES_HTML_PHP,
+} from "./linguist-languages.cjs";
+import parse from "./parser.js";
+import print from "./printer.js";
+import clean from "./clean.js";
+import options from "./options.js";
+import {
+  handleOwnLineComment,
+  handleEndOfLineComment,
+  handleRemainingComment,
+  getCommentChildNodes,
+  canAttachComment,
+  isBlockComment,
+} from "./comments.js";
+import { hasPragma, insertPragma } from "./pragma.js";
+
+const { join, hardline } = doc.builders;
 
 function createLanguage(linguistData, { extend, override }) {
   const language = {};
@@ -30,13 +41,13 @@ function createLanguage(linguistData, { extend, override }) {
 }
 
 const languages = [
-  createLanguage(require("linguist-languages/data/PHP"), {
+  createLanguage(LINGUIST_LANGUAGES_PHP, {
     override: {
       parsers: ["php"],
       vscodeLanguageIds: ["php"],
     },
   }),
-  createLanguage(require("linguist-languages/data/HTML+PHP"), {
+  createLanguage(LINGUIST_LANGUAGES_HTML_PHP, {
     override: {
       parsers: ["php"],
       vscodeLanguageIds: ["php"],
@@ -81,21 +92,21 @@ const printers = {
     getVisitorKeys,
     insertPragma,
     massageAstNode: clean,
-    getCommentChildNodes: comments.getCommentChildNodes,
-    canAttachComment: comments.canAttachComment,
-    isBlockComment: comments.isBlockComment,
+    getCommentChildNodes,
+    canAttachComment,
+    isBlockComment,
     handleComments: {
-      ownLine: comments.handleOwnLineComment,
-      endOfLine: comments.handleEndOfLineComment,
-      remaining: comments.handleRemainingComment,
+      ownLine: handleOwnLineComment,
+      endOfLine: handleEndOfLineComment,
+      remaining: handleRemainingComment,
     },
     willPrintOwnComments(path) {
-      const node = path.getValue();
+      const { node } = path;
 
       return node && node.kind === "noop";
     },
-    printComment(commentPath) {
-      const comment = commentPath.getValue();
+    printComment(path) {
+      const comment = path.node;
 
       switch (comment.kind) {
         case "commentblock": {
@@ -138,8 +149,7 @@ const printers = {
         !comment.value.includes("prettier-ignore-start") &&
         !comment.value.includes("prettier-ignore-end");
 
-      const parentNode = path.getParentNode();
-      const node = path.getNode();
+      const { node, parent: parentNode } = path;
 
       return (
         (node &&
@@ -161,12 +171,8 @@ const printers = {
   },
 };
 
-module.exports = {
-  languages,
-  printers,
-  parsers,
-  options,
-  defaultOptions: {
-    tabWidth: 4,
-  },
+const defaultOptions = {
+  tabWidth: 4,
 };
+
+export { languages, printers, parsers, options, defaultOptions };
diff --git a/src/linguist-languages.cjs b/src/linguist-languages.cjs
new file mode 100644
index 0000000..a9f2b15
--- /dev/null
+++ b/src/linguist-languages.cjs
@@ -0,0 +1,4 @@
+"use strict";
+
+module.exports.LINGUIST_LANGUAGES_PHP = require("linguist-languages/data/PHP");
+module.exports.LINGUIST_LANGUAGES_HTML_PHP = require("linguist-languages/data/HTML+PHP");
diff --git a/src/module.js b/src/module.js
deleted file mode 100644
index 0056da5..0000000
--- a/src/module.js
+++ /dev/null
@@ -1,2 +0,0 @@
-"use strict";
-module.exports = require("./index");
diff --git a/src/needs-parens.js b/src/needs-parens.js
index d63ad69..bf4f4e9 100644
--- a/src/needs-parens.js
+++ b/src/needs-parens.js
@@ -1,18 +1,14 @@
-"use strict";
-
-const assert = require("assert");
-
-const { getPrecedence, shouldFlatten, isBitwiseOperator } = require("./util");
+import assert from "assert";
+import { getPrecedence, shouldFlatten, isBitwiseOperator } from "./util.js";
 
 function needsParens(path) {
-  const parent = path.getParentNode();
+  const { parent } = path;
 
   if (!parent) {
     return false;
   }
 
-  const name = path.getName();
-  const node = path.getNode();
+  const { key, node } = path;
 
   if (
     [
@@ -56,9 +52,9 @@ function needsParens(path) {
         case "staticlookup":
         case "offsetlookup":
         case "call":
-          return name === "what" && parent.what === node;
+          return key === "what";
         case "bin":
-          return parent.type === "**" && name === "left";
+          return parent.type === "**" && key === "left";
         default:
           return false;
       }
@@ -80,7 +76,7 @@ function needsParens(path) {
         case "nullsafepropertylookup":
         case "staticlookup":
         case "offsetlookup":
-          return name === "what" && parent.what === node;
+          return key === "what";
         case "bin": {
           const po = parent.type;
           const pp = getPrecedence(po);
@@ -95,7 +91,7 @@ function needsParens(path) {
             return true;
           }
 
-          if (pp === np && name === "right") {
+          if (pp === np && key === "right") {
             assert.strictEqual(parent.right, node);
 
             return true;
@@ -127,11 +123,7 @@ function needsParens(path) {
     case "staticlookup": {
       switch (parent.kind) {
         case "call":
-          return (
-            name === "what" &&
-            parent.what === node &&
-            node.parenthesizedExpression
-          );
+          return key === "what" && node.parenthesizedExpression;
 
         default:
           return false;
@@ -145,7 +137,7 @@ function needsParens(path) {
         case "staticlookup":
         case "offsetlookup":
         case "call":
-          return name === "what" && parent.what === node;
+          return key === "what";
         default:
           return false;
       }
@@ -157,10 +149,10 @@ function needsParens(path) {
         case "staticlookup":
         case "offsetlookup":
         case "call":
-          return name === "what" && parent.what === node;
+          return key === "what";
 
         case "retif":
-          return parent.test === node;
+          return key === "test";
 
         default:
           return !!(node.key || node.value);
@@ -195,7 +187,7 @@ function needsParens(path) {
         case "unary":
         case "bin":
         case "retif":
-          if (name === "test" && !parent.trueExpr) {
+          if (key === "test" && !parent.trueExpr) {
             return false;
           }
 
@@ -205,7 +197,7 @@ function needsParens(path) {
         case "staticlookup":
         case "offsetlookup":
         case "call":
-          return name === "what" && parent.what === node;
+          return key === "what";
 
         default:
           return false;
@@ -213,7 +205,7 @@ function needsParens(path) {
     case "closure":
       switch (parent.kind) {
         case "call":
-          return name === "what" && parent.what === node;
+          return key === "what";
 
         // https://github.com/prettier/plugin-php/issues/1675
         case "propertylookup":
@@ -243,7 +235,7 @@ function needsParens(path) {
             return false;
           }
 
-          return name === "what" && parent.what === node;
+          return key === "what";
         default:
           return false;
       }
@@ -255,4 +247,4 @@ function needsParens(path) {
   return false;
 }
 
-module.exports = needsParens;
+export default needsParens;
diff --git a/src/options.js b/src/options.js
index 8b30e9e..1600dbd 100644
--- a/src/options.js
+++ b/src/options.js
@@ -1,8 +1,6 @@
-"use strict";
-
 const CATEGORY_PHP = "PHP";
 
-module.exports = {
+export default {
   phpVersion: {
     since: "0.13.0",
     category: CATEGORY_PHP,
diff --git a/src/parser.js b/src/parser.js
index 1b1d817..343e421 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -1,5 +1,4 @@
-"use strict";
-const engine = require("php-parser");
+import engine from "php-parser";
 
 function parse(text, opts) {
   const inMarkdown = opts && opts.parentParser === "markdown";
@@ -61,4 +60,4 @@ function parse(text, opts) {
   return ast;
 }
 
-module.exports = parse;
+export default parse;
diff --git a/src/pragma.js b/src/pragma.js
index 379e3db..0647dbd 100644
--- a/src/pragma.js
+++ b/src/pragma.js
@@ -1,7 +1,5 @@
-"use strict";
-
-const parse = require("./parser");
-const memoize = require("mem");
+import memoize from "mem";
+import parse from "./parser.js";
 
 const reHasPragma = /@prettier|@format/;
 
@@ -91,7 +89,4 @@ function insertPragma(text) {
 ${after}`;
 }
 
-module.exports = {
-  hasPragma,
-  insertPragma,
-};
+export { hasPragma, insertPragma };
diff --git a/src/printer.js b/src/printer.js
index 53b27a8..1b00955 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -1,5 +1,4 @@
-"use strict";
-
+import { util as prettierUtil, doc } from "prettier";
 const {
   breakParent,
   join,
@@ -15,14 +14,22 @@ const {
   literalline,
   align,
   dedentToRoot,
-} = require("prettier").doc.builders;
-const { willBreak } = require("prettier").doc.utils;
+} = doc.builders;
+const { willBreak } = doc.utils;
 const { isNextLineEmptyAfterIndex, hasNewline, hasNewlineInRange } =
-  require("prettier").util;
-const comments = require("./comments");
-const pathNeedsParens = require("./needs-parens");
-
-const {
+  prettierUtil;
+import {
+  printAllComments,
+  hasTrailingComment,
+  hasLeadingComment,
+  printDanglingComments,
+  printComments,
+  isBlockComment,
+  hasLeadingOwnLineComment,
+} from "./comments.js";
+import pathNeedsParens from "./needs-parens.js";
+
+import {
   getLast,
   getPenultimate,
   isLastStatement,
@@ -32,8 +39,6 @@ const {
   maybeStripLeadingSlashFromUse,
   fileShouldEndWithHardline,
   hasDanglingComments,
-  hasLeadingComment,
-  hasTrailingComment,
   docShouldHaveTrailingNewline,
   isLookupNode,
   isFirstChildrenInlineNode,
@@ -54,7 +59,7 @@ const {
   getNextNonSpaceNonCommentCharacterIndex,
   isNextLineEmpty,
   isPreviousLineEmpty,
-} = require("./util");
+} from "./util.js";
 
 function isMinVersion(actualVersion, requiredVersion) {
   return parseFloat(actualVersion) >= parseFloat(requiredVersion);
@@ -80,11 +85,9 @@ function shouldPrintHardlineForOpenBrace(options) {
 }
 
 function genericPrint(path, options, print) {
-  const node = path.getValue();
+  const { node } = path;
 
-  if (!node) {
-    return "";
-  } else if (typeof node === "string") {
+  if (typeof node === "string") {
     return node;
   }
 
@@ -123,14 +126,14 @@ function printNullsafePropertyLookup(path, options, print) {
 }
 
 function printStaticLookup(path, options, print) {
-  const node = path.getValue();
+  const { node } = path;
   const needCurly = !["variable", "identifier"].includes(node.offset.kind);
 
   return ["::", needCurly ? "{" : "", print("offset"), needCurly ? "}" : ""];
 }
 
 function printOffsetLookup(path, options, print) {
-  const node = path.getValue();
+  const { node } = path;
   const shouldInline =
     (node.offset && node.offset.kind === "number") ||
     getAncestorNode(path, "encapsed");
@@ -212,7 +215,7 @@ function printMemberChain(path, options, print) {
   }
 
   function traverse(path) {
-    const node = path.getValue();
+    const { node } = path;
 
     if (
       node.kind === "call" &&
@@ -221,7 +224,7 @@ function printMemberChain(path, options, print) {
       printedNodes.unshift({
         node,
         printed: [
-          comments.printAllComments(
+          printAllComments(
             path,
             () => printArgumentsList(path, options, print),
             options
@@ -247,11 +250,7 @@ function printMemberChain(path, options, print) {
       printedNodes.unshift({
         node,
         needsParens: pathNeedsParens(path, options),
-        printed: comments.printAllComments(
-          path,
-          () => printedMemberish,
-          options
-        ),
+        printed: printAllComments(path, () => printedMemberish, options),
       });
       path.call((what) => traverse(what), "what");
     } else {
@@ -262,7 +261,7 @@ function printMemberChain(path, options, print) {
     }
   }
 
-  const node = path.getValue();
+  const { node } = path;
 
   printedNodes.unshift({
     node,
@@ -365,7 +364,7 @@ function printMemberChain(path, options, print) {
 
     if (
       printedNodes[i].node.comments &&
-      comments.hasTrailingComment(printedNodes[i].node)
+      hasTrailingComment(printedNodes[i].node)
     ) {
       groups.push(currentGroup);
       currentGroup = [];
@@ -426,8 +425,7 @@ function printMemberChain(path, options, print) {
     );
   }
 
-  const isExpressionStatement =
-    path.getParentNode().kind === "expressionstatement";
+  const isExpressionStatement = path.parent.kind === "expressionstatement";
   const shouldMerge =
     groups.length >= 2 && !groups[1][0].node.comments && shouldNotWrap(groups);
 
@@ -479,13 +477,9 @@ function printMemberChain(path, options, print) {
   const flatGroups = groups.slice(0, cutoff).flat();
 
   const hasComment =
-    flatGroups
-      .slice(1, -1)
-      .some((node) => comments.hasLeadingComment(node.node)) ||
-    flatGroups
-      .slice(0, -1)
-      .some((node) => comments.hasTrailingComment(node.node)) ||
-    (groups[cutoff] && comments.hasLeadingComment(groups[cutoff][0].node));
+    flatGroups.slice(1, -1).some((node) => hasLeadingComment(node.node)) ||
+    flatGroups.slice(0, -1).some((node) => hasTrailingComment(node.node)) ||
+    (groups[cutoff] && hasLeadingComment(groups[cutoff][0].node));
 
   const hasEncapsedAncestor = getAncestorNode(path, "encapsed");
 
@@ -579,12 +573,12 @@ function shouldGroupFirstArg(args) {
 }
 
 function printArgumentsList(path, options, print, argumentsKey = "arguments") {
-  const args = path.getValue()[argumentsKey];
+  const args = path.node[argumentsKey];
 
   if (args.length === 0) {
     return [
       "(",
-      comments.printDanglingComments(path, options, /* sameIndent */ true),
+      printDanglingComments(path, options, /* sameIndent */ true),
       ")",
     ];
   }
@@ -592,15 +586,13 @@ function printArgumentsList(path, options, print, argumentsKey = "arguments") {
   let anyArgEmptyLine = false;
   let hasEmptyLineFollowingFirstArg = false;
 
-  const lastArgIndex = args.length - 1;
-  const printedArguments = path.map((argPath, index) => {
-    const arg = argPath.getNode();
-    const parts = [print(argPath)];
+  const printedArguments = path.map(({ node: arg, isLast, isFirst }) => {
+    const parts = [print()];
 
-    if (index === lastArgIndex) {
+    if (isLast) {
       // do nothing
     } else if (isNextLineEmpty(options.originalText, arg, options)) {
-      if (index === 0) {
+      if (isFirst) {
         hasEmptyLineFollowingFirstArg = true;
       }
 
@@ -613,7 +605,7 @@ function printArgumentsList(path, options, print, argumentsKey = "arguments") {
     return parts;
   }, argumentsKey);
 
-  const node = path.getValue();
+  const { node } = path;
   const lastArg = getLast(args);
 
   const maybeTrailingComma =
@@ -649,12 +641,11 @@ function printArgumentsList(path, options, print, argumentsKey = "arguments") {
 
     // We want to print the last argument with a special flag
     let printedExpanded;
-    let i = 0;
 
-    path.each((argPath) => {
-      if (shouldGroupFirst && i === 0) {
+    path.each(({ isLast, isFirst }) => {
+      if (shouldGroupFirst && isFirst) {
         printedExpanded = [
-          argPath.call(() => print([], { expandFirstArg: true })),
+          print([], { expandFirstArg: true }),
           printedArguments.length > 1 ? "," : "",
           hasEmptyLineFollowingFirstArg ? hardline : line,
           hasEmptyLineFollowingFirstArg ? hardline : "",
@@ -662,14 +653,12 @@ function printArgumentsList(path, options, print, argumentsKey = "arguments") {
         ];
       }
 
-      if (shouldGroupLast && i === args.length - 1) {
+      if (shouldGroupLast && isLast) {
         printedExpanded = [
           ...printedArguments.slice(0, -1),
-          argPath.call(() => print([], { expandLastArg: true })),
+          print([], { expandLastArg: true }),
         ];
       }
-
-      i++;
     }, argumentsKey);
 
     const somePrintedArgumentsWillBreak = printedArguments.some(willBreak);
@@ -751,7 +740,7 @@ function printBinaryExpression(
   isInsideParenthesis
 ) {
   let parts = [];
-  const node = path.getValue();
+  const { node } = path;
 
   if (node.kind === "bin") {
     // Put all operators with the same precedence level in the same
@@ -790,7 +779,7 @@ function printBinaryExpression(
 
     // If there's only a single binary expression, we want to create a group
     // in order to avoid having a small right part like -1 be on its own line.
-    const parent = path.getParentNode();
+    const { parent } = path;
     const shouldGroup =
       !(isInsideParenthesis && ["||", "&&"].includes(node.type)) &&
       getNodeKindIncludingLogical(parent) !==
@@ -813,7 +802,7 @@ function printBinaryExpression(
     // the other ones since we don't call the normal print on bin,
     // only for the left and right parts
     if (isNested && node.comments) {
-      parts = comments.printAllComments(path, () => parts, options);
+      parts = printAllComments(path, () => parts, options);
     }
   } else {
     // Our stopping case. Simply print the node normally.
@@ -824,7 +813,7 @@ function printBinaryExpression(
 }
 
 function printLookupNodes(path, options, print) {
-  const node = path.getValue();
+  const { node } = path;
 
   switch (node.kind) {
     case "propertylookup":
@@ -863,16 +852,13 @@ function printArrayItems(path, options, print) {
   const printedElements = [];
   let separatorParts = [];
 
-  path.each((childPath) => {
+  path.each(({ node }) => {
     printedElements.push(separatorParts);
-    printedElements.push(group(print(childPath)));
+    printedElements.push(group(print()));
 
     separatorParts = [",", line];
 
-    if (
-      childPath.getValue() &&
-      isNextLineEmpty(options.originalText, childPath.getValue(), options)
-    ) {
+    if (node && isNextLineEmpty(options.originalText, node, options)) {
       separatorParts.push(softline);
     }
   }, "items");
@@ -925,22 +911,26 @@ function wrapPartsIntoGroups(parts, indexes) {
 }
 
 function printLines(path, options, print, childrenAttribute = "children") {
-  const node = path.getValue();
-  const parentNode = path.getParentNode();
+  const { node, parent: parentNode } = path;
 
   let lastInlineIndex = -1;
 
   const parts = [];
   const groupIndexes = [];
 
-  path.map((childPath, index) => {
-    const childNode = childPath.getValue();
+  path.map(() => {
+    const {
+      node: childNode,
+      next: nextNode,
+      isFirst: isFirstNode,
+      isLast: isLastNode,
+      index,
+    } = path;
+
     const isInlineNode = childNode.kind === "inline";
-    const printedPath = print(childPath);
-    const children = node[childrenAttribute];
-    const nextNode = children[index + 1];
+    const printedPath = print();
     const canPrintBlankLine =
-      !isLastStatement(childPath) &&
+      !isLastStatement(path) &&
       !isInlineNode &&
       (nextNode && nextNode.kind === "case"
         ? !isFirstChildrenInlineNode(path)
@@ -955,8 +945,6 @@ function printLines(path, options, print, childrenAttribute = "children") {
         : "",
     ];
 
-    const isFirstNode = index === 0;
-    const isLastNode = children.length - 1 === index;
     const isBlockNestedNode =
       node.kind === "block" &&
       parentNode &&
@@ -981,7 +969,7 @@ function printLines(path, options, print, childrenAttribute = "children") {
           (isInlineNode ? prevLastInlineIndex : lastInlineIndex) + 1;
         const end = isLastNode && !isInlineNode ? index + 1 : index;
         const prevInlineNode =
-          children[isInlineNode ? prevLastInlineIndex : lastInlineIndex];
+          path.siblings[isInlineNode ? prevLastInlineIndex : lastInlineIndex];
         const alignment = prevInlineNode
           ? getAlignment(prevInlineNode.raw)
           : "";
@@ -1019,7 +1007,7 @@ function printLines(path, options, print, childrenAttribute = "children") {
                 ? "<?php"
                 : "",
               node.kind === "namespace" || !isBlockNestedNode ? hardline : "",
-              comments.printComments(childNode.leadingComments, options),
+              printComments(childNode.leadingComments, options),
               hardline,
               "?>",
             ]
@@ -1027,6 +1015,7 @@ function printLines(path, options, print, childrenAttribute = "children") {
           ? ""
           : [beforeCloseTagInlineNode, "?>"];
 
+      //FIXME getNode is used to get ancestors, but it seems this means to get next sibling?
       const nextV = path.getNode(index + 1);
       const skipLastComment = nextV && nextV.children && nextV.children.length;
 
@@ -1035,9 +1024,7 @@ function printLines(path, options, print, childrenAttribute = "children") {
           ? [
               openTag,
               hardline,
-              skipLastComment
-                ? comments.printComments(childNode.comments, options)
-                : "",
+              skipLastComment ? printComments(childNode.comments, options) : "",
               hardline,
             ]
           : isProgramLikeNode(node) && isLastNode
@@ -1064,7 +1051,7 @@ function printLines(path, options, print, childrenAttribute = "children") {
         between && between[2] && between[2].includes("\n")
           ? [hardline, between[2].split("\n").length > 2 ? hardline : ""]
           : " ",
-        node.comments ? comments.printComments(node.comments, options) : "",
+        node.comments ? printComments(node.comments, options) : "",
       ];
 
       const shortEcho =
@@ -1111,17 +1098,15 @@ function printLines(path, options, print, childrenAttribute = "children") {
 }
 
 function printStatements(path, options, print, childrenAttribute) {
-  return path.map((childPath) => {
+  return path.map(() => {
     const parts = [];
 
-    parts.push(print(childPath));
+    parts.push(print());
 
-    if (!isLastStatement(childPath)) {
+    if (!isLastStatement(path)) {
       parts.push(hardline);
 
-      if (
-        isNextLineEmpty(options.originalText, childPath.getValue(), options)
-      ) {
+      if (isNextLineEmpty(options.originalText, path.node, options)) {
         parts.push(hardline);
       }
     }
@@ -1138,28 +1123,25 @@ function printClassPart(
   beforePart = " ",
   afterPart = " "
 ) {
-  const node = path.getValue();
-  const printedBeforePart = hasDanglingComments(node[part])
+  const value = path.node[part];
+  const printedBeforePart = hasDanglingComments(value)
     ? [
         hardline,
-        path.call(
-          () => comments.printDanglingComments(path, options, true),
-          part
-        ),
+        path.call(() => printDanglingComments(path, options, true), part),
         hardline,
       ]
     : beforePart;
-  const printedPartItems = Array.isArray(node[part])
+  const printedPartItems = Array.isArray(value)
     ? group(
         join(
           ",",
-          path.map((itemPartPath) => {
-            const printedPart = print(itemPartPath);
+          path.map(({ node }) => {
+            const printedPart = print();
             // Check if any of the implements nodes have comments
-            return hasDanglingComments(itemPartPath.getValue())
+            return hasDanglingComments(node)
               ? [
                   hardline,
-                  comments.printDanglingComments(itemPartPath, options, true),
+                  printDanglingComments(path, options, true),
                   hardline,
                   printedPart,
                 ]
@@ -1178,23 +1160,23 @@ function printClassPart(
 
 function printAttrs(path, options, print, { inline = false } = {}) {
   const allAttrs = [];
-  if (!path.getValue().attrGroups) {
+  if (!path.node.attrGroups) {
     return [];
   }
-  path.each((agPath) => {
+  path.each(() => {
     const attrGroup = ["#["];
     if (!inline && allAttrs.length > 0) {
       allAttrs.push(hardline);
     }
     attrGroup.push(softline);
-    agPath.each((attrPath) => {
-      const attrNode = attrPath.getValue();
+    path.each(() => {
+      const attrNode = path.node;
       if (attrGroup.length > 2) {
         attrGroup.push(",", line);
       }
       const attrStmt = [attrNode.name];
       if (attrNode.args.length > 0) {
-        attrStmt.push(printArgumentsList(attrPath, options, print, "args"));
+        attrStmt.push(printArgumentsList(path, options, print, "args"));
       }
       attrGroup.push(group(attrStmt));
     }, "attrs");
@@ -1215,7 +1197,7 @@ function printAttrs(path, options, print, { inline = false } = {}) {
 }
 
 function printClass(path, options, print) {
-  const node = path.getValue();
+  const { node } = path;
   const isAnonymousClass = node.kind === "class" && node.isAnonymous;
   const attrs = printAttrs(path, options, print, { inline: isAnonymousClass });
   const declaration = isAnonymousClass ? [] : [...attrs];
@@ -1326,7 +1308,7 @@ function printClass(path, options, print) {
       hasEmptyClassBody ? "" : hardline,
       printStatements(path, options, print, "body"),
     ]),
-    comments.printDanglingComments(path, options, true),
+    printDanglingComments(path, options, true),
     isAnonymousClass && hasEmptyClassBody ? softline : hardline,
     "}",
   ];
@@ -1335,7 +1317,7 @@ function printClass(path, options, print) {
 }
 
 function printFunction(path, options, print) {
-  const node = path.getValue();
+  const { node } = path;
   const declAttrs = printAttrs(path, options, print, {
     inline: node.kind === "closure",
   });
@@ -1380,10 +1362,7 @@ function printFunction(path, options, print) {
       ": ",
       hasDanglingComments(node.type)
         ? [
-            path.call(
-              () => comments.printDanglingComments(path, options, true),
-              "type"
-            ),
+            path.call(() => printDanglingComments(path, options, true), "type"),
             " ",
           ]
         : "",
@@ -1444,7 +1423,7 @@ function printBodyControlStructure(
   print,
   bodyProperty = "body"
 ) {
-  const node = path.getValue();
+  const { node } = path;
 
   if (!node[bodyProperty]) {
     return ";";
@@ -1523,9 +1502,7 @@ function printAssignmentRight(
 ) {
   const ref = hasRef ? "&" : "";
 
-  if (
-    comments.hasLeadingOwnLineComment(options.originalText, rightNode, options)
-  ) {
+  if (hasLeadingOwnLineComment(options.originalText, rightNode, options)) {
     return indent([hardline, ref, printedRight]);
   }
 
@@ -1561,7 +1538,7 @@ function needsHardlineAfterDanglingComment(node) {
     node.comments.filter((comment) => !comment.leading && !comment.trailing)
   );
 
-  return lastDanglingComment && !comments.isBlockComment(lastDanglingComment);
+  return lastDanglingComment && !isBlockComment(lastDanglingComment);
 }
 
 function stringHasNewLines(node) {
@@ -1580,18 +1557,18 @@ function isStringOnItsOwnLine(node, text, options) {
 
 function printComposedTypes(path, print, glue) {
   return group(
-    path.map((uPath, i) => (i === 0 ? [print()] : [glue, print()]), "types")
+    path.map(({ isFirst }) => (isFirst ? [print()] : [glue, print()]), "types")
   );
 }
 
 function printNode(path, options, print) {
-  const node = path.getValue();
+  const { node } = path;
 
   switch (node.kind) {
     case "program": {
       return group([
         printLines(path, options, print),
-        comments.printDanglingComments(
+        printDanglingComments(
           path,
           options,
           /* sameIndent */ true,
@@ -1604,14 +1581,11 @@ function printNode(path, options, print) {
     case "block":
       return [
         printLines(path, options, print),
-        comments.printDanglingComments(path, options, true),
+        printDanglingComments(path, options, true),
       ];
     case "declare": {
       const printDeclareArguments = (path) => {
-        return join(
-          ", ",
-          path.map((directive) => print(directive), "directives")
-        );
+        return join(", ", path.map(print, "directives"));
       };
 
       if (["block", "short"].includes(node.mode)) {
@@ -1623,7 +1597,7 @@ function printNode(path, options, print) {
           node.children.length > 0
             ? indent([hardline, printLines(path, options, print)])
             : "",
-          comments.printDanglingComments(path, options),
+          printDanglingComments(path, options),
           hardline,
           node.mode === "block" ? "}" : "enddeclare;",
         ];
@@ -1648,7 +1622,7 @@ function printNode(path, options, print) {
           : "",
         node.withBrackets ? "{" : ";",
         hasDanglingComments(node)
-          ? [" ", comments.printDanglingComments(path, options, true)]
+          ? [" ", printDanglingComments(path, options, true)]
           : "",
         node.children.length > 0
           ? node.withBrackets
@@ -1679,10 +1653,7 @@ function printNode(path, options, print) {
           node.name
             ? [maybeStripLeadingSlashFromUse(node.name), "\\{", softline]
             : "",
-          join(
-            [",", line],
-            path.map((item) => print(item), "items")
-          ),
+          join([",", line], path.map(print, "items")),
         ]),
         node.name
           ? [
@@ -1697,7 +1668,7 @@ function printNode(path, options, print) {
         node.type ? [node.type, " "] : "",
         maybeStripLeadingSlashFromUse(node.name),
         hasDanglingComments(node)
-          ? [" ", comments.printDanglingComments(path, options, true)]
+          ? [" ", printDanglingComments(path, options, true)]
           : "",
         node.alias ? [" as ", print("alias")] : "",
       ];
@@ -1740,11 +1711,7 @@ function printNode(path, options, print) {
                     hardline,
                   ]
                 : hasDanglingComments(node)
-                ? [
-                    line,
-                    comments.printDanglingComments(path, options, true),
-                    line,
-                  ]
+                ? [line, printDanglingComments(path, options, true), line]
                 : "",
               "}",
             ]
@@ -1794,7 +1761,7 @@ function printNode(path, options, print) {
           // no node to attach comments that fall in between the parameter name
           // and value, we store them as dangling comments
           hasDanglingComments(node) ? " " : "",
-          comments.printDanglingComments(path, options, true),
+          printDanglingComments(path, options, true),
           " =",
           printAssignmentRight(
             node.name,
@@ -1831,13 +1798,10 @@ function printNode(path, options, print) {
       ]);
     case "propertystatement": {
       const attrs = [];
-      path.map(
-        (propPath) => attrs.push(...printAttrs(propPath, options, print)),
-        "properties"
-      );
-      const printed = path.map((childPath) => {
-        return print(childPath);
+      path.each(() => {
+        attrs.push(...printAttrs(path, options, print));
       }, "properties");
+      const printed = path.map(print, "properties");
 
       const hasValue = node.properties.some((property) => property.value);
 
@@ -1885,7 +1849,7 @@ function printNode(path, options, print) {
         const commentOnOwnLine =
           (hasTrailingComment(node.body) &&
             node.body.comments.some(
-              (comment) => comment.trailing && !comments.isBlockComment(comment)
+              (comment) => comment.trailing && !isBlockComment(comment)
             )) ||
           needsHardlineAfterDanglingComment(node);
         const elseOnSameLine = !commentOnOwnLine;
@@ -1896,7 +1860,7 @@ function printNode(path, options, print) {
             isNextLineEmpty(options.originalText, node.body, options)
               ? hardline
               : "",
-            comments.printDanglingComments(path, options, true),
+            printDanglingComments(path, options, true),
             commentOnOwnLine ? hardline : " "
           );
         }
@@ -1938,7 +1902,7 @@ function printNode(path, options, print) {
       // We want to keep dangling comments above the loop to stay consistent.
       // Any comment positioned between the for statement and the parentheses
       // is going to be printed before the statement.
-      const dangling = comments.printDanglingComments(
+      const dangling = printDanglingComments(
         path,
         options,
         /* sameLine */ true
@@ -1977,7 +1941,7 @@ function printNode(path, options, print) {
       // We want to keep dangling comments above the loop to stay consistent.
       // Any comment positioned between the for statement and the parentheses
       // is going to be printed before the statement.
-      const dangling = comments.printDanglingComments(
+      const dangling = printDanglingComments(
         path,
         options,
         /* sameLine */ true
@@ -2120,7 +2084,7 @@ function printNode(path, options, print) {
         parts.push(
           node.what.leadingComments &&
             node.what.leadingComments[0].kind === "commentblock"
-            ? [comments.printComments(node.what.leadingComments, options), " "]
+            ? [printComments(node.what.leadingComments, options), " "]
             : "",
           ...path.call(
             () => printAttrs(path, options, print, { inline: true }),
@@ -2155,11 +2119,11 @@ function printNode(path, options, print) {
     case "nullsafepropertylookup":
     case "staticlookup":
     case "offsetlookup": {
-      const parent = path.getParentNode();
+      const { parent } = path;
 
+      // TODO: Use `AstPath.findAncestor` when it's stable
       let firstNonMemberParent;
       let i = 0;
-
       do {
         firstNonMemberParent = path.getParentNode(i);
         i++;
@@ -2195,7 +2159,7 @@ function printNode(path, options, print) {
           ? isStringOnItsOwnLine(node.expression, options.originalText, options)
             ? print("expression")
             : [indent([softline, print("expression")]), softline]
-          : comments.printDanglingComments(path, options),
+          : printDanglingComments(path, options),
         ")",
       ]);
     case "global":
@@ -2225,11 +2189,7 @@ function printNode(path, options, print) {
       return [
         hasDanglingComments(node)
           ? [
-              comments.printDanglingComments(
-                path,
-                options,
-                /* sameIndent */ true
-              ),
+              printDanglingComments(path, options, /* sameIndent */ true),
               hardline,
             ]
           : "",
@@ -2245,9 +2205,7 @@ function printNode(path, options, print) {
         ")",
       ]);
     case "echo": {
-      const printedArguments = path.map((childPath) => {
-        return print(childPath);
-      }, "expressions");
+      const printedArguments = path.map(print, "expressions");
 
       let firstVariable;
 
@@ -2288,7 +2246,7 @@ function printNode(path, options, print) {
       if (hasDanglingComments(node)) {
         parts.push(
           " ",
-          comments.printDanglingComments(path, options, /* sameIndent */ true)
+          printDanglingComments(path, options, /* sameIndent */ true)
         );
       }
 
@@ -2308,8 +2266,7 @@ function printNode(path, options, print) {
         ")",
       ]);
     case "variable": {
-      const parent = path.getParentNode();
-      const parentParent = path.getParentNode(1);
+      const { parent, grandparent: parentParent } = path;
       const ampersand = parent.kind === "assign" ? "" : node.byref ? "&" : "";
       const dollar =
         (parent.kind === "encapsedpart" &&
@@ -2330,7 +2287,7 @@ function printNode(path, options, print) {
     case "constantstatement":
     case "classconstant": {
       const attrs = printAttrs(path, options, print);
-      const printed = path.map((childPath) => print(childPath), "constants");
+      const printed = path.map(print, "constants");
 
       let firstVariable;
 
@@ -2361,9 +2318,7 @@ function printNode(path, options, print) {
         options
       );
     case "static": {
-      const printed = path.map((childPath) => {
-        return print(childPath);
-      }, "variables");
+      const printed = path.map(print, "variables");
 
       const hasValue = node.variables.some((item) => item.defaultValue);
 
@@ -2411,7 +2366,7 @@ function printNode(path, options, print) {
 
         return group([
           open,
-          comments.printDanglingComments(path, options),
+          printDanglingComments(path, options),
           softline,
           close,
         ]);
@@ -2459,7 +2414,7 @@ function printNode(path, options, print) {
                 ]
               : ""
           ),
-          comments.printDanglingComments(path, options, true),
+          printDanglingComments(path, options, true),
           softline,
           close,
         ],
@@ -2528,8 +2483,7 @@ function printNode(path, options, print) {
       );
     }
     case "bin": {
-      const parent = path.getParentNode();
-      const parentParent = path.getParentNode(1);
+      const { parent, grandparent: parentParent } = path;
       const isInsideParenthesis =
         node !== parent.body &&
         (parent.kind === "if" ||
@@ -2612,8 +2566,9 @@ function printNode(path, options, print) {
     }
     case "retif": {
       const parts = [];
-      const parent = path.getParentNode();
+      const { parent } = path;
 
+      // TODO: Use `AstPath.findAncestor` when it's stable
       // Find the outermost non-retif parent, and the outermost retif parent.
       let currentParent;
       let i = 0;
@@ -2660,7 +2615,7 @@ function printNode(path, options, print) {
       //   ? $b
       //   : $c
       // )->call()
-      const parentParent = path.getParentNode(1);
+      const parentParent = path.grandparent;
       const pureParent =
         parent.kind === "cast" && parentParent ? parentParent : parent;
       const breakLookupNodes = [
@@ -2712,10 +2667,10 @@ function printNode(path, options, print) {
     case "number":
       return printNumber(node.value);
     case "string": {
-      const parent = path.getParentNode();
+      const { parent } = path;
 
       if (parent.kind === "encapsedpart") {
-        const parentParent = path.getParentNode(1);
+        const parentParent = path.grandparent;
         let closingTagIndentation = 0;
         const flexible = isMinVersion(options.phpVersion, "7.3");
         let linebreak = literalline;
@@ -2839,7 +2794,7 @@ function printNode(path, options, print) {
     case "nullkeyword":
       return "null";
     case "identifier": {
-      const parent = path.getParentNode();
+      const { parent } = path;
 
       if (parent.kind === "method") {
         node.name = normalizeMagicMethodName(node.name);
@@ -2848,19 +2803,18 @@ function printNode(path, options, print) {
       return print("name");
     }
     case "match": {
-      const lastArmIndex = node.arms.length - 1;
-      const arms = path.map((armPath, armIdx) => {
-        const armNode = armPath.getValue();
+      const arms = path.map(() => {
+        const armNode = path.node;
 
-        const maybeLeadingComment = comments.hasLeadingComment(armNode)
-          ? [comments.printComments(armNode.leadingComments, options), hardline]
+        const maybeLeadingComment = hasLeadingComment(armNode)
+          ? [printComments(armNode.leadingComments, options), hardline]
           : [];
         const maybeTrailingComma =
-          armIdx < lastArmIndex || options.trailingCommaPHP ? "," : "";
-        const maybeTrailingComment = comments.hasTrailingComment(armNode)
+          !path.isLast || options.trailingCommaPHP ? "," : "";
+        const maybeTrailingComment = hasTrailingComment(armNode)
           ? [
               " ",
-              comments.printComments(
+              printComments(
                 armNode.comments.filter((c) => c.trailing),
                 options
               ),
@@ -2870,14 +2824,13 @@ function printNode(path, options, print) {
         const conds =
           armNode.conds === null
             ? "default"
-            : armPath.map(
-                (condPath, condIdx) =>
-                  [",", line, print(condPath)].slice(condIdx === 0 ? 2 : 0),
+            : path.map(
+                ({ isFirst }) => [",", line, print()].slice(isFirst ? 2 : 0),
                 "conds"
               );
         const body = print("body");
         const maybeEmptyLineBetweenArms =
-          armIdx > 0 &&
+          !path.isFirst &&
           isPreviousLineEmpty(options.originalText, armNode, options)
             ? hardline
             : "";
@@ -2894,7 +2847,7 @@ function printNode(path, options, print) {
             maybeTrailingComma,
             ...maybeTrailingComment,
           ]),
-        ].slice(armIdx > 0 ? 0 : 1);
+        ].slice(!path.isFirst ? 0 : 1);
       }, "arms");
       return group([
         "match (",
@@ -2908,9 +2861,7 @@ function printNode(path, options, print) {
     }
 
     case "noop":
-      return node.comments
-        ? comments.printComments(path.getValue().comments, options)
-        : "";
+      return node.comments ? printComments(node.comments, options) : "";
     case "namedargument":
       return [node.name, ": ", print("value")];
 
@@ -2941,4 +2892,4 @@ function printNode(path, options, print) {
   }
 }
 
-module.exports = genericPrint;
+export default genericPrint;
diff --git a/src/util.js b/src/util.js
index e308bd2..e8c6b59 100644
--- a/src/util.js
+++ b/src/util.js
@@ -1,4 +1,4 @@
-"use strict";
+import { util as prettierUtil, version as prettierVersion } from "prettier";
 
 const {
   hasNewline,
@@ -8,9 +8,7 @@ const {
   isPreviousLineEmpty: _isPreviousLineEmpty,
   getNextNonSpaceNonCommentCharacterIndex:
     _getNextNonSpaceNonCommentCharacterIndex,
-} = require("prettier").util;
-
-const prettierVersion = require("prettier").version;
+} = prettierUtil;
 
 function lookupIfPrettier2(options, prop) {
   return parseInt(prettierVersion[0]) > 1 ? options[prop] : options;
@@ -190,7 +188,7 @@ function getNodeListProperty(node) {
 }
 
 function getParentNodeListProperty(path) {
-  const parent = path.getParentNode();
+  const { parent } = path;
   if (!parent) {
     return null;
   }
@@ -216,12 +214,12 @@ function isLastStatement(path) {
   if (!body) {
     return true;
   }
-  const node = path.getValue();
+  const { node } = path;
   return body[body.length - 1] === node;
 }
 
 function isFirstChildrenInlineNode(path) {
-  const node = path.getValue();
+  const { node } = path;
 
   if (node.kind === "program") {
     const children = getNodeListProperty(node);
@@ -377,8 +375,7 @@ function docShouldHaveTrailingNewline(path, recurse = 0) {
 }
 
 function lineShouldEndWithSemicolon(path) {
-  const node = path.getValue();
-  const parentNode = path.getParentNode();
+  const { node, parent: parentNode } = path;
   if (!parentNode) {
     return false;
   }
@@ -407,7 +404,7 @@ function lineShouldEndWithSemicolon(path) {
     return true;
   }
   if (node.kind === "method") {
-    const parent = path.getParentNode();
+    const { parent } = path;
     if (parent && parent.kind === "interface") {
       return true;
     }
@@ -435,7 +432,7 @@ function lineShouldEndWithSemicolon(path) {
 }
 
 function fileShouldEndWithHardline(path) {
-  const node = path.getValue();
+  const { node } = path;
   const isProgramNode = node.kind === "program";
   const lastNode = node.children && getLast(node.children);
 
@@ -477,14 +474,6 @@ function hasDanglingComments(node) {
   );
 }
 
-function hasLeadingComment(node) {
-  return node.comments && node.comments.some((comment) => comment.leading);
-}
-
-function hasTrailingComment(node) {
-  return node.comments && node.comments.some((comment) => comment.trailing);
-}
-
 function isLookupNode(node) {
   return (
     node.kind === "propertylookup" ||
@@ -495,7 +484,7 @@ function isLookupNode(node) {
 }
 
 function shouldPrintHardLineAfterStartInControlStructure(path) {
-  const node = path.getValue();
+  const { node } = path;
 
   if (["try", "catch"].includes(node.kind)) {
     return false;
@@ -505,7 +494,7 @@ function shouldPrintHardLineAfterStartInControlStructure(path) {
 }
 
 function shouldPrintHardLineBeforeEndInControlStructure(path) {
-  const node = path.getValue();
+  const { node } = path;
 
   if (["try", "catch"].includes(node.kind)) {
     return true;
@@ -544,7 +533,7 @@ function getAlignment(text) {
 }
 
 function getNextNode(path, node) {
-  const parent = path.getParentNode();
+  const { parent } = path;
   const children = getNodeListProperty(parent);
 
   if (!children) {
@@ -606,7 +595,7 @@ function useDoubleQuote(node, options) {
 }
 
 function hasEmptyBody(path, name = "body") {
-  const node = path.getValue();
+  const { node } = path;
 
   return (
     node[name] &&
@@ -692,7 +681,7 @@ function normalizeMagicMethodName(name) {
   return name;
 }
 
-module.exports = {
+export {
   printNumber,
   getPrecedence,
   isBitwiseOperator,
@@ -708,8 +697,6 @@ module.exports = {
   fileShouldEndWithHardline,
   maybeStripLeadingSlashFromUse,
   hasDanglingComments,
-  hasLeadingComment,
-  hasTrailingComment,
   docShouldHaveTrailingNewline,
   isLookupNode,
   isFirstChildrenInlineNode,
diff --git a/yarn.lock b/yarn.lock
index a9c8a96..a4f5b6e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1674,7 +1674,7 @@
   resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
   integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
 
-"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
+"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.14":
   version "1.4.15"
   resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
   integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
@@ -1744,6 +1744,39 @@
     picocolors "^1.0.0"
     tslib "^2.6.0"
 
+"@rollup/plugin-commonjs@^25.0.4":
+  version "25.0.4"
+  resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.4.tgz#a7547a0c4ec3fa79818eb313e1de0023e548f4e6"
+  integrity sha512-L92Vz9WUZXDnlQQl3EwbypJR4+DM2EbsO+/KOcEkP4Mc6Ct453EeDB2uH9lgRwj4w5yflgNpq9pHOiY8aoUXBQ==
+  dependencies:
+    "@rollup/pluginutils" "^5.0.1"
+    commondir "^1.0.1"
+    estree-walker "^2.0.2"
+    glob "^8.0.3"
+    is-reference "1.2.1"
+    magic-string "^0.27.0"
+
+"@rollup/plugin-node-resolve@^15.1.0":
+  version "15.1.0"
+  resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz#9ffcd8e8c457080dba89bb9fcb583a6778dc757e"
+  integrity sha512-xeZHCgsiZ9pzYVgAo9580eCGqwh/XCEUM9q6iQfGNocjgkufHAqC3exA+45URvhiYV8sBF9RlBai650eNs7AsA==
+  dependencies:
+    "@rollup/pluginutils" "^5.0.1"
+    "@types/resolve" "1.20.2"
+    deepmerge "^4.2.2"
+    is-builtin-module "^3.2.1"
+    is-module "^1.0.0"
+    resolve "^1.22.1"
+
+"@rollup/pluginutils@^5.0.1":
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.0.3.tgz#183126d69aeb1cfa23401d5a71cb4b8c16c4a4e0"
+  integrity sha512-hfllNN4a80rwNQ9QCxhxuHCGHMAvabXqxNdaChUSSadMre7t4iEUI6fFAhBOn/eIYTgYVhBv7vCLsAJ4u3lf3g==
+  dependencies:
+    "@types/estree" "^1.0.0"
+    estree-walker "^2.0.2"
+    picomatch "^2.3.1"
+
 "@sinclair/typebox@^0.27.8":
   version "0.27.8"
   resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
@@ -1811,6 +1844,11 @@
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
   integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
 
+"@types/estree@^1.0.0":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194"
+  integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==
+
 "@types/graceful-fs@^4.1.3":
   version "4.1.5"
   resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15"
@@ -1866,12 +1904,10 @@
   resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
   integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
 
-"@types/resolve@0.0.8":
-  version "0.0.8"
-  resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194"
-  integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==
-  dependencies:
-    "@types/node" "*"
+"@types/resolve@1.20.2":
+  version "1.20.2"
+  resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975"
+  integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==
 
 "@types/stack-utils@^2.0.0":
   version "2.0.1"
@@ -2008,6 +2044,11 @@ ansi-regex@^5.0.1:
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
   integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
 
+ansi-regex@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
+  integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
+
 ansi-styles@^3.2.1:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@@ -2243,6 +2284,13 @@ brace-expansion@^1.1.7:
     balanced-match "^1.0.0"
     concat-map "0.0.1"
 
+brace-expansion@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+  integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+  dependencies:
+    balanced-match "^1.0.0"
+
 braces@^3.0.1:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
@@ -2307,10 +2355,10 @@ buffer-from@^1.0.0:
   resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
   integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
 
-builtin-modules@^3.1.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887"
-  integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==
+builtin-modules@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
+  integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
 
 bundle-name@^3.0.0:
   version "3.0.0"
@@ -2455,6 +2503,11 @@ commander@^2.20.0:
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
   integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
 
+commondir@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+  integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
+
 concat-map@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -3062,6 +3115,11 @@ estree-walker@^0.6.1:
   resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362"
   integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==
 
+estree-walker@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
+  integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
+
 esutils@^2.0.2:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
@@ -3353,6 +3411,17 @@ glob@^7.1.3, glob@^7.1.4:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
+glob@^8.0.3:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
+  integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^5.0.1"
+    once "^1.3.0"
+
 globals@^11.1.0:
   version "11.12.0"
   resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
@@ -3645,6 +3714,13 @@ is-boolean-object@^1.1.0:
     call-bind "^1.0.2"
     has-tostringtag "^1.0.0"
 
+is-builtin-module@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169"
+  integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==
+  dependencies:
+    builtin-modules "^3.3.0"
+
 is-callable@^1.1.3, is-callable@^1.2.7:
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
@@ -3766,7 +3842,7 @@ is-potential-custom-element-name@^1.0.1:
   resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
   integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
 
-is-reference@^1.1.2:
+is-reference@1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7"
   integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==
@@ -4484,6 +4560,13 @@ magic-string@^0.25.2, magic-string@^0.25.3:
   dependencies:
     sourcemap-codec "^1.4.4"
 
+magic-string@^0.27.0:
+  version "0.27.0"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3"
+  integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==
+  dependencies:
+    "@jridgewell/sourcemap-codec" "^1.4.13"
+
 make-dir@^3.0.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@@ -4565,6 +4648,13 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2:
   dependencies:
     brace-expansion "^1.1.7"
 
+minimatch@^5.0.1:
+  version "5.1.6"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
+  integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
+  dependencies:
+    brace-expansion "^2.0.1"
+
 minimist@^1.2.0, minimist@^1.2.6:
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
@@ -4874,7 +4964,7 @@ picocolors@^1.0.0:
   resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
   integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
 
-picomatch@^2.0.4, picomatch@^2.2.3:
+picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
   integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
@@ -5076,15 +5166,6 @@ resolve.exports@^2.0.0:
   resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.1.tgz#cee884cd4e3f355660e501fa3276b27d7ffe5a20"
   integrity sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw==
 
-resolve@^1.11.0, resolve@^1.11.1, resolve@^1.20.0:
-  version "1.22.0"
-  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
-  integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
-  dependencies:
-    is-core-module "^2.8.1"
-    path-parse "^1.0.7"
-    supports-preserve-symlinks-flag "^1.0.0"
-
 resolve@^1.14.2:
   version "1.22.2"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f"
@@ -5094,6 +5175,15 @@ resolve@^1.14.2:
     path-parse "^1.0.7"
     supports-preserve-symlinks-flag "^1.0.0"
 
+resolve@^1.20.0:
+  version "1.22.0"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
+  integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
+  dependencies:
+    is-core-module "^2.8.1"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
 resolve@^1.22.1:
   version "1.22.1"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
@@ -5139,17 +5229,6 @@ rollup-plugin-babel@^4.3.2:
     "@babel/helper-module-imports" "^7.0.0"
     rollup-pluginutils "^2.8.1"
 
-rollup-plugin-commonjs@^10.0.2:
-  version "10.1.0"
-  resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz#417af3b54503878e084d127adf4d1caf8beb86fb"
-  integrity sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==
-  dependencies:
-    estree-walker "^0.6.1"
-    is-reference "^1.1.2"
-    magic-string "^0.25.2"
-    resolve "^1.11.0"
-    rollup-pluginutils "^2.8.1"
-
 rollup-plugin-inject@^3.0.1:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz#e4233855bfba6c0c12a312fd6649dff9a13ee9f4"
@@ -5166,17 +5245,6 @@ rollup-plugin-json@^4.0.0:
   dependencies:
     rollup-pluginutils "^2.5.0"
 
-rollup-plugin-node-resolve@^5.2.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz#730f93d10ed202473b1fb54a5997a7db8c6d8523"
-  integrity sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==
-  dependencies:
-    "@types/resolve" "0.0.8"
-    builtin-modules "^3.1.0"
-    is-module "^1.0.0"
-    resolve "^1.11.1"
-    rollup-pluginutils "^2.8.1"
-
 rollup-plugin-replace@^2.1.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/rollup-plugin-replace/-/rollup-plugin-replace-2.2.0.tgz#f41ae5372e11e7a217cde349c8b5d5fd115e70e3"
@@ -5436,6 +5504,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
   dependencies:
     ansi-regex "^5.0.1"
 
+strip-ansi@^7.1.0:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
+  integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
+  dependencies:
+    ansi-regex "^6.0.1"
+
 strip-bom@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"


Test patch:
diff --git a/tests/markdown/jsfmt.spec.js b/tests/markdown/jsfmt.spec.js
index 6c89822..486660a 100644
--- a/tests/markdown/jsfmt.spec.js
+++ b/tests/markdown/jsfmt.spec.js
@@ -1,5 +1,5 @@
 run_spec(import.meta, ["markdown"], {
-  plugins: global.STANDALONE ? [require("prettier/parser-markdown")] : [],
+  plugins: global.STANDALONE ? ["prettier/parser-markdown"] : [],
   proseWrap: "always",
   tabWidth: 4,
 });
diff --git a/tests/package.json b/tests/package.json
deleted file mode 100644
index 0038dff..0000000
--- a/tests/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "//": "Remove this file after we migrate to ESM.",
-  "description": "Make Node.js treat .js files in this directory as module",
-  "type": "module"
-}
diff --git a/tests_config/get_engine.js b/tests_config/get_engine.js
index cef2790..f8ce99b 100644
--- a/tests_config/get_engine.js
+++ b/tests_config/get_engine.js
@@ -1,14 +1,12 @@
-import path from "path";
 import url from "url";
 import { createRequire } from "module";
+import prettierModule from "prettier";
+import prettierStandalone from "prettier/standalone";
+import * as prettierPluginPhp from "../src/index.js";
 
 const require = createRequire(import.meta.url);
-const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
 
-// TODO: Use ESM version
-export const prettier = require(global.STANDALONE
-  ? "prettier/standalone"
-  : "prettier");
-export const plugin = require(global.STANDALONE
-  ? path.join(__dirname, "../standalone.js")
-  : path.join(__dirname, ".."));
+export const prettier = global.STANDALONE ? prettierStandalone : prettierModule;
+export const plugin = global.STANDALONE
+  ? require(url.fileURLToPath(new URL("../standalone.js", import.meta.url)))
+  : prettierPluginPhp;
diff --git a/tests_config/package.json b/tests_config/package.json
deleted file mode 100644
index 0038dff..0000000
--- a/tests_config/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "//": "Remove this file after we migrate to ESM.",
-  "description": "Make Node.js treat .js files in this directory as module",
-  "type": "module"
-}