Compare commits

..

11 Commits

Author SHA1 Message Date
Steven
fb4f477325 Publish Stable
- vercel@27.0.2
 - @vercel/fs-detectors@1.0.2
 - @vercel/node@2.4.2
2022-07-11 16:21:30 -04:00
P.B. To
016bff848e [fs-detectors] process detectFramework in parallel (#8128)
The Vercel API's `detect-framework` API contacts GitHub's API using its own file adapter. 

However, currently the `detectFramework` function in the Vercel CLI (on which the API depends) calls the file system adapter in series using a `for` loop. This results in large amounts of lag when in a networked situation such as calling GitHub's API as each API call takes a couple hundred milliseconds.

I propose changing `detectFramework` to process the directories it scans in parallel.

### 📋 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

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2022-07-11 20:16:04 +00:00
Steven
183e411f7c [cli] Remove DEBUG=corepack env var (#8131)
This debug log was originally added in #7871 because corepack has no output by default. In particular, it was nice to see the first deployment was not stalled when the package manager is being installed.

That being said, this gets noisy really fast because cache detections also print a log line.
For example, here's a deployment that prints 3 times:

```
Detected ENABLE_EXPERIMENTAL_COREPACK=1 and "npm@8.10.0" in package.json
Running "install" command: `npm install --prefix=.. --no-audit --engine-strict=false`...
2022-07-11T18:27:00.696Z corepack Reusing npm@8.10.0
356 packages are looking for funding
Running "npm run vercel-build"
2022-07-11T18:27:06.664Z corepack Reusing npm@8.10.0
> front@0.0.0 vercel-build
> npm run buildonly && npm run build:rss
2022-07-11T18:27:07.088Z corepack Reusing npm@8.10.0
> front@0.0.0 buildonly
> next build
```

I think its best to let users add this env var themselves if they want to debug what corepack is doing, so this PR removes that environment variable.
2022-07-11 19:31:04 +00:00
Sean Massa
070e300148 [node] add links to edge error messages (#8048)
Add links to some Edge errors.

---

Follow up to: https://github.com/vercel/vercel/pull/8007
2022-07-11 18:50:15 +00:00
Steven
cbdf9b4a88 [cli] Fix beta label (#8129)
This PR is a follow up to #7991 which incorrectly used a the empty string instead of empty array

- Maybe related to #8125
2022-07-11 18:05:36 +00:00
Steven
ec9b55dc81 Publish Stable
- vercel@27.0.1
 - @vercel/remix@1.0.7
2022-07-10 15:04:09 -04:00
Leon Salsiccia
06829bc21a [remix] fix monorepo support (#8077)
Co-authored-by: Steven <steven@ceriously.com>
2022-07-10 13:28:39 -04:00
Matthew Stanciu
628071f659 [cli] Debug log error messages in create-git-meta (#8112)
This is a follow-up to #8094 which debug logs errors.

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] 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
2022-07-08 19:30:28 +00:00
Matthew Stanciu
5a7461dfe3 [cli] Explicitly use vc project vs. vc projects (#8113)
This is a follow-up to #8091 which:

- Makes `vc project` the default command, with `vc projects` aliased to `vc project` (previously it was not clear in the code which one was the "real" command)
- Makes some helper names for `ls` more specific

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] 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
2022-07-08 18:56:35 +00:00
Sean Massa
599f8f675c [tests] remove commented code (#8109)
Removes a leftover commented line.
2022-07-08 16:52:55 +00:00
Steven
0a8bc494fc [tests] Try building with ENABLE_VC_BUILD (#8110)
This will dogfood `vc build` which happens to improve this deployment time from 4 min down to 2 min.
2022-07-08 12:30:03 -04:00
23 changed files with 130 additions and 94 deletions

View File

@@ -5,7 +5,8 @@
"description": "API for the vercel/vercel repo",
"main": "index.js",
"scripts": {
"vercel-build": "node ../utils/run.js build all"
"//TODO": "We should add this pkg to yarn workspaces",
"vercel-build": "cd .. && yarn install && yarn vercel-build"
},
"dependencies": {
"@sentry/node": "5.11.1",

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "27.0.0",
"version": "27.0.2",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -46,10 +46,10 @@
"@vercel/go": "2.0.5",
"@vercel/hydrogen": "0.0.2",
"@vercel/next": "3.1.4",
"@vercel/node": "2.4.1",
"@vercel/node": "2.4.2",
"@vercel/python": "3.0.5",
"@vercel/redwood": "1.0.6",
"@vercel/remix": "1.0.6",
"@vercel/remix": "1.0.7",
"@vercel/ruby": "1.3.13",
"@vercel/static-build": "1.0.5",
"update-notifier": "5.1.0"
@@ -98,7 +98,7 @@
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.1.0",
"@vercel/frameworks": "1.1.0",
"@vercel/fs-detectors": "1.0.1",
"@vercel/fs-detectors": "1.0.2",
"@vercel/ncc": "0.24.0",
"@zeit/fun": "0.11.2",
"@zeit/source-map-support": "0.6.2",

View File

@@ -336,8 +336,7 @@ export default async function main(client: Client): Promise<number> {
const buildResults: Map<Builder, BuildResult> = new Map();
const overrides: PathOverride[] = [];
const repoRootPath = cwd;
const rootPackageJsonPath = repoRootPath || workPath;
const corepackShimDir = await initCorepack({ cwd, rootPackageJsonPath });
const corepackShimDir = await initCorepack({ repoRootPath });
for (const build of builds) {
if (typeof build.src !== 'string') continue;

View File

@@ -25,8 +25,8 @@ export default new Map([
['logout', 'logout'],
['logs', 'logs'],
['ls', 'list'],
['project', 'projects'],
['projects', 'projects'],
['project', 'project'],
['projects', 'project'],
['pull', 'pull'],
['remove', 'remove'],
['rm', 'remove'],

View File

@@ -173,7 +173,7 @@ const main = async () => {
const targetOrSubcommand = argv._[2];
// Currently no beta commands - add here as needed
const betaCommands: string[] = [''];
const betaCommands: string[] = [];
if (betaCommands.includes(targetOrSubcommand)) {
console.log(
`${chalk.grey(
@@ -653,7 +653,7 @@ const main = async () => {
case 'logout':
func = require('./commands/logout').default;
break;
case 'projects':
case 'project':
func = require('./commands/project').default;
break;
case 'pull':

View File

@@ -6,11 +6,9 @@ import { VERCEL_DIR } from '../projects/link';
import readJSONFile from '../read-json-file';
export async function initCorepack({
cwd,
rootPackageJsonPath,
repoRootPath,
}: {
cwd: string;
rootPackageJsonPath: string;
repoRootPath: string;
}): Promise<string | null> {
if (process.env.ENABLE_EXPERIMENTAL_COREPACK !== '1') {
// Since corepack is experimental, we need to exit early
@@ -18,7 +16,7 @@ export async function initCorepack({
return null;
}
const pkg = await readJSONFile<PackageJson>(
join(rootPackageJsonPath, 'package.json')
join(repoRootPath, 'package.json')
);
if (pkg instanceof CantParseJSONFile) {
console.warn(
@@ -32,16 +30,13 @@ export async function initCorepack({
console.log(
`Detected ENABLE_EXPERIMENTAL_COREPACK=1 and "${pkg.packageManager}" in package.json`
);
const corepackRootDir = join(cwd, VERCEL_DIR, 'cache', 'corepack');
const corepackRootDir = join(repoRootPath, VERCEL_DIR, 'cache', 'corepack');
const corepackHomeDir = join(corepackRootDir, 'home');
const corepackShimDir = join(corepackRootDir, 'shim');
await fs.mkdirp(corepackHomeDir);
await fs.mkdirp(corepackShimDir);
process.env.COREPACK_HOME = corepackHomeDir;
process.env.PATH = `${corepackShimDir}${delimiter}${process.env.PATH}`;
process.env.DEBUG = process.env.DEBUG
? `corepack,${process.env.DEBUG}`
: 'corepack';
const pkgManagerName = pkg.packageManager.split('@')[0];
// We must explicitly call `corepack enable npm` since `corepack enable`
// doesn't work with npm. See https://github.com/nodejs/corepack/pull/24
@@ -72,11 +67,4 @@ export function cleanupCorepack(corepackShimDir: string) {
''
);
}
if (process.env.DEBUG) {
if (process.env.DEBUG === 'corepack') {
delete process.env.DEBUG;
} else {
process.env.DEBUG = process.env.DEBUG.replace('corepack,', '');
}
}
}

View File

@@ -9,11 +9,11 @@ import { Output } from '../output';
export function isDirty(directory: string, output: Output): Promise<boolean> {
return new Promise(resolve => {
exec('git status -s', { cwd: directory }, function (err, stdout, stderr) {
if (err) return resolve(false);
if (stderr) {
output.debug(
`Failed to determine if git repo has been modified: ${stderr.trim()}`
);
let debugMessage = `Failed to determine if Git repo has been modified:`;
if (err || stderr) {
if (err) debugMessage += `\n${err}`;
if (stderr) debugMessage += `\n${stderr.trim()}`;
output.debug(debugMessage);
return resolve(false);
}
resolve(stdout.trim().length > 0);
@@ -64,7 +64,10 @@ export async function createGitMeta(
return;
}
const [commit, dirty] = await Promise.all([
getLastCommit(directory).catch(() => {
getLastCommit(directory).catch(err => {
output.debug(
`Failed to get last commit. The directory is likely not a Git repo, there are no latest commits, or it is corrupted.\n${err}`
);
return;
}),
isDirty(directory, output),

View File

@@ -1,4 +1,4 @@
export function getDataFromIntro(output: string): {
export function pluckIdentifiersFromDeploymentList(output: string): {
project: string | undefined;
org: string | undefined;
} {
@@ -11,7 +11,7 @@ export function getDataFromIntro(output: string): {
};
}
export function parseTable(output: string): string[] {
export function parseSpacedTableRow(output: string): string[] {
return output
.trim()
.replace(/ {1} +/g, ',')

View File

@@ -0,0 +1,23 @@
import { MockClient } from '../mocks/client';
export function readOutputStream(
client: MockClient,
length: number = 3
): Promise<string> {
return new Promise((resolve, reject) => {
const chunks: Buffer[] = [];
const timeout = setTimeout(() => {
reject();
}, 3000);
client.stderr.resume();
client.stderr.on('data', chunk => {
chunks.push(chunk);
if (chunks.length === length) {
clearTimeout(timeout);
resolve(chunks.toString().replace(/,/g, ''));
}
});
client.stderr.on('error', reject);
});
}

View File

@@ -1323,12 +1323,7 @@ test('[vc projects] should create a project successfully', async t => {
Math.random().toString(36).split('.')[1]
}`;
const vc = execa(binaryPath, [
'projects',
'add',
projectName,
...defaultArgs,
]);
const vc = execa(binaryPath, ['project', 'add', projectName, ...defaultArgs]);
await waitForPrompt(vc, chunk =>
chunk.includes(`Success! Project ${projectName} added`)
@@ -1339,7 +1334,7 @@ test('[vc projects] should create a project successfully', async t => {
// creating the same project again should succeed
const vc2 = execa(binaryPath, [
'projects',
'project',
'add',
projectName,
...defaultArgs,

View File

@@ -35,7 +35,6 @@ export class MockClient extends Client {
scenario: Scenario;
mockServer?: Server;
private app: Express;
private mockOutput: jest.Mock<void, Parameters<Output['print']>>;
constructor() {
super({
@@ -53,13 +52,6 @@ export class MockClient extends Client {
output: new Output(new PassThrough()),
});
this.mockOutput = jest.fn();
const _print = this.output.print.bind(this.output);
this.output.print = s => {
_print(s);
this.mockOutput(s);
};
this.app = express();
this.app.use(express.json());
@@ -99,13 +91,7 @@ export class MockClient extends Client {
this._createPromptModule();
this.mockOutput.mockReset();
this.output = new Output(this.stderr);
const _print = this.output.print.bind(this.output);
this.output.print = s => {
_print(s);
this.mockOutput(s);
};
this.argv = [];
this.authConfig = {};
@@ -115,10 +101,6 @@ export class MockClient extends Client {
this.scenario = Router();
}
get outputBuffer() {
return this.mockOutput.mock.calls.map(c => c[0]).join('');
}
async startMockServer() {
this.mockServer = createServer(this.app);
await listen(this.mockServer, 0);

View File

@@ -25,8 +25,6 @@ type GetMatcherType<TP, TResult> = TP extends PromiseFunction
? (...args: Tail<Parameters<TP>>) => TResult
: TP;
//type T = GetMatcherType<typeof matchers['toOutput'], void>;
type GetMatchersType<TMatchers, TResult> = {
[P in keyof TMatchers]: GetMatcherType<TMatchers[P], TResult>;
};

View File

@@ -5,7 +5,11 @@ import { join } from 'path';
import { useTeams } from '../../mocks/team';
import { defaultProject, useProject } from '../../mocks/project';
import { useDeployment } from '../../mocks/deployment';
import { parseTable, getDataFromIntro } from '../../helpers/parse-table';
import { readOutputStream } from '../../helpers/read-output-stream';
import {
parseSpacedTableRow,
pluckIdentifiersFromDeploymentList,
} from '../../helpers/parse-table';
const fixture = (name: string) =>
join(__dirname, '../../fixtures/unit/commands/list', name);
@@ -31,10 +35,11 @@ describe('list', () => {
await list(client);
const [line0, , line2, line3] = client.outputBuffer.split('\n');
const { org } = getDataFromIntro(line0);
const header: string[] = parseTable(line2);
const data: string[] = parseTable(line3);
const output = await readOutputStream(client);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[2]);
const data: string[] = parseSpacedTableRow(output.split('\n')[3]);
data.splice(2, 1);
expect(org).toEqual(team[0].slug);
@@ -72,10 +77,11 @@ describe('list', () => {
client.setArgv(deployment.name);
await list(client);
const [line0, , line2, line3] = client.outputBuffer.split('\n');
const { org } = getDataFromIntro(line0);
const header: string[] = parseTable(line2);
const data: string[] = parseTable(line3);
const output = await readOutputStream(client);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[2]);
const data: string[] = parseSpacedTableRow(output.split('\n')[3]);
data.splice(2, 1);
expect(org).toEqual(teamSlug);

View File

@@ -4,9 +4,13 @@ import { useTeams } from '../../mocks/team';
import { defaultProject, useProject } from '../../mocks/project';
import { client } from '../../mocks/client';
import { Project } from '../../../src/types';
import { getDataFromIntro, parseTable } from '../../helpers/parse-table';
import { readOutputStream } from '../../helpers/read-output-stream';
import {
pluckIdentifiersFromDeploymentList,
parseSpacedTableRow,
} from '../../helpers/parse-table';
describe('projects', () => {
describe('project', () => {
describe('list', () => {
it('should list deployments under a user', async () => {
const user = useUser();
@@ -17,10 +21,10 @@ describe('projects', () => {
client.setArgv('project', 'ls');
await projects(client);
const [line0, , line2, line3] = client.outputBuffer.split('\n');
const { org } = getDataFromIntro(line0);
const header: string[] = parseTable(line2);
const data: string[] = parseTable(line3);
const output = await readOutputStream(client, 2);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[2]);
const data: string[] = parseSpacedTableRow(output.split('\n')[3]);
data.pop();
expect(org).toEqual(user.username);
@@ -38,10 +42,10 @@ describe('projects', () => {
client.setArgv('project', 'ls');
await projects(client);
const [line0, , line2, line3] = client.outputBuffer.split('\n');
const { org } = getDataFromIntro(line0);
const header: string[] = parseTable(line2);
const data: string[] = parseTable(line3);
const output = await readOutputStream(client, 2);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[2]);
const data: string[] = parseSpacedTableRow(output.split('\n')[3]);
data.pop();
expect(org).toEqual(team[0].slug);

View File

@@ -135,7 +135,15 @@ describe('createGitMeta', () => {
await fs.copy(directory, tmpDir);
await fs.rename(join(tmpDir, 'git'), join(tmpDir, '.git'));
client.output.debugEnabled = true;
const data = await createGitMeta(tmpDir, client.output);
await expect(client.stderr).toOutput(
`Failed to get last commit. The directory is likely not a Git repo, there are no latest commits, or it is corrupted.`
);
await expect(client.stderr).toOutput(
`Failed to determine if Git repo has been modified:`
);
expect(data).toBeUndefined();
} finally {
await fs.remove(tmpDir);

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "1.0.1",
"version": "1.0.2",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View File

@@ -80,11 +80,13 @@ export async function detectFramework({
fs,
frameworkList,
}: DetectFrameworkOptions): Promise<string | null> {
for (const framework of frameworkList) {
if (await matches(fs, framework)) {
return framework.slug;
}
}
return null;
const result = await Promise.all(
frameworkList.map(async frameworkMatch => {
if (await matches(fs, frameworkMatch)) {
return frameworkMatch.slug;
}
return null;
})
);
return result.find(res => res !== null) ?? null;
}

View File

@@ -252,6 +252,19 @@ describe('DetectorFilesystem', () => {
expect(await detectFramework({ fs, frameworkList })).toBe('nextjs');
});
it('Detect frameworks based on ascending order in framework list', async () => {
const fs = new VirtualFilesystem({
'package.json': JSON.stringify({
dependencies: {
next: '9.0.0',
gatsby: '4.18.0',
},
}),
});
expect(await detectFramework({ fs, frameworkList })).toBe('nextjs');
});
it('Detect Nuxt.js', async () => {
const fs = new VirtualFilesystem({
'package.json': JSON.stringify({

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node",
"version": "2.4.1",
"version": "2.4.2",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",

View File

@@ -193,7 +193,7 @@ async function compileUserCode(entrypoint: string) {
let edgeHandler = module.exports.default;
if (!edgeHandler) {
throw new Error('No default export was found. Add a default export to handle requests.');
throw new Error('No default export was found. Add a default export to handle requests. Learn more: https://vercel.link/creating-edge-middleware');
}
let response = await edgeHandler(event.request, event);
@@ -305,7 +305,7 @@ function parseRuntime(
throw new Error(
`Invalid function runtime "${runtime}" for "${entrypoint}". Valid runtimes are: ${JSON.stringify(
validRuntimes
)}`
)}. Learn more: https://vercel.link/creating-edge-functions`
);
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/remix",
"version": "1.0.6",
"version": "1.0.7",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",

View File

@@ -187,6 +187,17 @@ export const build: BuildV2 = async ({
// Explicit directory path the server output will be
serverBuildPath = join(remixConfig.serverBuildDirectory, 'index.js');
}
// Also check for whether were in a monorepo.
// If we are, prepend the app root directory from config onto the build path.
// e.g. `/apps/my-remix-app/api/index.js`
const isMonorepo = repoRootPath && repoRootPath !== workPath;
if (isMonorepo && config.projectSettings?.rootDirectory) {
serverBuildPath = join(
config.projectSettings.rootDirectory,
serverBuildPath
);
}
} catch (err: any) {
// Ignore error if `remix.config.js` does not exist
if (err.code !== 'MODULE_NOT_FOUND') throw err;
@@ -196,6 +207,7 @@ export const build: BuildV2 = async ({
glob('**', join(entrypointFsDirname, 'public')),
createRenderFunction(
entrypointFsDirname,
repoRootPath,
serverBuildPath,
needsHandler,
nodeVersion
@@ -230,6 +242,7 @@ function hasScript(scriptName: string, pkg: PackageJson | null) {
}
async function createRenderFunction(
entrypointDir: string,
rootDir: string,
serverBuildPath: string,
needsHandler: boolean,
@@ -250,6 +263,7 @@ async function createRenderFunction(
// Trace the handler with `@vercel/nft`
const trace = await nodeFileTrace([handlerPath], {
base: rootDir,
processCwd: entrypointDir,
});
for (const warning of trace.warnings) {

View File

@@ -20,7 +20,7 @@
],
"build": {
"env": {
"ENABLE_FILE_SYSTEM_API": "1"
"ENABLE_VC_BUILD": "1"
}
},
"github": {