mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-09 21:07:46 +00:00
[cli] Add node_modules/.bin to PATH instead of running npx/yarn run (#8890)
`runDevCommand()` assumes the dev command is an npm package and thus uses `npx` or `yarn run` to execute it. In the case of a Hugo-based app, there is no npm package, so we want spawn to find Hugo in the `PATH`. Then for Node-based apps, instead of `npx`, spawn should find the command since `node_modules/.bin` has been added to the `PATH`. ### Related Issues > https://github.com/vercel/customer-issues/issues/871 Note: This PR is a recreation of https://github.com/vercel/vercel/pull/8864 because prettier changed a bunch of Hugo files, which was bloating the original PR. ### 📋 Checklist <!-- Please keep your PR as a Draft until the checklist is complete --> #### Tests - [x] The code changed/added as part of this PR has been covered with tests - [x] All tests pass locally with `yarn test-unit` #### Code Review - [ ] This PR has a concise title and thorough description useful to a reviewer - [ ] Issue from task tracker has a link to this PR
This commit is contained in:
@@ -172,7 +172,6 @@
|
|||||||
"typescript": "4.7.4",
|
"typescript": "4.7.4",
|
||||||
"universal-analytics": "0.4.20",
|
"universal-analytics": "0.4.20",
|
||||||
"utility-types": "2.1.0",
|
"utility-types": "2.1.0",
|
||||||
"which": "2.0.2",
|
|
||||||
"write-json-file": "2.2.0",
|
"write-json-file": "2.2.0",
|
||||||
"xdg-app-paths": "5.1.0",
|
"xdg-app-paths": "5.1.0",
|
||||||
"yauzl-promise": "2.1.3"
|
"yauzl-promise": "2.1.3"
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import directoryTemplate from 'serve-handler/src/directory';
|
|||||||
import getPort from 'get-port';
|
import getPort from 'get-port';
|
||||||
import isPortReachable from 'is-port-reachable';
|
import isPortReachable from 'is-port-reachable';
|
||||||
import deepEqual from 'fast-deep-equal';
|
import deepEqual from 'fast-deep-equal';
|
||||||
import which from 'which';
|
|
||||||
import npa from 'npm-package-arg';
|
import npa from 'npm-package-arg';
|
||||||
import type { ChildProcess } from 'child_process';
|
import type { ChildProcess } from 'child_process';
|
||||||
|
|
||||||
@@ -33,6 +32,7 @@ import {
|
|||||||
Builder,
|
Builder,
|
||||||
cloneEnv,
|
cloneEnv,
|
||||||
Env,
|
Env,
|
||||||
|
getNodeBinPath,
|
||||||
StartDevServerResult,
|
StartDevServerResult,
|
||||||
FileFsRef,
|
FileFsRef,
|
||||||
PackageJson,
|
PackageJson,
|
||||||
@@ -2238,6 +2238,10 @@ export default class DevServer {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// add the node_modules/.bin directory to the PATH
|
||||||
|
const nodeBinPath = await getNodeBinPath({ cwd });
|
||||||
|
env.PATH = `${nodeBinPath}${path.delimiter}${env.PATH}`;
|
||||||
|
|
||||||
// This is necesary so that the dev command in the Project
|
// This is necesary so that the dev command in the Project
|
||||||
// will work cross-platform (especially Windows).
|
// will work cross-platform (especially Windows).
|
||||||
let command = devCommand
|
let command = devCommand
|
||||||
@@ -2252,22 +2256,6 @@ export default class DevServer {
|
|||||||
})}`
|
})}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const isNpxAvailable = await which('npx')
|
|
||||||
.then(() => true)
|
|
||||||
.catch(() => false);
|
|
||||||
|
|
||||||
if (isNpxAvailable) {
|
|
||||||
command = `npx --no-install ${command}`;
|
|
||||||
} else {
|
|
||||||
const isYarnAvailable = await which('yarn')
|
|
||||||
.then(() => true)
|
|
||||||
.catch(() => false);
|
|
||||||
|
|
||||||
if (isYarnAvailable) {
|
|
||||||
command = `yarn run --silent ${command}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.output.debug(`Spawning dev command: ${command}`);
|
this.output.debug(`Spawning dev command: ${command}`);
|
||||||
|
|
||||||
const proxyPort = new RegExp(port.toString(), 'g');
|
const proxyPort = new RegExp(port.toString(), 'g');
|
||||||
|
|||||||
@@ -104,13 +104,38 @@ test(
|
|||||||
|
|
||||||
test('[vercel dev] 08-hugo', async () => {
|
test('[vercel dev] 08-hugo', async () => {
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
// Update PATH to find the Hugo executable installed via GH Actions
|
// 1. run the test without Hugo in the PATH
|
||||||
|
let tester = await testFixtureStdio(
|
||||||
|
'08-hugo',
|
||||||
|
async () => {
|
||||||
|
throw new Error('Expected dev server to fail to be ready');
|
||||||
|
},
|
||||||
|
{
|
||||||
|
readyTimeout: 2000,
|
||||||
|
|
||||||
|
// Important: for the first test, we MUST deploy this app so that the
|
||||||
|
// framework (e.g. Hugo) will be detected by the server and associated
|
||||||
|
// with the project since `vc dev` doesn't do framework detection
|
||||||
|
skipDeploy: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await expect(tester()).rejects.toThrow(
|
||||||
|
new Error('Dev server timed out while waiting to be ready')
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Update PATH to find the Hugo executable installed via GH Actions
|
||||||
process.env.PATH = `${resolve(fixture('08-hugo'))}${delimiter}${
|
process.env.PATH = `${resolve(fixture('08-hugo'))}${delimiter}${
|
||||||
process.env.PATH
|
process.env.PATH
|
||||||
}`;
|
}`;
|
||||||
const tester = testFixtureStdio('08-hugo', async (testPath: any) => {
|
|
||||||
|
// 3. Rerun the test now that Hugo is in the PATH
|
||||||
|
tester = testFixtureStdio(
|
||||||
|
'08-hugo',
|
||||||
|
async (testPath: any) => {
|
||||||
await testPath(200, '/', /Hugo/m);
|
await testPath(200, '/', /Hugo/m);
|
||||||
});
|
},
|
||||||
|
{ skipDeploy: true }
|
||||||
|
);
|
||||||
await tester();
|
await tester();
|
||||||
} else {
|
} else {
|
||||||
console.log(`Skipping 08-hugo on platform ${process.platform}`);
|
console.log(`Skipping 08-hugo on platform ${process.platform}`);
|
||||||
|
|||||||
@@ -61,8 +61,13 @@ function fetchWithRetry(url, opts = {}) {
|
|||||||
|
|
||||||
function createResolver() {
|
function createResolver() {
|
||||||
let resolver;
|
let resolver;
|
||||||
const p = new Promise(res => (resolver = res));
|
let rejector;
|
||||||
|
const p = new Promise((resolve, reject) => {
|
||||||
|
resolver = resolve;
|
||||||
|
rejector = reject;
|
||||||
|
});
|
||||||
p.resolve = resolver;
|
p.resolve = resolver;
|
||||||
|
p.reject = rejector;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +279,13 @@ async function testFixture(directory, opts = {}, args = []) {
|
|||||||
function testFixtureStdio(
|
function testFixtureStdio(
|
||||||
directory,
|
directory,
|
||||||
fn,
|
fn,
|
||||||
{ expectedCode = 0, skipDeploy, isExample, projectSettings } = {}
|
{
|
||||||
|
expectedCode = 0,
|
||||||
|
skipDeploy,
|
||||||
|
isExample,
|
||||||
|
projectSettings,
|
||||||
|
readyTimeout = 0,
|
||||||
|
} = {}
|
||||||
) {
|
) {
|
||||||
return async () => {
|
return async () => {
|
||||||
const nodeMajor = Number(process.versions.node.split('.')[0]);
|
const nodeMajor = Number(process.versions.node.split('.')[0]);
|
||||||
@@ -385,6 +396,18 @@ function testFixtureStdio(
|
|||||||
const readyResolver = createResolver();
|
const readyResolver = createResolver();
|
||||||
const exitResolver = createResolver();
|
const exitResolver = createResolver();
|
||||||
|
|
||||||
|
// By default, tests will wait 6 minutes for the dev server to be ready and
|
||||||
|
// perform the tests, however a `readyTimeout` can be used to reduce the
|
||||||
|
// wait time if the dev server is expected to fail to start or hang
|
||||||
|
let readyTimer = null;
|
||||||
|
if (readyTimeout > 0) {
|
||||||
|
readyTimer = setTimeout(() => {
|
||||||
|
readyResolver.reject(
|
||||||
|
new Error('Dev server timed out while waiting to be ready')
|
||||||
|
);
|
||||||
|
}, readyTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let printedOutput = false;
|
let printedOutput = false;
|
||||||
|
|
||||||
@@ -424,6 +447,7 @@ function testFixtureStdio(
|
|||||||
stderr += data;
|
stderr += data;
|
||||||
|
|
||||||
if (stripAnsi(data).includes('Ready! Available at')) {
|
if (stripAnsi(data).includes('Ready! Available at')) {
|
||||||
|
clearTimeout(readyTimer);
|
||||||
readyResolver.resolve();
|
readyResolver.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,5 +531,6 @@ module.exports = {
|
|||||||
shouldSkip,
|
shouldSkip,
|
||||||
fixture,
|
fixture,
|
||||||
fetch,
|
fetch,
|
||||||
|
fetchWithRetry,
|
||||||
validateResponseHeaders,
|
validateResponseHeaders,
|
||||||
};
|
};
|
||||||
|
|||||||
14
yarn.lock
14
yarn.lock
@@ -13677,13 +13677,6 @@ which-module@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||||
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
|
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
|
||||||
|
|
||||||
which@2.0.2, which@^2.0.1:
|
|
||||||
version "2.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
|
||||||
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
|
|
||||||
dependencies:
|
|
||||||
isexe "^2.0.0"
|
|
||||||
|
|
||||||
which@^1.2.9, which@^1.3.1:
|
which@^1.2.9, which@^1.3.1:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||||
@@ -13691,6 +13684,13 @@ which@^1.2.9, which@^1.3.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
isexe "^2.0.0"
|
isexe "^2.0.0"
|
||||||
|
|
||||||
|
which@^2.0.1:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||||
|
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
|
||||||
|
dependencies:
|
||||||
|
isexe "^2.0.0"
|
||||||
|
|
||||||
wide-align@^1.1.0:
|
wide-align@^1.1.0:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
|
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
|
||||||
|
|||||||
Reference in New Issue
Block a user