diff --git a/__tests__/data/package.json b/__tests__/data/package.json
new file mode 100644
index 00000000..e537e200
--- /dev/null
+++ b/__tests__/data/package.json
@@ -0,0 +1,5 @@
+{
+  "engines": {
+    "node": ">=14.0.0"
+  }
+}
diff --git a/__tests__/installer.test.ts b/__tests__/installer.test.ts
index cd536192..c1e16b5f 100644
--- a/__tests__/installer.test.ts
+++ b/__tests__/installer.test.ts
@@ -591,6 +591,33 @@ describe('setup-node', () => {
       );
     });
 
+    it('reads package.json as node-version-file if provided', async () => {
+      // Arrange
+      const versionSpec = fs.readFileSync(
+        path.join(__dirname, 'data/package.json'),
+        'utf-8'
+      );
+      const versionFile = 'package.json';
+      const expectedVersionSpec = '14';
+      process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
+      inputs['node-version-file'] = versionFile;
+
+      parseNodeVersionSpy.mockImplementation(() => expectedVersionSpec);
+      existsSpy.mockImplementationOnce(
+        input => input === path.join(__dirname, 'data', versionFile)
+      );
+      // Act
+      await main.run();
+
+      // Assert
+      expect(existsSpy).toHaveBeenCalledTimes(1);
+      expect(existsSpy).toHaveReturnedWith(true);
+      expect(parseNodeVersionSpy).toHaveBeenCalledWith(versionSpec);
+      expect(logSpy).toHaveBeenCalledWith(
+        `Resolved ${versionFile} as ${expectedVersionSpec}`
+      );
+    });
+
     it('both node-version-file and node-version are provided', async () => {
       inputs['node-version'] = '12';
       const versionSpec = 'v14';
diff --git a/dist/setup/index.js b/dist/setup/index.js
index 28d5dc8f..0a0c0bad 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -71768,15 +71768,25 @@ function translateArchToDistUrl(arch) {
     }
 }
 function parseNodeVersionFile(contents) {
-    var _a;
+    var _a, _b;
+    let nodeVersion;
     const found = contents.match(/^(?:nodejs\s+)?v?(?<version>[^\s]+)$/m);
-    const nodeVersion = (_a = found === null || found === void 0 ? void 0 : found.groups) === null || _a === void 0 ? void 0 : _a.version;
-    if (nodeVersion) {
-        return nodeVersion;
+    nodeVersion = (_a = found === null || found === void 0 ? void 0 : found.groups) === null || _a === void 0 ? void 0 : _a.version;
+    if (!nodeVersion) {
+        try {
+            // Try parsing the file as an NPM `package.json`
+            // file.
+            nodeVersion = (_b = JSON.parse(contents).engines) === null || _b === void 0 ? void 0 : _b.node;
+            if (!nodeVersion)
+                throw new Error();
+        }
+        catch (err) {
+            // In the case of an unknown format,
+            // return as is and evaluate the version separately.
+            nodeVersion = contents.trim();
+        }
     }
-    // In the case of an unknown format,
-    // return as is and evaluate the version separately.
-    return contents.trim();
+    return nodeVersion;
 }
 exports.parseNodeVersionFile = parseNodeVersionFile;
 function isLatestSyntax(versionSpec) {
diff --git a/src/installer.ts b/src/installer.ts
index 019f2eda..193ff16a 100644
--- a/src/installer.ts
+++ b/src/installer.ts
@@ -495,16 +495,26 @@ function translateArchToDistUrl(arch: string): string {
 }
 
 export function parseNodeVersionFile(contents: string): string {
-  const found = contents.match(/^(?:nodejs\s+)?v?(?<version>[^\s]+)$/m);
-  const nodeVersion = found?.groups?.version;
+  let nodeVersion: string | undefined;
 
-  if (nodeVersion) {
-    return nodeVersion;
+  const found = contents.match(/^(?:nodejs\s+)?v?(?<version>[^\s]+)$/m);
+  nodeVersion = found?.groups?.version;
+
+  if (!nodeVersion) {
+    try {
+      // Try parsing the file as an NPM `package.json`
+      // file.
+      nodeVersion = JSON.parse(contents).engines?.node;
+
+      if (!nodeVersion) throw new Error();
+    } catch (err) {
+      // In the case of an unknown format,
+      // return as is and evaluate the version separately.
+      nodeVersion = contents.trim();
+    }
   }
 
-  // In the case of an unknown format,
-  // return as is and evaluate the version separately.
-  return contents.trim();
+  return nodeVersion as string;
 }
 
 function isLatestSyntax(versionSpec): boolean {