mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 12:57:46 +00:00
Compare commits
23 Commits
create-inp
...
@vercel-in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c42649916 | ||
|
|
c1891f64f4 | ||
|
|
2fc46c0d47 | ||
|
|
73b112b1f7 | ||
|
|
548afd371a | ||
|
|
d0877c1023 | ||
|
|
326fe0f0e6 | ||
|
|
346e665bb0 | ||
|
|
9a27ccbe6d | ||
|
|
3b6d96891b | ||
|
|
07d8d11989 | ||
|
|
8a16e8eb65 | ||
|
|
63e9de8932 | ||
|
|
c6d588201b | ||
|
|
0b1ff4bf62 | ||
|
|
14102ddf56 | ||
|
|
49f88975b9 | ||
|
|
13f3db06a8 | ||
|
|
78e2c012f9 | ||
|
|
f707f13821 | ||
|
|
9ed967034d | ||
|
|
1825b58df8 | ||
|
|
c82a55c460 |
@@ -1,2 +0,0 @@
|
||||
---
|
||||
---
|
||||
@@ -1,2 +0,0 @@
|
||||
---
|
||||
---
|
||||
@@ -1,2 +0,0 @@
|
||||
---
|
||||
---
|
||||
@@ -1,3 +0,0 @@
|
||||
---
|
||||
---
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'vercel': patch
|
||||
---
|
||||
|
||||
Replace `inquirer` with `@inquirer/prompts`
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@vercel/remix-builder": patch
|
||||
---
|
||||
|
||||
Add `mjs` and `mts` extensions to vite detection
|
||||
@@ -1,2 +0,0 @@
|
||||
---
|
||||
---
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
---
|
||||
|
||||
Update CODEOWNERS
|
||||
@@ -1,2 +0,0 @@
|
||||
---
|
||||
---
|
||||
22
.github/CODEOWNERS
vendored
22
.github/CODEOWNERS
vendored
@@ -2,17 +2,17 @@
|
||||
# https://help.github.com/en/articles/about-code-owners
|
||||
|
||||
# Restricted Paths
|
||||
* @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55
|
||||
/.github/workflows @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @ijjk
|
||||
/packages/fs-detectors @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @agadzik @chloetedder
|
||||
/packages/next @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @timneutkens @ijjk @ztanner @huozhi @Ethan-Arrowood @styfle
|
||||
/packages/routing-utils @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @ijjk
|
||||
/packages/static-build @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55
|
||||
/packages/edge @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @vercel/compute
|
||||
/examples @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @leerob
|
||||
/examples/create-react-app @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @Timer
|
||||
/examples/nextjs @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @timneutkens @ijjk @ztanner @huozhi @Ethan-Arrowood @styfle
|
||||
/packages/node @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @Kikobeats
|
||||
* @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads
|
||||
/.github/workflows @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @ijjk
|
||||
/packages/fs-detectors @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @agadzik @chloetedder
|
||||
/packages/next @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @timneutkens @ijjk @ztanner @huozhi @Ethan-Arrowood @styfle
|
||||
/packages/routing-utils @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @ijjk
|
||||
/packages/static-build @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads
|
||||
/packages/edge @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @vercel/compute
|
||||
/examples @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @leerob
|
||||
/examples/create-react-app @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @Timer
|
||||
/examples/nextjs @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @timneutkens @ijjk @ztanner @huozhi @Ethan-Arrowood @styfle
|
||||
/packages/node @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @Kikobeats
|
||||
|
||||
# Unrestricted Paths
|
||||
.changeset/
|
||||
|
||||
62
.github/dependabot.yml
vendored
62
.github/dependabot.yml
vendored
@@ -90,8 +90,15 @@ updates:
|
||||
prefix: '[framework-fixtures]'
|
||||
package-ecosystem: 'npm'
|
||||
allow:
|
||||
- dependency-name: '@angular*'
|
||||
- dependency-name: '@ionic*'
|
||||
- dependency-name: '@angular*'
|
||||
ignore:
|
||||
- dependency-name: '@ionic*'
|
||||
update-types:
|
||||
['version-update:semver-major', 'version-update:semver-patch']
|
||||
- dependency-name: '@angular*'
|
||||
update-types:
|
||||
['version-update:semver-major', 'version-update:semver-patch']
|
||||
groups:
|
||||
core:
|
||||
patterns:
|
||||
@@ -100,6 +107,35 @@ updates:
|
||||
update-types:
|
||||
- 'minor'
|
||||
|
||||
- directory: /packages/static-build/test/fixtures/ionic-react-v7
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
open-pull-requests-limit: 1
|
||||
reviewers:
|
||||
- 'trek'
|
||||
- 'TooTallNate'
|
||||
- 'EndangeredMassa'
|
||||
commit-message:
|
||||
prefix: '[framework-fixtures]'
|
||||
package-ecosystem: 'npm'
|
||||
allow:
|
||||
- dependency-name: '@ionic*'
|
||||
- dependency-name: 'react*'
|
||||
ignore:
|
||||
- dependency-name: '@ionic*'
|
||||
update-types:
|
||||
['version-update:semver-major', 'version-update:semver-patch']
|
||||
- dependency-name: 'react*'
|
||||
update-types:
|
||||
['version-update:semver-major', 'version-update:semver-patch']
|
||||
groups:
|
||||
core:
|
||||
patterns:
|
||||
- '@ionic*'
|
||||
- 'react*'
|
||||
update-types:
|
||||
- 'minor'
|
||||
|
||||
- directory: /packages/static-build/test/fixtures/nuxt-v3
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
@@ -139,6 +175,30 @@ updates:
|
||||
update-types:
|
||||
- 'minor'
|
||||
|
||||
- directory: /packages/static-build/test/fixtures/preact-v10
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
open-pull-requests-limit: 1
|
||||
reviewers:
|
||||
- 'trek'
|
||||
- 'TooTallNate'
|
||||
- 'EndangeredMassa'
|
||||
commit-message:
|
||||
prefix: '[framework-fixtures]'
|
||||
package-ecosystem: 'npm'
|
||||
allow:
|
||||
- dependency-name: 'preact*'
|
||||
ignore:
|
||||
- dependency-name: 'preact*'
|
||||
update-types:
|
||||
['version-update:semver-major', 'version-update:semver-patch']
|
||||
groups:
|
||||
core:
|
||||
patterns:
|
||||
- 'preact*'
|
||||
update-types:
|
||||
- 'minor'
|
||||
|
||||
- directory: /packages/static-build/test/fixtures/stencil-v4
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
|
||||
@@ -4,7 +4,7 @@ import { lstatSync, readdirSync } from 'fs';
|
||||
export async function deployExample(filename: string) {
|
||||
const { testDeployment } = require('../../test/lib/deployment/test-deployment.js');
|
||||
const example = basename(filename).replace(/\.test\.ts$/, '');
|
||||
await testDeployment(join(filename, '..', '..', '..', example));
|
||||
await testDeployment(join(process.cwd(), example));
|
||||
}
|
||||
|
||||
export function getExamples() {
|
||||
|
||||
@@ -8,6 +8,6 @@ menu:
|
||||
main: {}
|
||||
---
|
||||
|
||||
This is an example of a custom shortcode that you can put right into your content. You will need to add a form action to the the shortcode to make it work. Check out [Formspree](https://formspree.io/) for a simple, free form service.
|
||||
This is an example of a custom shortcode that you can put right into your content. You will need to add a form action to the shortcode to make it work. Check out [Formspree](https://formspree.io/) for a simple, free form service.
|
||||
|
||||
{{< form-contact action="https://example.com" >}}
|
||||
|
||||
3
examples/package.json
vendored
3
examples/package.json
vendored
@@ -9,7 +9,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.4.1",
|
||||
"@vercel/frameworks": "3.0.0"
|
||||
"@vercel/build-utils": "7.11.0",
|
||||
"@vercel/frameworks": "3.0.1"
|
||||
},
|
||||
"version": null
|
||||
}
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @vercel-internals/types
|
||||
|
||||
## 1.0.29
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`73b112b1f`](https://github.com/vercel/vercel/commit/73b112b1f74480e1bb941e1b754105fc7dace401)]:
|
||||
- @vercel/build-utils@7.11.0
|
||||
|
||||
## 1.0.28
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`1825b58df`](https://github.com/vercel/vercel/commit/1825b58df8d783e79f0addf262618f422246f4b3)]:
|
||||
- @vercel/build-utils@7.10.0
|
||||
|
||||
## 1.0.27
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@vercel-internals/types",
|
||||
"version": "1.0.27",
|
||||
"version": "1.0.29",
|
||||
"types": "index.d.ts",
|
||||
"main": "index.d.ts",
|
||||
"files": [
|
||||
@@ -10,7 +10,7 @@
|
||||
"dependencies": {
|
||||
"@types/node": "14.14.31",
|
||||
"@vercel-internals/constants": "1.0.4",
|
||||
"@vercel/build-utils": "7.9.1",
|
||||
"@vercel/build-utils": "7.11.0",
|
||||
"@vercel/routing-utils": "3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"source-map-support": "0.5.12",
|
||||
"ts-eager": "2.0.2",
|
||||
"ts-jest": "29.1.0",
|
||||
"turbo": "1.12.5",
|
||||
"turbo": "1.13.0",
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @vercel/build-utils
|
||||
|
||||
## 7.11.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Add `getOsRelease()` and `getProvidedRuntime()` functions ([#11370](https://github.com/vercel/vercel/pull/11370))
|
||||
|
||||
## 7.10.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Allow environment variables to be specified for `EdgeFunction` ([#11029](https://github.com/vercel/vercel/pull/11029))
|
||||
|
||||
## 7.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "7.9.1",
|
||||
"version": "7.11.0",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Files, FunctionFramework } from './types';
|
||||
import type { Env, Files, FunctionFramework } from './types';
|
||||
|
||||
/**
|
||||
* An Edge Functions output
|
||||
@@ -23,6 +23,11 @@ export class EdgeFunction {
|
||||
*/
|
||||
entrypoint: string;
|
||||
|
||||
/**
|
||||
* Environment variables for the edge function to use at runtime.
|
||||
*/
|
||||
environment?: Env;
|
||||
|
||||
/**
|
||||
* The list of files to be included in the edge function bundle.
|
||||
*/
|
||||
@@ -48,5 +53,6 @@ export class EdgeFunction {
|
||||
this.assets = params.assets;
|
||||
this.regions = params.regions;
|
||||
this.framework = params.framework;
|
||||
this.environment = params.environment;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ export {
|
||||
export { EdgeFunction } from './edge-function';
|
||||
export { readConfigFile } from './fs/read-config-file';
|
||||
export { normalizePath } from './fs/normalize-path';
|
||||
export { getOsRelease, getProvidedRuntime } from './os';
|
||||
|
||||
export * from './should-serve';
|
||||
export * from './schemas';
|
||||
|
||||
@@ -5,11 +5,7 @@ import minimatch from 'minimatch';
|
||||
import { readlink } from 'fs-extra';
|
||||
import { isSymbolicLink, isDirectory } from './fs/download';
|
||||
import streamToBuffer from './fs/stream-to-buffer';
|
||||
import type { Files, Config, FunctionFramework } from './types';
|
||||
|
||||
interface Environment {
|
||||
[key: string]: string;
|
||||
}
|
||||
import type { Config, Env, Files, FunctionFramework } from './types';
|
||||
|
||||
export type LambdaOptions = LambdaOptionsWithFiles | LambdaOptionsWithZipBuffer;
|
||||
|
||||
@@ -21,7 +17,7 @@ export interface LambdaOptionsBase {
|
||||
architecture?: LambdaArchitecture;
|
||||
memory?: number;
|
||||
maxDuration?: number;
|
||||
environment?: Environment;
|
||||
environment?: Env;
|
||||
allowQuery?: string[];
|
||||
regions?: string[];
|
||||
supportsMultiPayloads?: boolean;
|
||||
@@ -69,7 +65,7 @@ export class Lambda {
|
||||
architecture?: LambdaArchitecture;
|
||||
memory?: number;
|
||||
maxDuration?: number;
|
||||
environment: Environment;
|
||||
environment: Env;
|
||||
allowQuery?: string[];
|
||||
regions?: string[];
|
||||
/**
|
||||
|
||||
42
packages/build-utils/src/os.ts
Normal file
42
packages/build-utils/src/os.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { readFile } from 'fs-extra';
|
||||
import { isErrnoException } from '@vercel/error-utils';
|
||||
|
||||
export async function getOsRelease() {
|
||||
try {
|
||||
const data = await readFile('/etc/os-release', 'utf8');
|
||||
return await parseOsRelease(data);
|
||||
} catch (err) {
|
||||
if (isErrnoException(err) && err.code === 'ENOENT') {
|
||||
return null;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function parseOsRelease(data: string) {
|
||||
const obj: Record<string, string> = {};
|
||||
// Example file contents:
|
||||
// NAME="Amazon Linux"
|
||||
// VERSION="2023"
|
||||
// ID="amzn"
|
||||
// ID_LIKE="fedora"
|
||||
for (const line of data.trim().split('\n')) {
|
||||
const m = /(?<key>.*)="(?<value>.*)"/.exec(line);
|
||||
if (!m?.groups) {
|
||||
continue;
|
||||
}
|
||||
obj[m.groups.key] = m.groups.value;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
export async function getProvidedRuntime() {
|
||||
const os = await getOsRelease();
|
||||
if (!os) {
|
||||
return 'provided.al2023';
|
||||
}
|
||||
|
||||
return os.PRETTY_NAME === 'Amazon Linux 2'
|
||||
? 'provided.al2'
|
||||
: 'provided.al2023';
|
||||
}
|
||||
@@ -3,5 +3,8 @@
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"build": "mkdir -p public && (printf \"npm version: \" && npm -v) > public/index.txt"
|
||||
},
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,5 +6,8 @@
|
||||
"dependencies": {
|
||||
"exeggcute": "^1.0.0",
|
||||
"once": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"sharp": "0.33.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
}
|
||||
}
|
||||
|
||||
89
packages/build-utils/test/unit.get-os-release.test.ts
vendored
Normal file
89
packages/build-utils/test/unit.get-os-release.test.ts
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
import { parseOsRelease } from '../src/os';
|
||||
|
||||
describe('getOsRelease()', () => {
|
||||
it('should parse `amazonlinux:2`', async () => {
|
||||
const data = `NAME="Amazon Linux"
|
||||
VERSION="2"
|
||||
ID="amzn"
|
||||
ID_LIKE="centos rhel fedora"
|
||||
VERSION_ID="2"
|
||||
PRETTY_NAME="Amazon Linux 2"
|
||||
ANSI_COLOR="0;33"
|
||||
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"
|
||||
HOME_URL="https://amazonlinux.com/"
|
||||
SUPPORT_END="2025-06-30"
|
||||
`;
|
||||
const parsed = await parseOsRelease(data);
|
||||
expect(parsed).toMatchObject({
|
||||
ANSI_COLOR: '0;33',
|
||||
CPE_NAME: 'cpe:2.3:o:amazon:amazon_linux:2',
|
||||
HOME_URL: 'https://amazonlinux.com/',
|
||||
ID: 'amzn',
|
||||
ID_LIKE: 'centos rhel fedora',
|
||||
NAME: 'Amazon Linux',
|
||||
PRETTY_NAME: 'Amazon Linux 2',
|
||||
SUPPORT_END: '2025-06-30',
|
||||
VERSION: '2',
|
||||
VERSION_ID: '2',
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse `amazonlinux:2023`', async () => {
|
||||
const data = `NAME="Amazon Linux"
|
||||
VERSION="2023"
|
||||
ID="amzn"
|
||||
ID_LIKE="fedora"
|
||||
VERSION_ID="2023"
|
||||
PLATFORM_ID="platform:al2023"
|
||||
PRETTY_NAME="Amazon Linux 2023"
|
||||
ANSI_COLOR="0;33"
|
||||
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2023"
|
||||
HOME_URL="https://aws.amazon.com/linux/"
|
||||
BUG_REPORT_URL="https://github.com/amazonlinux/amazon-linux-2023"
|
||||
SUPPORT_END="2028-03-01"
|
||||
`;
|
||||
const parsed = await parseOsRelease(data);
|
||||
expect(parsed).toMatchObject({
|
||||
NAME: 'Amazon Linux',
|
||||
VERSION: '2023',
|
||||
ID: 'amzn',
|
||||
ID_LIKE: 'fedora',
|
||||
VERSION_ID: '2023',
|
||||
PLATFORM_ID: 'platform:al2023',
|
||||
PRETTY_NAME: 'Amazon Linux 2023',
|
||||
ANSI_COLOR: '0;33',
|
||||
CPE_NAME: 'cpe:2.3:o:amazon:amazon_linux:2023',
|
||||
HOME_URL: 'https://aws.amazon.com/linux/',
|
||||
BUG_REPORT_URL: 'https://github.com/amazonlinux/amazon-linux-2023',
|
||||
SUPPORT_END: '2028-03-01',
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse `ubuntu:jammy`', async () => {
|
||||
const data = `PRETTY_NAME="Ubuntu 22.04.3 LTS"
|
||||
NAME="Ubuntu"
|
||||
VERSION_ID="22.04"
|
||||
VERSION="22.04.3 LTS (Jammy Jellyfish)"
|
||||
VERSION_CODENAME=jammy
|
||||
ID=ubuntu
|
||||
ID_LIKE=debian
|
||||
HOME_URL="https://www.ubuntu.com/"
|
||||
SUPPORT_URL="https://help.ubuntu.com/"
|
||||
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
|
||||
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
|
||||
UBUNTU_CODENAME=jammy
|
||||
`;
|
||||
const parsed = await parseOsRelease(data);
|
||||
expect(parsed).toMatchObject({
|
||||
PRETTY_NAME: 'Ubuntu 22.04.3 LTS',
|
||||
NAME: 'Ubuntu',
|
||||
VERSION_ID: '22.04',
|
||||
VERSION: '22.04.3 LTS (Jammy Jellyfish)',
|
||||
HOME_URL: 'https://www.ubuntu.com/',
|
||||
SUPPORT_URL: 'https://help.ubuntu.com/',
|
||||
BUG_REPORT_URL: 'https://bugs.launchpad.net/ubuntu/',
|
||||
PRIVACY_POLICY_URL:
|
||||
'https://www.ubuntu.com/legal/terms-and-policies/privacy-policy',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,35 @@
|
||||
# vercel
|
||||
|
||||
## 33.6.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Handle `--repo` linked in `vc deploy --prebuilt` ([#11309](https://github.com/vercel/vercel/pull/11309))
|
||||
|
||||
- Revert "[cli] extract `isZeroConfigBuild` into utility function (#11316)" ([#11350](https://github.com/vercel/vercel/pull/11350))
|
||||
|
||||
- Replace `inquirer` with `@inquirer/prompts` ([#11321](https://github.com/vercel/vercel/pull/11321))
|
||||
|
||||
- Updated dependencies [[`73b112b1f`](https://github.com/vercel/vercel/commit/73b112b1f74480e1bb941e1b754105fc7dace401), [`346e665bb`](https://github.com/vercel/vercel/commit/346e665bb021e6034bc70c82ef336485622595fe), [`73b112b1f`](https://github.com/vercel/vercel/commit/73b112b1f74480e1bb941e1b754105fc7dace401), [`548afd371`](https://github.com/vercel/vercel/commit/548afd371aa7a9dd3a7f4c60f7f94a7084d8023e)]:
|
||||
- @vercel/go@3.1.0
|
||||
- @vercel/node@3.0.26
|
||||
- @vercel/build-utils@7.11.0
|
||||
- @vercel/static-build@2.4.6
|
||||
|
||||
## 33.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Added sunset warning to secrets command. ([#11333](https://github.com/vercel/vercel/pull/11333))
|
||||
|
||||
- Swap jest for vitest in CLI unit tests ([#11302](https://github.com/vercel/vercel/pull/11302))
|
||||
|
||||
- Updated dependencies [[`988f7b75a`](https://github.com/vercel/vercel/commit/988f7b75a27387e84fce541b844f984d2c151980), [`1825b58df`](https://github.com/vercel/vercel/commit/1825b58df8d783e79f0addf262618f422246f4b3)]:
|
||||
- @vercel/remix-builder@2.1.5
|
||||
- @vercel/build-utils@7.10.0
|
||||
- @vercel/node@3.0.25
|
||||
- @vercel/static-build@2.4.5
|
||||
|
||||
## 33.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "33.6.1",
|
||||
"version": "33.6.3",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jest --reporters=default --reporters=jest-junit --env node --verbose --bail",
|
||||
"test-unit": "pnpm test test/unit/",
|
||||
"test-unit": "pnpm vitest test/unit/",
|
||||
"test-e2e": "rimraf test/fixtures/integration && pnpm test test/integration-1.test.ts test/integration-2.test.ts test/integration-3.test.ts",
|
||||
"test-dev": "pnpm test test/dev/",
|
||||
"coverage": "codecov",
|
||||
@@ -31,25 +31,27 @@
|
||||
"node": ">= 16"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "7.9.1",
|
||||
"@vercel/build-utils": "7.11.0",
|
||||
"@vercel/fun": "1.1.0",
|
||||
"@vercel/go": "3.0.5",
|
||||
"@vercel/go": "3.1.0",
|
||||
"@vercel/hydrogen": "1.0.2",
|
||||
"@vercel/next": "4.1.6",
|
||||
"@vercel/node": "3.0.24",
|
||||
"@vercel/node": "3.0.26",
|
||||
"@vercel/python": "4.1.1",
|
||||
"@vercel/redwood": "2.0.8",
|
||||
"@vercel/remix-builder": "2.1.4",
|
||||
"@vercel/remix-builder": "2.1.5",
|
||||
"@vercel/ruby": "2.0.5",
|
||||
"@vercel/static-build": "2.4.4",
|
||||
"@vercel/static-build": "2.4.6",
|
||||
"chokidar": "3.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alex_neo/jest-expect-message": "1.0.5",
|
||||
"@edge-runtime/node-utils": "2.3.0",
|
||||
"@inquirer/core": "7.1.0",
|
||||
"@inquirer/prompts": "4.3.0",
|
||||
"@inquirer/type": "1.2.1",
|
||||
"@inquirer/checkbox": "2.2.0",
|
||||
"@inquirer/confirm": "3.1.0",
|
||||
"@inquirer/expand": "2.1.0",
|
||||
"@inquirer/input": "2.1.1",
|
||||
"@inquirer/select": "2.2.0",
|
||||
"@next/env": "11.1.2",
|
||||
"@sentry/node": "5.5.0",
|
||||
"@sindresorhus/slugify": "0.11.0",
|
||||
@@ -66,7 +68,6 @@
|
||||
"@types/glob": "7.1.1",
|
||||
"@types/http-proxy": "1.16.2",
|
||||
"@types/ini": "1.3.31",
|
||||
"@types/inquirer": "7.3.1",
|
||||
"@types/jest": "27.4.1",
|
||||
"@types/jest-expect-message": "1.0.3",
|
||||
"@types/json-parse-better-errors": "1.0.0",
|
||||
@@ -91,12 +92,13 @@
|
||||
"@types/yauzl-promise": "2.1.0",
|
||||
"@vercel-internals/constants": "1.0.4",
|
||||
"@vercel-internals/get-package-json": "1.0.0",
|
||||
"@vercel-internals/types": "1.0.27",
|
||||
"@vercel/client": "13.1.7",
|
||||
"@vercel-internals/types": "1.0.29",
|
||||
"@vercel/client": "13.1.9",
|
||||
"@vercel/error-utils": "2.0.2",
|
||||
"@vercel/frameworks": "3.0.0",
|
||||
"@vercel/fs-detectors": "5.2.1",
|
||||
"@vercel/frameworks": "3.0.1",
|
||||
"@vercel/fs-detectors": "5.2.2",
|
||||
"@vercel/routing-utils": "3.1.0",
|
||||
"@vitest/expect": "1.4.0",
|
||||
"ajv": "6.12.2",
|
||||
"alpha-sort": "2.0.1",
|
||||
"ansi-escapes": "4.3.2",
|
||||
@@ -167,6 +169,8 @@
|
||||
"ts-node": "10.9.1",
|
||||
"universal-analytics": "0.4.20",
|
||||
"utility-types": "2.1.0",
|
||||
"vite": "5.1.6",
|
||||
"vitest": "1.3.1",
|
||||
"which": "3.0.0",
|
||||
"write-json-file": "2.2.0",
|
||||
"xdg-app-paths": "5.1.0",
|
||||
|
||||
@@ -4,7 +4,7 @@ import dotenv from 'dotenv';
|
||||
import semver from 'semver';
|
||||
import minimatch from 'minimatch';
|
||||
import { join, normalize, relative, resolve, sep } from 'path';
|
||||
import frameworks from '@vercel/frameworks';
|
||||
import { frameworkList } from '@vercel/frameworks';
|
||||
import {
|
||||
getDiscontinuedNodeVersions,
|
||||
normalizePath,
|
||||
@@ -695,7 +695,7 @@ async function getFramework(
|
||||
): Promise<{ version: string } | undefined> {
|
||||
const detectedFramework = await detectFrameworkRecord({
|
||||
fs: new LocalFileSystemDetector(cwd),
|
||||
frameworkList: frameworks,
|
||||
frameworkList,
|
||||
});
|
||||
|
||||
if (!detectedFramework) {
|
||||
|
||||
@@ -303,7 +303,10 @@ export default async (client: Client): Promise<number> => {
|
||||
}
|
||||
|
||||
// build `--prebuilt`
|
||||
let vercelOutputDir: string | undefined;
|
||||
if (argv['--prebuilt']) {
|
||||
vercelOutputDir = join(cwd, '.vercel/output');
|
||||
|
||||
// For repo-style linking, update `cwd` to be the Project
|
||||
// subdirectory when `rootDirectory` setting is defined
|
||||
if (
|
||||
@@ -311,10 +314,10 @@ export default async (client: Client): Promise<number> => {
|
||||
link.repoRoot &&
|
||||
link.project.rootDirectory
|
||||
) {
|
||||
cwd = join(cwd, link.project.rootDirectory);
|
||||
vercelOutputDir = join(cwd, link.project.rootDirectory, '.vercel/output');
|
||||
}
|
||||
|
||||
const prebuiltExists = await fs.pathExists(join(cwd, '.vercel/output'));
|
||||
const prebuiltExists = await fs.pathExists(vercelOutputDir);
|
||||
if (!prebuiltExists) {
|
||||
error(
|
||||
`The ${param(
|
||||
@@ -326,7 +329,7 @@ export default async (client: Client): Promise<number> => {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const prebuiltBuild = await getPrebuiltJson(cwd);
|
||||
const prebuiltBuild = await getPrebuiltJson(vercelOutputDir);
|
||||
|
||||
// Ensure that there was not a build error
|
||||
const prebuiltError =
|
||||
@@ -545,6 +548,7 @@ export default async (client: Client): Promise<number> => {
|
||||
forceNew: argv['--force'],
|
||||
withCache: argv['--with-cache'],
|
||||
prebuilt: argv['--prebuilt'],
|
||||
vercelOutputDir,
|
||||
rootDirectory,
|
||||
quiet,
|
||||
wantsPublic: Boolean(argv['--public'] || localConfig.public),
|
||||
@@ -572,7 +576,7 @@ export default async (client: Client): Promise<number> => {
|
||||
nodeVersion,
|
||||
};
|
||||
|
||||
if (status !== 'not_linked') {
|
||||
if (status === 'linked') {
|
||||
createArgs.projectSettings = {
|
||||
...createArgs.projectSettings,
|
||||
...localConfigurationOverrides,
|
||||
|
||||
2
packages/cli/src/commands/env/add.ts
vendored
2
packages/cli/src/commands/env/add.ts
vendored
@@ -64,7 +64,7 @@ export default async function add(
|
||||
}
|
||||
|
||||
while (!envName) {
|
||||
envName = await client.input({
|
||||
envName = await client.input.text({
|
||||
message: `What’s the name of the variable?`,
|
||||
});
|
||||
|
||||
|
||||
4
packages/cli/src/commands/env/rm.ts
vendored
4
packages/cli/src/commands/env/rm.ts
vendored
@@ -41,7 +41,7 @@ export default async function rm(
|
||||
let [envName, envTarget, envGitBranch] = args;
|
||||
|
||||
while (!envName) {
|
||||
const inputName = await client.input({
|
||||
const inputName = await client.input.text({
|
||||
message: `What’s the name of the variable?`,
|
||||
});
|
||||
|
||||
@@ -81,7 +81,7 @@ export default async function rm(
|
||||
}
|
||||
|
||||
while (envs.length > 1) {
|
||||
const id = await client.select({
|
||||
const id = await client.input.select({
|
||||
message: `Remove ${envName} from which Environments?`,
|
||||
choices: envs.map(env => ({ value: env.id, name: formatEnvTarget(env) })),
|
||||
});
|
||||
|
||||
@@ -2,9 +2,14 @@ import { packageName, getCommandName } from '../../util/pkg-name';
|
||||
|
||||
export const secretsCommand = {
|
||||
name: 'secrets',
|
||||
description: `NOTE: The ${getCommandName(
|
||||
'env'
|
||||
)} command is recommended instead of ${getCommandName('secrets')}`,
|
||||
description:
|
||||
'WARNING: On May 1st, 2024 secrets will be automatically converted to sensitive Environment Variables for Preview and Production environments. Secrets attached to Development environments will not be migrated.\n' +
|
||||
`The ${getCommandName(
|
||||
'secrets'
|
||||
)} command will be deprecated at this time, please use the ${getCommandName(
|
||||
'env'
|
||||
)} command instead.\n` +
|
||||
'See https://vercel.com/changelog/legacy-environment-variable-secrets-are-being-sunset for more information.',
|
||||
arguments: [
|
||||
{
|
||||
name: 'command',
|
||||
|
||||
@@ -78,6 +78,12 @@ async function run({ output, contextName, currentTeam, client }) {
|
||||
|
||||
const commandName = getCommandName('secret ' + subcommand);
|
||||
|
||||
output.warn(
|
||||
'On May 1st, 2024 secrets will be automatically converted to sensitive Environment Variables for Preview and Production environments.\n' +
|
||||
'Secrets attached to Development environments will not be migrated.' +
|
||||
'See https://vercel.com/changelog/legacy-environment-variable-secrets-are-being-sunset for more information.'
|
||||
);
|
||||
|
||||
if (subcommand === 'ls' || subcommand === 'list') {
|
||||
output.note(
|
||||
`The ${getCommandName(
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import frameworkList from '@vercel/frameworks';
|
||||
import { frameworkList } from '@vercel/frameworks';
|
||||
|
||||
export function sortBuilders<B extends { use: string }>(builds: B[]): B[] {
|
||||
const frontendRuntimeSet = new Set(
|
||||
frameworkList.map(f => f.useRuntime?.use || '@vercel/static-build')
|
||||
);
|
||||
const toNumber = (build: B) => (frontendRuntimeSet.has(build.use) ? 0 : 1);
|
||||
|
||||
return builds.sort((build1, build2) => {
|
||||
return toNumber(build1) - toNumber(build2);
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { bold } from 'chalk';
|
||||
import input from './custom-input/input';
|
||||
import select from './custom-input/select';
|
||||
import checkbox from './custom-input/checkbox';
|
||||
import expand from './custom-input/expand';
|
||||
import confirm from './custom-input/confirm';
|
||||
import { bold, gray } from 'chalk';
|
||||
import checkbox from '@inquirer/checkbox';
|
||||
import confirm from '@inquirer/confirm';
|
||||
import expand from '@inquirer/expand';
|
||||
import input from '@inquirer/input';
|
||||
import select from '@inquirer/select';
|
||||
import { EventEmitter } from 'events';
|
||||
import { URL } from 'url';
|
||||
import { VercelConfig } from '@vercel/client';
|
||||
@@ -87,17 +87,28 @@ export default class Client extends EventEmitter implements Stdio {
|
||||
this.localConfig = opts.localConfig;
|
||||
this.localConfigPath = opts.localConfigPath;
|
||||
this.requestIdCounter = 1;
|
||||
|
||||
const theme = {
|
||||
prefix: gray('?'),
|
||||
style: { answer: gray },
|
||||
};
|
||||
this.input = {
|
||||
text: (opts: Parameters<typeof input>[0]) =>
|
||||
input(opts, { input: this.stdin, output: this.stderr }),
|
||||
input({ theme, ...opts }, { input: this.stdin, output: this.stderr }),
|
||||
checkbox: <T>(opts: Parameters<typeof checkbox<T>>[0]) =>
|
||||
checkbox<T>(opts, { input: this.stdin, output: this.stderr }),
|
||||
checkbox<T>(
|
||||
{ theme, ...opts },
|
||||
{ input: this.stdin, output: this.stderr }
|
||||
),
|
||||
expand: (opts: Parameters<typeof expand>[0]) =>
|
||||
expand(opts, { input: this.stdin, output: this.stderr }),
|
||||
expand({ theme, ...opts }, { input: this.stdin, output: this.stderr }),
|
||||
confirm: (opts: Parameters<typeof confirm>[0]) =>
|
||||
confirm(opts, { input: this.stdin, output: this.stderr }),
|
||||
confirm({ theme, ...opts }, { input: this.stdin, output: this.stderr }),
|
||||
select: <T>(opts: Parameters<typeof select<T>>[0]) =>
|
||||
select<T>(opts, { input: this.stdin, output: this.stderr }),
|
||||
select<T>(
|
||||
{ theme, ...opts },
|
||||
{ input: this.stdin, output: this.stderr }
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ const getGlobalPathConfig = (): string => {
|
||||
];
|
||||
|
||||
// The customPath flag is the preferred location,
|
||||
// followed by the the vercel directory,
|
||||
// followed by the vercel directory,
|
||||
// followed by the now directory.
|
||||
// If none of those exist, use the vercel directory.
|
||||
return (
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
import {
|
||||
createPrompt,
|
||||
useState,
|
||||
useKeypress,
|
||||
usePrefix,
|
||||
usePagination,
|
||||
useMemo,
|
||||
makeTheme,
|
||||
isUpKey,
|
||||
isDownKey,
|
||||
isSpaceKey,
|
||||
isNumberKey,
|
||||
isEnterKey,
|
||||
ValidationError,
|
||||
Separator,
|
||||
type Theme,
|
||||
} from '@inquirer/core';
|
||||
import type { PartialDeep } from '@inquirer/type';
|
||||
import chalk from 'chalk';
|
||||
import figures from './util/figures';
|
||||
import ansiEscapes from 'ansi-escapes';
|
||||
|
||||
import isUnicodeSupported from './util/is-unicode-supported';
|
||||
|
||||
const unicode = isUnicodeSupported();
|
||||
const s = (c: string, fallback: string) => (unicode ? c : fallback);
|
||||
const S_STEP_ACTIVE = s('◆', '*');
|
||||
const S_STEP_SUBMIT = s('◇', 'o');
|
||||
|
||||
const S_BAR = s('│', '|');
|
||||
|
||||
const S_CHECKBOX_SELECTED = s('◼', '[+]');
|
||||
const S_CHECKBOX_INACTIVE = s('◻', '[ ]');
|
||||
|
||||
type Status = 'pending' | 'done';
|
||||
|
||||
const symbol = (state: Status) => {
|
||||
switch (state) {
|
||||
case 'pending':
|
||||
return chalk.cyan(S_STEP_ACTIVE);
|
||||
case 'done':
|
||||
return chalk.green(S_STEP_SUBMIT);
|
||||
}
|
||||
};
|
||||
|
||||
type CheckboxTheme = {
|
||||
icon: {
|
||||
checked: string;
|
||||
unchecked: string;
|
||||
cursor: string;
|
||||
};
|
||||
style: {
|
||||
disabledChoice: (text: string) => string;
|
||||
renderSelectedChoices: <T>(
|
||||
selectedChoices: ReadonlyArray<Choice<T>>,
|
||||
allChoices: ReadonlyArray<Choice<T> | Separator>
|
||||
) => string;
|
||||
};
|
||||
};
|
||||
|
||||
const checkboxTheme: CheckboxTheme = {
|
||||
icon: {
|
||||
checked: chalk.green(figures.circleFilled),
|
||||
unchecked: figures.circle,
|
||||
cursor: figures.pointer,
|
||||
},
|
||||
style: {
|
||||
disabledChoice: (text: string) => chalk.dim(`- ${text}`),
|
||||
renderSelectedChoices: selectedChoices =>
|
||||
selectedChoices.map(choice => choice.name || choice.value).join(', '),
|
||||
},
|
||||
};
|
||||
|
||||
type Choice<Value> = {
|
||||
name?: string;
|
||||
value: Value;
|
||||
disabled?: boolean | string;
|
||||
checked?: boolean;
|
||||
type?: never;
|
||||
};
|
||||
|
||||
type Config<Value> = {
|
||||
message: string;
|
||||
prefix?: string;
|
||||
pageSize?: number;
|
||||
instructions?: string | boolean;
|
||||
choices: ReadonlyArray<Choice<Value> | Separator>;
|
||||
loop?: boolean;
|
||||
required?: boolean;
|
||||
validate?: (
|
||||
items: ReadonlyArray<Item<Value>>
|
||||
) => boolean | string | Promise<string | boolean>;
|
||||
theme?: PartialDeep<Theme<CheckboxTheme>>;
|
||||
};
|
||||
|
||||
type Item<Value> = Separator | Choice<Value>;
|
||||
|
||||
function isSelectable<Value>(item: Item<Value>): item is Choice<Value> {
|
||||
return !Separator.isSeparator(item) && !item.disabled;
|
||||
}
|
||||
|
||||
function isChecked<Value>(item: Item<Value>): item is Choice<Value> {
|
||||
return isSelectable(item) && Boolean(item.checked);
|
||||
}
|
||||
|
||||
function toggle<Value>(item: Item<Value>): Item<Value> {
|
||||
return isSelectable(item) ? { ...item, checked: !item.checked } : item;
|
||||
}
|
||||
|
||||
function check(checked: boolean) {
|
||||
return function <Value>(item: Item<Value>): Item<Value> {
|
||||
return isSelectable(item) ? { ...item, checked } : item;
|
||||
};
|
||||
}
|
||||
|
||||
export default createPrompt(
|
||||
<Value>(config: Config<Value>, done: (value: Array<Value>) => void) => {
|
||||
const {
|
||||
instructions,
|
||||
pageSize = 7,
|
||||
loop = true,
|
||||
choices,
|
||||
required,
|
||||
validate = () => true,
|
||||
} = config;
|
||||
const theme = makeTheme<CheckboxTheme>(checkboxTheme, config.theme);
|
||||
const prefix = usePrefix({ theme });
|
||||
const [status, setStatus] = useState<Status>('pending');
|
||||
const [items, setItems] = useState<ReadonlyArray<Item<Value>>>(
|
||||
choices.map(choice => ({ ...choice }))
|
||||
);
|
||||
|
||||
const bounds = useMemo(() => {
|
||||
const first = items.findIndex(isSelectable);
|
||||
// TODO: Replace with `findLastIndex` when it's available.
|
||||
const last =
|
||||
items.length - 1 - [...items].reverse().findIndex(isSelectable);
|
||||
|
||||
if (first < 0) {
|
||||
throw new ValidationError(
|
||||
'[checkbox prompt] No selectable choices. All choices are disabled.'
|
||||
);
|
||||
}
|
||||
|
||||
return { first, last };
|
||||
}, [items]);
|
||||
|
||||
const [active, setActive] = useState(bounds.first);
|
||||
const [showHelpTip, setShowHelpTip] = useState(true);
|
||||
const [errorMsg, setError] = useState<string | undefined>(undefined);
|
||||
|
||||
useKeypress(async key => {
|
||||
if (isEnterKey(key)) {
|
||||
const selection = items.filter(isChecked);
|
||||
const isValid = await validate([...selection]);
|
||||
if (required && !items.some(isChecked)) {
|
||||
setError('At least one choice must be selected');
|
||||
} else if (isValid === true) {
|
||||
setStatus('done');
|
||||
done(selection.map(choice => choice.value));
|
||||
} else {
|
||||
setError(isValid || 'You must select a valid value');
|
||||
}
|
||||
} else if (isUpKey(key) || isDownKey(key)) {
|
||||
if (
|
||||
loop ||
|
||||
(isUpKey(key) && active !== bounds.first) ||
|
||||
(isDownKey(key) && active !== bounds.last)
|
||||
) {
|
||||
const offset = isUpKey(key) ? -1 : 1;
|
||||
let next = active;
|
||||
do {
|
||||
next = (next + offset + items.length) % items.length;
|
||||
} while (!isSelectable(items[next]!));
|
||||
setActive(next);
|
||||
}
|
||||
} else if (isSpaceKey(key)) {
|
||||
setError(undefined);
|
||||
setShowHelpTip(false);
|
||||
setItems(
|
||||
items.map((choice, i) => (i === active ? toggle(choice) : choice))
|
||||
);
|
||||
} else if (key.name === 'a') {
|
||||
const selectAll = Boolean(
|
||||
items.find(choice => isSelectable(choice) && !choice.checked)
|
||||
);
|
||||
setItems(items.map(check(selectAll)));
|
||||
} else if (key.name === 'i') {
|
||||
setItems(items.map(toggle));
|
||||
} else if (isNumberKey(key)) {
|
||||
// Adjust index to start at 1
|
||||
const position = Number(key.name) - 1;
|
||||
const item = items[position];
|
||||
if (item != null && isSelectable(item)) {
|
||||
setActive(position);
|
||||
setItems(
|
||||
items.map((choice, i) => (i === position ? toggle(choice) : choice))
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const message = theme.style.message(config.message);
|
||||
|
||||
const page = usePagination<Item<Value>>({
|
||||
items,
|
||||
active,
|
||||
renderItem({ item, isActive }: { item: Item<Value>; isActive: boolean }) {
|
||||
if (Separator.isSeparator(item)) {
|
||||
return `${chalk.cyan(S_BAR)} ${item.separator}`;
|
||||
}
|
||||
|
||||
const line = item.name || item.value;
|
||||
if (item.disabled) {
|
||||
const disabledLabel =
|
||||
typeof item.disabled === 'string' ? item.disabled : '(disabled)';
|
||||
return `${chalk.cyan(S_BAR)} ${theme.style.disabledChoice(
|
||||
`${line} ${disabledLabel}`
|
||||
)}`;
|
||||
}
|
||||
|
||||
const checkbox = item.checked
|
||||
? S_CHECKBOX_SELECTED
|
||||
: S_CHECKBOX_INACTIVE;
|
||||
const color = isActive ? theme.style.highlight : (x: string) => x;
|
||||
// const cursor = isActive ? " " : " ";
|
||||
return `${chalk.cyan(S_BAR)} ${color(`${checkbox} ${line}`)}`;
|
||||
},
|
||||
pageSize,
|
||||
loop,
|
||||
theme,
|
||||
});
|
||||
|
||||
let helpTip = '';
|
||||
if (showHelpTip && (instructions === undefined || instructions)) {
|
||||
if (typeof instructions === 'string') {
|
||||
helpTip = instructions;
|
||||
} else {
|
||||
const keys = [
|
||||
`${theme.style.key('space')} to select`,
|
||||
`${theme.style.key('a')} to toggle all`,
|
||||
`${theme.style.key('i')} to invert selection`,
|
||||
`and ${theme.style.key('enter')} to proceed`,
|
||||
];
|
||||
helpTip = ` (Press ${keys.join(', ')})`;
|
||||
}
|
||||
}
|
||||
const title_basic = `${chalk.gray(S_BAR)}\n${symbol(
|
||||
status
|
||||
)} ${prefix} ${message}`;
|
||||
const title_with_help = `${title_basic}${helpTip}\n`;
|
||||
const title_without_help = `${title_basic}\n`;
|
||||
|
||||
if (status === 'done') {
|
||||
const selection = items.filter(isChecked);
|
||||
const answer = theme.style.answer(
|
||||
theme.style.renderSelectedChoices(selection, items)
|
||||
);
|
||||
|
||||
return `${title_without_help}${chalk.gray(S_BAR)} ${answer}`;
|
||||
}
|
||||
|
||||
let error = '';
|
||||
if (errorMsg) {
|
||||
error = theme.style.error(errorMsg);
|
||||
}
|
||||
|
||||
return `${title_with_help}${page}\n${error}${ansiEscapes.cursorHide}`;
|
||||
}
|
||||
);
|
||||
|
||||
export { Separator };
|
||||
@@ -1,128 +0,0 @@
|
||||
import {
|
||||
createPrompt,
|
||||
useState,
|
||||
useKeypress,
|
||||
isEnterKey,
|
||||
makeTheme,
|
||||
isUpKey,
|
||||
isDownKey,
|
||||
type Theme,
|
||||
type KeypressEvent,
|
||||
} from '@inquirer/core';
|
||||
import type { PartialDeep } from '@inquirer/type';
|
||||
|
||||
import isUnicodeSupported from './util/is-unicode-supported';
|
||||
import chalk from 'chalk';
|
||||
import ansiEscapes from 'ansi-escapes';
|
||||
|
||||
const unicode = isUnicodeSupported();
|
||||
const s = (c: string, fallback: string) => (unicode ? c : fallback);
|
||||
const S_STEP_ACTIVE = s('◆', '*');
|
||||
const S_STEP_SUBMIT = s('◇', 'o');
|
||||
|
||||
const S_BAR = s('│', '|');
|
||||
const S_BAR_END = s('└', '—');
|
||||
|
||||
const S_RADIO_ACTIVE = s('●', '>');
|
||||
const S_RADIO_INACTIVE = s('○', ' ');
|
||||
|
||||
type Status = 'pending' | 'done';
|
||||
|
||||
const symbol = (state: Status) => {
|
||||
switch (state) {
|
||||
case 'pending':
|
||||
return chalk.cyan(S_STEP_ACTIVE);
|
||||
case 'done':
|
||||
return chalk.green(S_STEP_SUBMIT);
|
||||
}
|
||||
};
|
||||
|
||||
type ConfirmConfig = {
|
||||
message: string;
|
||||
default?: CursorState;
|
||||
active?: string;
|
||||
inactive?: string;
|
||||
transformer?: (value: boolean) => string;
|
||||
theme?: PartialDeep<Theme>;
|
||||
};
|
||||
|
||||
const isLeftKey = (key: KeypressEvent): boolean =>
|
||||
// The left key
|
||||
key.name === 'left' ||
|
||||
// Vim keybinding
|
||||
key.name === 'h' ||
|
||||
// Emacs keybinding
|
||||
(key.ctrl && key.name === 'b');
|
||||
|
||||
const isRightKey = (key: KeypressEvent): boolean =>
|
||||
// The right key
|
||||
key.name === 'right' ||
|
||||
// Vim keybinding
|
||||
key.name === 'l' ||
|
||||
// Emacs keybinding
|
||||
(key.ctrl && key.name === 'f');
|
||||
|
||||
export type CursorState = 'yes' | 'no';
|
||||
|
||||
export default createPrompt<boolean, ConfirmConfig>((config, done) => {
|
||||
const { transformer = answer => (answer ? 'yes' : 'no') } = config;
|
||||
const [status, setStatus] = useState<Status>('pending');
|
||||
const [value, setValue] = useState('');
|
||||
const [cursorStatus, setCursorStatus] = useState<CursorState>(
|
||||
(config.default ? 'yes' : 'no') ?? 'yes'
|
||||
);
|
||||
const theme = makeTheme(config.theme);
|
||||
|
||||
const active = config.active ?? 'Yes';
|
||||
const inactive = config.active ?? 'No';
|
||||
|
||||
const toggle = (cursor_state: CursorState): CursorState => {
|
||||
if (cursor_state === 'yes') {
|
||||
return 'no';
|
||||
} else {
|
||||
return 'yes';
|
||||
}
|
||||
};
|
||||
|
||||
useKeypress(key => {
|
||||
if (isEnterKey(key)) {
|
||||
let answer = cursorStatus === 'yes';
|
||||
|
||||
setValue(transformer(answer));
|
||||
setStatus('done');
|
||||
done(answer);
|
||||
} else if (
|
||||
isUpKey(key) ||
|
||||
isDownKey(key) ||
|
||||
isLeftKey(key) ||
|
||||
isRightKey(key)
|
||||
) {
|
||||
setCursorStatus(toggle(cursorStatus));
|
||||
// setValue(rl.line);
|
||||
}
|
||||
});
|
||||
|
||||
const message = theme.style.message(config.message);
|
||||
const title = `${chalk.gray(S_BAR)}\n${symbol(status)} ${message}\n`;
|
||||
|
||||
const output = (status: Status) => {
|
||||
switch (status) {
|
||||
case 'done':
|
||||
return `${title}${chalk.gray(S_BAR)} ${chalk.dim(
|
||||
value === 'yes' ? active : inactive
|
||||
)}`;
|
||||
default:
|
||||
return `${title}${chalk.cyan(S_BAR)} ${
|
||||
cursorStatus === 'yes'
|
||||
? `${chalk.green(S_RADIO_ACTIVE)} ${active}`
|
||||
: `${chalk.dim(S_RADIO_INACTIVE)} ${chalk.dim(active)}`
|
||||
} ${chalk.dim('/')} ${
|
||||
cursorStatus === 'no'
|
||||
? `${chalk.green(S_RADIO_ACTIVE)} ${inactive}`
|
||||
: `${chalk.dim(S_RADIO_INACTIVE)} ${chalk.dim(inactive)}`
|
||||
} \n${chalk.cyan(S_BAR_END)} \n${ansiEscapes.cursorHide} `;
|
||||
}
|
||||
};
|
||||
|
||||
return output(status);
|
||||
});
|
||||
@@ -1,161 +0,0 @@
|
||||
import {
|
||||
createPrompt,
|
||||
useState,
|
||||
useKeypress,
|
||||
usePrefix,
|
||||
isEnterKey,
|
||||
makeTheme,
|
||||
type Theme,
|
||||
} from '@inquirer/core';
|
||||
import type { PartialDeep } from '@inquirer/type';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import isUnicodeSupported from './util/is-unicode-supported';
|
||||
|
||||
const unicode = isUnicodeSupported();
|
||||
const s = (c: string, fallback: string) => (unicode ? c : fallback);
|
||||
const S_STEP_ACTIVE = s('◆', '*');
|
||||
const S_STEP_SUBMIT = s('◇', 'o');
|
||||
|
||||
const S_BAR = s('│', '|');
|
||||
const S_BAR_END = s('└', '—');
|
||||
|
||||
type Status = 'pending' | 'done';
|
||||
|
||||
const symbol = (state: Status) => {
|
||||
switch (state) {
|
||||
case 'pending':
|
||||
return chalk.cyan(S_STEP_ACTIVE);
|
||||
case 'done':
|
||||
return chalk.green(S_STEP_SUBMIT);
|
||||
}
|
||||
};
|
||||
|
||||
type ExpandChoice =
|
||||
| { key: string; name: string }
|
||||
| { key: string; value: string }
|
||||
| { key: string; name: string; value: string };
|
||||
|
||||
type ExpandConfig = {
|
||||
message: string;
|
||||
choices: ReadonlyArray<ExpandChoice>;
|
||||
default?: string;
|
||||
expanded?: boolean;
|
||||
theme?: PartialDeep<Theme>;
|
||||
};
|
||||
|
||||
const helpChoice = {
|
||||
key: 'h',
|
||||
name: 'Help, list all options',
|
||||
value: undefined,
|
||||
};
|
||||
|
||||
function getChoiceKey(choice: ExpandChoice, key: 'name' | 'value'): string {
|
||||
if (key === 'name') {
|
||||
if ('name' in choice) return choice.name;
|
||||
return choice.value;
|
||||
}
|
||||
|
||||
if ('value' in choice) return choice.value;
|
||||
return choice.name;
|
||||
}
|
||||
|
||||
export default createPrompt<string, ExpandConfig>((config, done) => {
|
||||
const {
|
||||
choices,
|
||||
default: defaultKey = 'h',
|
||||
expanded: defaultExpandState = false,
|
||||
} = config;
|
||||
const [status, setStatus] = useState<string>('pending');
|
||||
const [value, setValue] = useState<string>('');
|
||||
const [expanded, setExpanded] = useState<boolean>(defaultExpandState);
|
||||
const [errorMsg, setError] = useState<string | undefined>(undefined);
|
||||
const theme = makeTheme(config.theme);
|
||||
const prefix = usePrefix({ theme });
|
||||
|
||||
useKeypress((event, rl) => {
|
||||
if (isEnterKey(event)) {
|
||||
const answer = (value || defaultKey).toLowerCase();
|
||||
if (answer === 'h' && !expanded) {
|
||||
setExpanded(true);
|
||||
} else {
|
||||
const selectedChoice = choices.find(({ key }) => key === answer);
|
||||
if (selectedChoice) {
|
||||
const finalValue = getChoiceKey(selectedChoice, 'value');
|
||||
setValue(finalValue);
|
||||
setStatus('done');
|
||||
done(finalValue);
|
||||
} else if (value === '') {
|
||||
setError('Please input a value');
|
||||
} else {
|
||||
setError(`"${chalk.red(value)}" isn't an available option`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setValue(rl.line);
|
||||
setError(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
const message = theme.style.message(config.message);
|
||||
|
||||
const title = `${chalk.gray(S_BAR)}\n${symbol(status)} ${prefix} ${message}`;
|
||||
|
||||
if (status === 'done') {
|
||||
// TODO: `value` should be the display name instead of the raw value.
|
||||
return `${title}\n${chalk.gray(S_BAR)} ${theme.style.answer(value)}`;
|
||||
}
|
||||
|
||||
const allChoices = expanded ? choices : [...choices, helpChoice];
|
||||
|
||||
// Collapsed display style
|
||||
let longChoices = '';
|
||||
let shortChoices = allChoices
|
||||
.map(choice => {
|
||||
if (choice.key === defaultKey) {
|
||||
return choice.key.toUpperCase();
|
||||
}
|
||||
|
||||
return choice.key;
|
||||
})
|
||||
.join('');
|
||||
shortChoices = ` ${theme.style.defaultAnswer(shortChoices)}`;
|
||||
|
||||
// Expanded display style
|
||||
if (expanded) {
|
||||
shortChoices = '';
|
||||
longChoices = allChoices
|
||||
.map(choice => {
|
||||
const line = ` ${choice.key}) ${getChoiceKey(choice, 'name')}`;
|
||||
if (choice.key === value.toLowerCase()) {
|
||||
return `${chalk.cyan(S_BAR)} ${theme.style.highlight(line)}`;
|
||||
}
|
||||
|
||||
return `${chalk.cyan(S_BAR)} ${line}`;
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
let helpTip = '';
|
||||
const currentOption = allChoices.find(
|
||||
({ key }) => key === value.toLowerCase()
|
||||
);
|
||||
if (currentOption) {
|
||||
helpTip = `${chalk.cyan(S_BAR)} ${chalk.cyan('>>')} ${getChoiceKey(
|
||||
currentOption,
|
||||
'name'
|
||||
)} `;
|
||||
}
|
||||
|
||||
let error = '';
|
||||
if (errorMsg) {
|
||||
error = theme.style.error(errorMsg);
|
||||
}
|
||||
|
||||
return [
|
||||
`${title}${shortChoices} \n${chalk.cyan(S_BAR)} ${value}`,
|
||||
`${[longChoices, helpTip, error, chalk.cyan(S_BAR_END)]
|
||||
.filter(Boolean)
|
||||
.join('\n')}`,
|
||||
];
|
||||
});
|
||||
@@ -1,129 +0,0 @@
|
||||
import {
|
||||
createPrompt,
|
||||
useState,
|
||||
useKeypress,
|
||||
isEnterKey,
|
||||
isBackspaceKey,
|
||||
makeTheme,
|
||||
type Theme,
|
||||
} from '@inquirer/core';
|
||||
|
||||
import type { PartialDeep } from '@inquirer/type';
|
||||
import chalk from 'chalk';
|
||||
import ansiEscapes from 'ansi-escapes';
|
||||
import isUnicodeSupported from './util/is-unicode-supported';
|
||||
|
||||
const unicode = isUnicodeSupported();
|
||||
const s = (c: string, fallback: string) => (unicode ? c : fallback);
|
||||
const S_STEP_ACTIVE = s('◆', '*');
|
||||
const S_STEP_CANCEL = s('■', 'x');
|
||||
const S_STEP_SUBMIT = s('◇', 'o');
|
||||
|
||||
const S_BAR = s('│', '|');
|
||||
const S_BAR_END = s('└', '—');
|
||||
|
||||
type InputConfig = {
|
||||
message: string;
|
||||
default?: string;
|
||||
transformer?: (value: string, { isFinal }: { isFinal: boolean }) => string;
|
||||
validate?: (value: string) => boolean | string | Promise<string | boolean>;
|
||||
theme?: PartialDeep<Theme>;
|
||||
};
|
||||
|
||||
type Status = 'pending' | 'loading' | 'done';
|
||||
|
||||
const symbol = (state: Status) => {
|
||||
switch (state) {
|
||||
case 'pending':
|
||||
return chalk.cyan(S_STEP_ACTIVE);
|
||||
case 'loading':
|
||||
return chalk.red(S_STEP_CANCEL);
|
||||
case 'done':
|
||||
return chalk.green(S_STEP_SUBMIT);
|
||||
}
|
||||
};
|
||||
|
||||
export default createPrompt<string, InputConfig>((config, done) => {
|
||||
const { validate = () => true } = config;
|
||||
const theme = makeTheme(config.theme);
|
||||
const [status, setStatus] = useState<Status>('pending');
|
||||
const [cursorIndex, setCursorIndex] = useState<number>(0);
|
||||
const [defaultValue = '', setDefaultValue] = useState<string>(
|
||||
config.default || ''
|
||||
);
|
||||
const [errorMsg, setError] = useState<string | undefined>(undefined);
|
||||
const [value, setValue] = useState<string>('');
|
||||
|
||||
useKeypress(async (key, rl) => {
|
||||
// Ignore keypress while our prompt is doing other processing.
|
||||
if (status !== 'pending') {
|
||||
return;
|
||||
}
|
||||
setCursorIndex(rl.cursor);
|
||||
if (isEnterKey(key)) {
|
||||
const answer = value || defaultValue;
|
||||
setStatus('loading');
|
||||
const isValid = await validate(answer);
|
||||
if (isValid === true) {
|
||||
setValue(answer);
|
||||
setStatus('done');
|
||||
done(answer);
|
||||
} else {
|
||||
// Reset the readline line value to the previous value. On line event, the value
|
||||
// get cleared, forcing the user to re-enter the value instead of fixing it.
|
||||
rl.write(value);
|
||||
setError(isValid || 'You must provide a valid value');
|
||||
setStatus('pending');
|
||||
}
|
||||
} else if (isBackspaceKey(key) && !value) {
|
||||
setDefaultValue('');
|
||||
} else if (key.name === 'tab' && !value) {
|
||||
setDefaultValue('');
|
||||
rl.clearLine(0); // Remove the tab character.
|
||||
rl.write(defaultValue);
|
||||
setValue(defaultValue);
|
||||
} else {
|
||||
setValue(rl.line);
|
||||
setError(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
const message = theme.style.message(config.message);
|
||||
let formattedValue = value;
|
||||
if (typeof config.transformer === 'function') {
|
||||
formattedValue = config.transformer(value, { isFinal: status === 'done' });
|
||||
} else if (status === 'done') {
|
||||
formattedValue = theme.style.answer(value);
|
||||
}
|
||||
|
||||
let error = '';
|
||||
if (errorMsg) {
|
||||
error = theme.style.error(errorMsg);
|
||||
}
|
||||
|
||||
let valueWithCursor: any;
|
||||
if (cursorIndex && cursorIndex >= value.length) {
|
||||
valueWithCursor = `${value}${chalk.inverse(chalk.hidden(' '))}`;
|
||||
} else {
|
||||
const s1 = value.slice(0, cursorIndex);
|
||||
const s2 = value.slice(cursorIndex);
|
||||
if (s2.length !== 0)
|
||||
valueWithCursor = `${s1}${chalk.inverse(s2[0])}${s2.slice(1)}`;
|
||||
else valueWithCursor = `${s1}${chalk.inverse(' ')}`;
|
||||
}
|
||||
|
||||
const title = `${chalk.gray(S_BAR)}\n${symbol(status)} ${message}\n`;
|
||||
|
||||
const output = (status: any) => {
|
||||
switch (status) {
|
||||
case 'done':
|
||||
return `${title}${chalk.gray(S_BAR)} ${formattedValue}`;
|
||||
default:
|
||||
return `${title}${chalk.cyan(S_BAR)} ${valueWithCursor}\n${chalk.cyan(
|
||||
S_BAR_END
|
||||
)}\n${ansiEscapes.cursorHide}`;
|
||||
}
|
||||
};
|
||||
|
||||
return [output(status), error];
|
||||
});
|
||||
@@ -1,229 +0,0 @@
|
||||
import {
|
||||
createPrompt,
|
||||
useState,
|
||||
useKeypress,
|
||||
usePrefix,
|
||||
usePagination,
|
||||
useRef,
|
||||
useMemo,
|
||||
isBackspaceKey,
|
||||
isEnterKey,
|
||||
isUpKey,
|
||||
isDownKey,
|
||||
isNumberKey,
|
||||
Separator,
|
||||
ValidationError,
|
||||
makeTheme,
|
||||
type Theme,
|
||||
} from '@inquirer/core';
|
||||
import type { PartialDeep } from '@inquirer/type';
|
||||
import chalk from 'chalk';
|
||||
import figures from './util/figures';
|
||||
import ansiEscapes from 'ansi-escapes';
|
||||
|
||||
import isUnicodeSupported from './util/is-unicode-supported';
|
||||
|
||||
const unicode = isUnicodeSupported();
|
||||
const s = (c: string, fallback: string) => (unicode ? c : fallback);
|
||||
const S_STEP_ACTIVE = s('◆', '*');
|
||||
const S_STEP_SUBMIT = s('◇', 'o');
|
||||
|
||||
const S_BAR = s('│', '|');
|
||||
const S_BAR_END = s('└', '—');
|
||||
|
||||
const S_RADIO_ACTIVE = s('●', '>');
|
||||
const S_RADIO_INACTIVE = s('○', ' ');
|
||||
|
||||
type SelectTheme = {
|
||||
icon: { cursor: string };
|
||||
style: { disabled: (text: string) => string };
|
||||
};
|
||||
|
||||
const selectTheme: SelectTheme = {
|
||||
icon: { cursor: figures.pointer },
|
||||
style: { disabled: (text: string) => chalk.dim(`- ${text}`) },
|
||||
};
|
||||
|
||||
type Choice<Value> = {
|
||||
value: Value;
|
||||
name?: string;
|
||||
description?: string;
|
||||
disabled?: boolean | string;
|
||||
type?: never;
|
||||
};
|
||||
|
||||
type SelectConfig<Value> = {
|
||||
message: string;
|
||||
choices: ReadonlyArray<Choice<Value> | Separator>;
|
||||
pageSize?: number;
|
||||
loop?: boolean;
|
||||
default?: unknown;
|
||||
theme?: PartialDeep<Theme<SelectTheme>>;
|
||||
};
|
||||
|
||||
type Item<Value> = Separator | Choice<Value>;
|
||||
|
||||
function isSelectable<Value>(item: Item<Value>): item is Choice<Value> {
|
||||
return !Separator.isSeparator(item) && !item.disabled;
|
||||
}
|
||||
|
||||
type Status = 'pending' | 'done';
|
||||
|
||||
const symbol = (state: Status) => {
|
||||
switch (state) {
|
||||
case 'pending':
|
||||
return chalk.cyan(S_STEP_ACTIVE);
|
||||
case 'done':
|
||||
return chalk.green(S_STEP_SUBMIT);
|
||||
}
|
||||
};
|
||||
|
||||
export default createPrompt(
|
||||
<Value>(
|
||||
config: SelectConfig<Value>,
|
||||
done: (value: Value) => void
|
||||
): string => {
|
||||
const { choices: items, loop = true, pageSize = 7 } = config;
|
||||
const firstRender = useRef(true);
|
||||
const theme = makeTheme<SelectTheme>(selectTheme, config.theme);
|
||||
const prefix = usePrefix({ theme });
|
||||
const [status, setStatus] = useState<Status>('pending');
|
||||
const searchTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
const bounds = useMemo(() => {
|
||||
const first = items.findIndex(isSelectable);
|
||||
// TODO: Replace with `findLastIndex` when it's available.
|
||||
const last =
|
||||
items.length - 1 - [...items].reverse().findIndex(isSelectable);
|
||||
if (first < 0)
|
||||
throw new ValidationError(
|
||||
'[select prompt] No selectable choices. All choices are disabled.'
|
||||
);
|
||||
return { first, last };
|
||||
}, [items]);
|
||||
|
||||
const defaultItemIndex = useMemo(() => {
|
||||
if (!('default' in config)) return -1;
|
||||
return items.findIndex(
|
||||
item => isSelectable(item) && item.value === config.default
|
||||
);
|
||||
}, [config.default, items]);
|
||||
|
||||
const [active, setActive] = useState(
|
||||
defaultItemIndex === -1 ? bounds.first : defaultItemIndex
|
||||
);
|
||||
|
||||
// Safe to assume the cursor position always point to a Choice.
|
||||
const selectedChoice = items[active] as Choice<Value>;
|
||||
|
||||
useKeypress((key, rl) => {
|
||||
clearTimeout(searchTimeoutRef.current);
|
||||
|
||||
if (isEnterKey(key)) {
|
||||
setStatus('done');
|
||||
done(selectedChoice.value);
|
||||
} else if (isUpKey(key) || isDownKey(key)) {
|
||||
rl.clearLine(0);
|
||||
if (
|
||||
loop ||
|
||||
(isUpKey(key) && active !== bounds.first) ||
|
||||
(isDownKey(key) && active !== bounds.last)
|
||||
) {
|
||||
const offset = isUpKey(key) ? -1 : 1;
|
||||
let next = active;
|
||||
do {
|
||||
next = (next + offset + items.length) % items.length;
|
||||
} while (!isSelectable(items[next]!));
|
||||
setActive(next);
|
||||
}
|
||||
} else if (isNumberKey(key)) {
|
||||
rl.clearLine(0);
|
||||
const position = Number(key.name) - 1;
|
||||
const item = items[position];
|
||||
if (item != null && isSelectable(item)) {
|
||||
setActive(position);
|
||||
}
|
||||
} else if (isBackspaceKey(key)) {
|
||||
rl.clearLine(0);
|
||||
} else {
|
||||
// Default to search
|
||||
const searchTerm = rl.line.toLowerCase();
|
||||
const matchIndex = items.findIndex(item => {
|
||||
if (Separator.isSeparator(item) || !isSelectable(item)) return false;
|
||||
|
||||
return String(item.name || item.value)
|
||||
.toLowerCase()
|
||||
.startsWith(searchTerm);
|
||||
});
|
||||
|
||||
if (matchIndex >= 0) {
|
||||
setActive(matchIndex);
|
||||
}
|
||||
|
||||
searchTimeoutRef.current = setTimeout(() => {
|
||||
rl.clearLine(0);
|
||||
}, 700);
|
||||
}
|
||||
});
|
||||
|
||||
const message = theme.style.message(config.message);
|
||||
|
||||
let helpTip;
|
||||
if (firstRender.current && items.length <= pageSize) {
|
||||
firstRender.current = false;
|
||||
helpTip = theme.style.help('(Use arrow keys)');
|
||||
}
|
||||
|
||||
const page = usePagination<Item<Value>>({
|
||||
items,
|
||||
active,
|
||||
renderItem({ item, isActive }: { item: Item<Value>; isActive: boolean }) {
|
||||
if (Separator.isSeparator(item)) {
|
||||
return `${chalk.cyan(S_BAR)} ${item.separator}`;
|
||||
}
|
||||
|
||||
const line = item.name || item.value;
|
||||
if (item.disabled) {
|
||||
const disabledLabel =
|
||||
typeof item.disabled === 'string' ? item.disabled : '(disabled)';
|
||||
return theme.style.disabled(`${line} ${disabledLabel}`);
|
||||
}
|
||||
|
||||
const color = isActive ? theme.style.highlight : (x: string) => x;
|
||||
const cursor = isActive ? S_RADIO_ACTIVE : S_RADIO_INACTIVE;
|
||||
return `${chalk.cyan(S_BAR)} ${color(`${cursor} ${line}`)}`;
|
||||
},
|
||||
pageSize,
|
||||
loop,
|
||||
theme,
|
||||
});
|
||||
|
||||
const title = `${chalk.gray(S_BAR)}\n${symbol(status)} ${[
|
||||
prefix,
|
||||
message,
|
||||
helpTip,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}\n`;
|
||||
|
||||
if (status === 'done') {
|
||||
const answer =
|
||||
selectedChoice.name ||
|
||||
// TODO: Could we enforce that at the type level? Name should be defined for non-string values.
|
||||
String(selectedChoice.value);
|
||||
return `${title}${chalk.gray(S_BAR)} ${theme.style.answer(answer)}`;
|
||||
}
|
||||
|
||||
const choiceDescription = selectedChoice.description
|
||||
? `\n${selectedChoice.description}`
|
||||
: ``;
|
||||
|
||||
return `${title}${page}${choiceDescription}\n${chalk.cyan(S_BAR_END)}${
|
||||
ansiEscapes.cursorHide
|
||||
} `;
|
||||
}
|
||||
);
|
||||
|
||||
export { Separator };
|
||||
@@ -1,307 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import isUnicodeSupported from './is-unicode-supported';
|
||||
|
||||
const common = {
|
||||
circleQuestionMark: '(?)',
|
||||
questionMarkPrefix: '(?)',
|
||||
square: '█',
|
||||
squareDarkShade: '▓',
|
||||
squareMediumShade: '▒',
|
||||
squareLightShade: '░',
|
||||
squareTop: '▀',
|
||||
squareBottom: '▄',
|
||||
squareLeft: '▌',
|
||||
squareRight: '▐',
|
||||
squareCenter: '■',
|
||||
bullet: '●',
|
||||
dot: '․',
|
||||
ellipsis: '…',
|
||||
pointerSmall: '›',
|
||||
triangleUp: '▲',
|
||||
triangleUpSmall: '▴',
|
||||
triangleDown: '▼',
|
||||
triangleDownSmall: '▾',
|
||||
triangleLeftSmall: '◂',
|
||||
triangleRightSmall: '▸',
|
||||
home: '⌂',
|
||||
heart: '♥',
|
||||
musicNote: '♪',
|
||||
musicNoteBeamed: '♫',
|
||||
arrowUp: '↑',
|
||||
arrowDown: '↓',
|
||||
arrowLeft: '←',
|
||||
arrowRight: '→',
|
||||
arrowLeftRight: '↔',
|
||||
arrowUpDown: '↕',
|
||||
almostEqual: '≈',
|
||||
notEqual: '≠',
|
||||
lessOrEqual: '≤',
|
||||
greaterOrEqual: '≥',
|
||||
identical: '≡',
|
||||
infinity: '∞',
|
||||
subscriptZero: '₀',
|
||||
subscriptOne: '₁',
|
||||
subscriptTwo: '₂',
|
||||
subscriptThree: '₃',
|
||||
subscriptFour: '₄',
|
||||
subscriptFive: '₅',
|
||||
subscriptSix: '₆',
|
||||
subscriptSeven: '₇',
|
||||
subscriptEight: '₈',
|
||||
subscriptNine: '₉',
|
||||
oneHalf: '½',
|
||||
oneThird: '⅓',
|
||||
oneQuarter: '¼',
|
||||
oneFifth: '⅕',
|
||||
oneSixth: '⅙',
|
||||
oneEighth: '⅛',
|
||||
twoThirds: '⅔',
|
||||
twoFifths: '⅖',
|
||||
threeQuarters: '¾',
|
||||
threeFifths: '⅗',
|
||||
threeEighths: '⅜',
|
||||
fourFifths: '⅘',
|
||||
fiveSixths: '⅚',
|
||||
fiveEighths: '⅝',
|
||||
sevenEighths: '⅞',
|
||||
line: '─',
|
||||
lineBold: '━',
|
||||
lineDouble: '═',
|
||||
lineDashed0: '┄',
|
||||
lineDashed1: '┅',
|
||||
lineDashed2: '┈',
|
||||
lineDashed3: '┉',
|
||||
lineDashed4: '╌',
|
||||
lineDashed5: '╍',
|
||||
lineDashed6: '╴',
|
||||
lineDashed7: '╶',
|
||||
lineDashed8: '╸',
|
||||
lineDashed9: '╺',
|
||||
lineDashed10: '╼',
|
||||
lineDashed11: '╾',
|
||||
lineDashed12: '−',
|
||||
lineDashed13: '–',
|
||||
lineDashed14: '‐',
|
||||
lineDashed15: '⁃',
|
||||
lineVertical: '│',
|
||||
lineVerticalBold: '┃',
|
||||
lineVerticalDouble: '║',
|
||||
lineVerticalDashed0: '┆',
|
||||
lineVerticalDashed1: '┇',
|
||||
lineVerticalDashed2: '┊',
|
||||
lineVerticalDashed3: '┋',
|
||||
lineVerticalDashed4: '╎',
|
||||
lineVerticalDashed5: '╏',
|
||||
lineVerticalDashed6: '╵',
|
||||
lineVerticalDashed7: '╷',
|
||||
lineVerticalDashed8: '╹',
|
||||
lineVerticalDashed9: '╻',
|
||||
lineVerticalDashed10: '╽',
|
||||
lineVerticalDashed11: '╿',
|
||||
lineDownLeft: '┐',
|
||||
lineDownLeftArc: '╮',
|
||||
lineDownBoldLeftBold: '┓',
|
||||
lineDownBoldLeft: '┒',
|
||||
lineDownLeftBold: '┑',
|
||||
lineDownDoubleLeftDouble: '╗',
|
||||
lineDownDoubleLeft: '╖',
|
||||
lineDownLeftDouble: '╕',
|
||||
lineDownRight: '┌',
|
||||
lineDownRightArc: '╭',
|
||||
lineDownBoldRightBold: '┏',
|
||||
lineDownBoldRight: '┎',
|
||||
lineDownRightBold: '┍',
|
||||
lineDownDoubleRightDouble: '╔',
|
||||
lineDownDoubleRight: '╓',
|
||||
lineDownRightDouble: '╒',
|
||||
lineUpLeft: '┘',
|
||||
lineUpLeftArc: '╯',
|
||||
lineUpBoldLeftBold: '┛',
|
||||
lineUpBoldLeft: '┚',
|
||||
lineUpLeftBold: '┙',
|
||||
lineUpDoubleLeftDouble: '╝',
|
||||
lineUpDoubleLeft: '╜',
|
||||
lineUpLeftDouble: '╛',
|
||||
lineUpRight: '└',
|
||||
lineUpRightArc: '╰',
|
||||
lineUpBoldRightBold: '┗',
|
||||
lineUpBoldRight: '┖',
|
||||
lineUpRightBold: '┕',
|
||||
lineUpDoubleRightDouble: '╚',
|
||||
lineUpDoubleRight: '╙',
|
||||
lineUpRightDouble: '╘',
|
||||
lineUpDownLeft: '┤',
|
||||
lineUpBoldDownBoldLeftBold: '┫',
|
||||
lineUpBoldDownBoldLeft: '┨',
|
||||
lineUpDownLeftBold: '┥',
|
||||
lineUpBoldDownLeftBold: '┩',
|
||||
lineUpDownBoldLeftBold: '┪',
|
||||
lineUpDownBoldLeft: '┧',
|
||||
lineUpBoldDownLeft: '┦',
|
||||
lineUpDoubleDownDoubleLeftDouble: '╣',
|
||||
lineUpDoubleDownDoubleLeft: '╢',
|
||||
lineUpDownLeftDouble: '╡',
|
||||
lineUpDownRight: '├',
|
||||
lineUpBoldDownBoldRightBold: '┣',
|
||||
lineUpBoldDownBoldRight: '┠',
|
||||
lineUpDownRightBold: '┝',
|
||||
lineUpBoldDownRightBold: '┡',
|
||||
lineUpDownBoldRightBold: '┢',
|
||||
lineUpDownBoldRight: '┟',
|
||||
lineUpBoldDownRight: '┞',
|
||||
lineUpDoubleDownDoubleRightDouble: '╠',
|
||||
lineUpDoubleDownDoubleRight: '╟',
|
||||
lineUpDownRightDouble: '╞',
|
||||
lineDownLeftRight: '┬',
|
||||
lineDownBoldLeftBoldRightBold: '┳',
|
||||
lineDownLeftBoldRightBold: '┯',
|
||||
lineDownBoldLeftRight: '┰',
|
||||
lineDownBoldLeftBoldRight: '┱',
|
||||
lineDownBoldLeftRightBold: '┲',
|
||||
lineDownLeftRightBold: '┮',
|
||||
lineDownLeftBoldRight: '┭',
|
||||
lineDownDoubleLeftDoubleRightDouble: '╦',
|
||||
lineDownDoubleLeftRight: '╥',
|
||||
lineDownLeftDoubleRightDouble: '╤',
|
||||
lineUpLeftRight: '┴',
|
||||
lineUpBoldLeftBoldRightBold: '┻',
|
||||
lineUpLeftBoldRightBold: '┷',
|
||||
lineUpBoldLeftRight: '┸',
|
||||
lineUpBoldLeftBoldRight: '┹',
|
||||
lineUpBoldLeftRightBold: '┺',
|
||||
lineUpLeftRightBold: '┶',
|
||||
lineUpLeftBoldRight: '┵',
|
||||
lineUpDoubleLeftDoubleRightDouble: '╩',
|
||||
lineUpDoubleLeftRight: '╨',
|
||||
lineUpLeftDoubleRightDouble: '╧',
|
||||
lineUpDownLeftRight: '┼',
|
||||
lineUpBoldDownBoldLeftBoldRightBold: '╋',
|
||||
lineUpDownBoldLeftBoldRightBold: '╈',
|
||||
lineUpBoldDownLeftBoldRightBold: '╇',
|
||||
lineUpBoldDownBoldLeftRightBold: '╊',
|
||||
lineUpBoldDownBoldLeftBoldRight: '╉',
|
||||
lineUpBoldDownLeftRight: '╀',
|
||||
lineUpDownBoldLeftRight: '╁',
|
||||
lineUpDownLeftBoldRight: '┽',
|
||||
lineUpDownLeftRightBold: '┾',
|
||||
lineUpBoldDownBoldLeftRight: '╂',
|
||||
lineUpDownLeftBoldRightBold: '┿',
|
||||
lineUpBoldDownLeftBoldRight: '╃',
|
||||
lineUpBoldDownLeftRightBold: '╄',
|
||||
lineUpDownBoldLeftBoldRight: '╅',
|
||||
lineUpDownBoldLeftRightBold: '╆',
|
||||
lineUpDoubleDownDoubleLeftDoubleRightDouble: '╬',
|
||||
lineUpDoubleDownDoubleLeftRight: '╫',
|
||||
lineUpDownLeftDoubleRightDouble: '╪',
|
||||
lineCross: '╳',
|
||||
lineBackslash: '╲',
|
||||
lineSlash: '╱',
|
||||
};
|
||||
|
||||
const specialMainSymbols = {
|
||||
tick: '✔',
|
||||
info: 'ℹ',
|
||||
warning: '⚠',
|
||||
cross: '✘',
|
||||
squareSmall: '◻',
|
||||
squareSmallFilled: '◼',
|
||||
circle: '◯',
|
||||
circleFilled: '◉',
|
||||
circleDotted: '◌',
|
||||
circleDouble: '◎',
|
||||
circleCircle: 'ⓞ',
|
||||
circleCross: 'ⓧ',
|
||||
circlePipe: 'Ⓘ',
|
||||
radioOn: '◉',
|
||||
radioOff: '◯',
|
||||
checkboxOn: '☒',
|
||||
checkboxOff: '☐',
|
||||
checkboxCircleOn: 'ⓧ',
|
||||
checkboxCircleOff: 'Ⓘ',
|
||||
pointer: '❯',
|
||||
triangleUpOutline: '△',
|
||||
triangleLeft: '◀',
|
||||
triangleRight: '▶',
|
||||
lozenge: '◆',
|
||||
lozengeOutline: '◇',
|
||||
hamburger: '☰',
|
||||
smiley: '㋡',
|
||||
mustache: '෴',
|
||||
star: '★',
|
||||
play: '▶',
|
||||
nodejs: '⬢',
|
||||
oneSeventh: '⅐',
|
||||
oneNinth: '⅑',
|
||||
oneTenth: '⅒',
|
||||
};
|
||||
|
||||
const specialFallbackSymbols = {
|
||||
tick: '√',
|
||||
info: 'i',
|
||||
warning: '‼',
|
||||
cross: '×',
|
||||
squareSmall: '□',
|
||||
squareSmallFilled: '■',
|
||||
circle: '( )',
|
||||
circleFilled: '(*)',
|
||||
circleDotted: '( )',
|
||||
circleDouble: '( )',
|
||||
circleCircle: '(○)',
|
||||
circleCross: '(×)',
|
||||
circlePipe: '(│)',
|
||||
radioOn: '(*)',
|
||||
radioOff: '( )',
|
||||
checkboxOn: '[×]',
|
||||
checkboxOff: '[ ]',
|
||||
checkboxCircleOn: '(×)',
|
||||
checkboxCircleOff: '( )',
|
||||
pointer: '>',
|
||||
triangleUpOutline: '∆',
|
||||
triangleLeft: '◄',
|
||||
triangleRight: '►',
|
||||
lozenge: '♦',
|
||||
lozengeOutline: '◊',
|
||||
hamburger: '≡',
|
||||
smiley: '☺',
|
||||
mustache: '┌─┐',
|
||||
star: '✶',
|
||||
play: '►',
|
||||
nodejs: '♦',
|
||||
oneSeventh: '1/7',
|
||||
oneNinth: '1/9',
|
||||
oneTenth: '1/10',
|
||||
};
|
||||
|
||||
export const mainSymbols = { ...common, ...specialMainSymbols };
|
||||
export const fallbackSymbols = { ...common, ...specialFallbackSymbols };
|
||||
|
||||
const shouldUseMain = isUnicodeSupported();
|
||||
const figures = shouldUseMain ? mainSymbols : fallbackSymbols;
|
||||
export default figures;
|
||||
|
||||
const replacements = Object.entries(specialMainSymbols);
|
||||
|
||||
// On terminals which do not support Unicode symbols, substitute them to other symbols
|
||||
export const replaceSymbols = (
|
||||
string,
|
||||
{ useFallback = !shouldUseMain } = {}
|
||||
) => {
|
||||
if (useFallback) {
|
||||
for (const [key, mainSymbol] of replacements) {
|
||||
string = string.replaceAll(mainSymbol, fallbackSymbols[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return string;
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
export default function isUnicodeSupported() {
|
||||
if (process.platform !== 'win32') {
|
||||
return process.env.TERM !== 'linux'; // Linux console (kernel)
|
||||
}
|
||||
|
||||
return (
|
||||
Boolean(process.env.WT_SESSION) || // Windows Terminal
|
||||
Boolean(process.env.TERMINUS_SUBLIME) || // Terminus (<0.2.27)
|
||||
process.env.ConEmuTask === '{cmd::Cmder}' || // ConEmu and cmder
|
||||
process.env.TERM_PROGRAM === 'Terminus-Sublime' ||
|
||||
process.env.TERM_PROGRAM === 'vscode' ||
|
||||
process.env.TERM === 'xterm-256color' ||
|
||||
process.env.TERM === 'alacritty' ||
|
||||
process.env.TERMINAL_EMULATOR === 'JetBrains-JediTerm'
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,7 @@ export default async function getPrebuiltJson(
|
||||
directory: string
|
||||
): Promise<BuildsManifest | null> {
|
||||
try {
|
||||
return await fs.readJSON(join(directory, '.vercel/output/builds.json'));
|
||||
return await fs.readJSON(join(directory, 'builds.json'));
|
||||
} catch (error) {
|
||||
// ignoring error
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ export default async function processDeployment({
|
||||
withCache?: boolean;
|
||||
org: Org;
|
||||
prebuilt: boolean;
|
||||
vercelOutputDir?: string;
|
||||
projectName: string;
|
||||
isSettingUpProject: boolean;
|
||||
archive?: ArchiveFormat;
|
||||
@@ -71,6 +72,7 @@ export default async function processDeployment({
|
||||
withCache,
|
||||
quiet,
|
||||
prebuilt,
|
||||
vercelOutputDir,
|
||||
rootDirectory,
|
||||
} = args;
|
||||
|
||||
@@ -92,6 +94,7 @@ export default async function processDeployment({
|
||||
force,
|
||||
withCache,
|
||||
prebuilt,
|
||||
vercelOutputDir,
|
||||
rootDirectory,
|
||||
skipAutoDetectionConfirmation,
|
||||
archive,
|
||||
|
||||
@@ -45,7 +45,7 @@ import {
|
||||
detectApiExtensions,
|
||||
isOfficialRuntime,
|
||||
} from '@vercel/fs-detectors';
|
||||
import frameworkList from '@vercel/frameworks';
|
||||
import { frameworkList } from '@vercel/frameworks';
|
||||
|
||||
import cmd from '../output/cmd';
|
||||
import link from '../output/link';
|
||||
|
||||
@@ -36,6 +36,7 @@ export interface CreateOptions {
|
||||
project?: string;
|
||||
wantsPublic: boolean;
|
||||
prebuilt?: boolean;
|
||||
vercelOutputDir?: string;
|
||||
rootDirectory?: string | null;
|
||||
meta: Dictionary<string>;
|
||||
gitMetadata?: GitMetadata;
|
||||
@@ -117,6 +118,7 @@ export default class Now extends EventEmitter {
|
||||
name,
|
||||
project,
|
||||
prebuilt = false,
|
||||
vercelOutputDir,
|
||||
rootDirectory,
|
||||
wantsPublic,
|
||||
meta,
|
||||
@@ -179,6 +181,7 @@ export default class Now extends EventEmitter {
|
||||
skipAutoDetectionConfirmation,
|
||||
cwd,
|
||||
prebuilt,
|
||||
vercelOutputDir,
|
||||
rootDirectory,
|
||||
noWait,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import confirm from './confirm';
|
||||
import chalk from 'chalk';
|
||||
import frameworkList, { Framework } from '@vercel/frameworks';
|
||||
import { frameworkList, Framework } from '@vercel/frameworks';
|
||||
import Client from '../client';
|
||||
import { isSettingValue } from '../is-setting-value';
|
||||
import type { ProjectSettings } from '@vercel-internals/types';
|
||||
@@ -124,29 +124,24 @@ export default async function editProjectSettings(
|
||||
return settings;
|
||||
}
|
||||
|
||||
const choices = settingKeys.reduce<Array<{ name: string; value: string }>>(
|
||||
(acc, setting) => {
|
||||
const skip =
|
||||
setting === 'framework' ||
|
||||
setting === 'commandForIgnoringBuildStep' ||
|
||||
setting === 'installCommand' ||
|
||||
localConfigurationOverrides?.[setting];
|
||||
if (!skip) {
|
||||
acc.push({ name: settingMap[setting], value: setting });
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
);
|
||||
const choices = settingKeys.reduce((acc, setting) => {
|
||||
const skip =
|
||||
setting === 'framework' ||
|
||||
setting === 'commandForIgnoringBuildStep' ||
|
||||
setting === 'installCommand' ||
|
||||
localConfigurationOverrides?.[setting];
|
||||
if (skip) return acc;
|
||||
return [...acc, { name: settingMap[setting], value: setting }];
|
||||
}, [] as { name: string; value: ConfigKeys }[]);
|
||||
|
||||
const settingFields = (await client.input.checkbox({
|
||||
const settingFields = await client.input.checkbox({
|
||||
message: 'Which settings would you like to overwrite (select multiple)?',
|
||||
choices,
|
||||
})) as ConfigKeys[];
|
||||
});
|
||||
|
||||
for (let setting of settingFields) {
|
||||
const field = settingMap[setting];
|
||||
settings[setting] = await client.input({
|
||||
settings[setting] = await client.input.text({
|
||||
message: `What's your ${chalk.bold(field)}?`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export default async function inputProject(
|
||||
let project: Project | ProjectNotFound | null = null;
|
||||
|
||||
while (!project || project instanceof ProjectNotFound) {
|
||||
const projectName = await client.input({
|
||||
const projectName = await client.input.text({
|
||||
message: 'What’s the name of your existing project?',
|
||||
});
|
||||
|
||||
@@ -106,7 +106,7 @@ export default async function inputProject(
|
||||
let newProjectName: string | null = null;
|
||||
|
||||
while (!newProjectName) {
|
||||
newProjectName = await client.input({
|
||||
newProjectName = await client.input.text({
|
||||
message: `What’s your project’s name?`,
|
||||
default: !detectedProject ? slugifiedName : undefined,
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ export async function inputRootDirectory(
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const rootDirectory = await client.input({
|
||||
const rootDirectory = await client.input.text({
|
||||
message: `In which directory is your code located?`,
|
||||
transformer: (input: string) => {
|
||||
return `${chalk.dim(`./`)}${input}`;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Separator } from '@inquirer/prompts';
|
||||
import { Separator } from '@inquirer/select';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import Client from '../client';
|
||||
import eraseLines from '../output/erase-lines';
|
||||
@@ -108,7 +108,7 @@ export default async function list(
|
||||
choices.push(cancelSeparator, _cancel);
|
||||
}
|
||||
|
||||
const answer = await client.select({
|
||||
const answer = await client.input.select({
|
||||
message,
|
||||
choices,
|
||||
pageSize,
|
||||
|
||||
@@ -51,7 +51,7 @@ export default async function selectOrg(
|
||||
return choices[defaultChoiceIndex].value;
|
||||
}
|
||||
|
||||
return await client.select({
|
||||
return await client.input.select({
|
||||
message: question,
|
||||
choices,
|
||||
default: choices[defaultChoiceIndex].value,
|
||||
|
||||
@@ -77,7 +77,7 @@ export default function text({
|
||||
// Tab
|
||||
// Right arrow
|
||||
autoCompleteChars = new Set(['\t', '\x1b[C']),
|
||||
// If true, converts everything the user types to to lowercase
|
||||
// If true, converts everything the user types to lowercase
|
||||
forceLowerCase = false,
|
||||
}: TextParams = {}): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import chalk from 'chalk';
|
||||
import { Separator } from '@inquirer/prompts';
|
||||
import { Separator } from '@inquirer/checkbox';
|
||||
import pluralize from 'pluralize';
|
||||
import { homedir } from 'os';
|
||||
import slugify from '@sindresorhus/slugify';
|
||||
@@ -144,7 +144,7 @@ export async function ensureRepoLink(
|
||||
if (yes) {
|
||||
remoteName = defaultRemote;
|
||||
} else {
|
||||
remoteName = await client.select({
|
||||
remoteName = await client.input.select({
|
||||
message: 'Which Git remote should be used?',
|
||||
choices: remoteNames.map(name => {
|
||||
return { name: name, value: name };
|
||||
@@ -219,7 +219,7 @@ export async function ensureRepoLink(
|
||||
selected = projects;
|
||||
} else {
|
||||
const addSeparators = projects.length > 0 && detectedProjectsCount > 0;
|
||||
selected = await client.input.checkbox({
|
||||
selected = await client.input.checkbox<Project | NewProject>({
|
||||
message: `Which Projects should be ${
|
||||
projects.length ? 'linked to' : 'created'
|
||||
}?`,
|
||||
@@ -259,18 +259,10 @@ export async function ensureRepoLink(
|
||||
},
|
||||
// Checked by default when there are no other existing Projects
|
||||
checked: projects.length === 0,
|
||||
};
|
||||
} as const;
|
||||
})
|
||||
),
|
||||
] as (
|
||||
| Separator
|
||||
| {
|
||||
newProject?: boolean;
|
||||
name: string;
|
||||
value: Project | NewProject;
|
||||
checked: boolean;
|
||||
}
|
||||
)[],
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ export async function readInput(
|
||||
|
||||
while (!input) {
|
||||
try {
|
||||
input = await client.input({ message });
|
||||
input = await client.input.text({ message });
|
||||
} catch (err: any) {
|
||||
console.log(); // \n
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { join } from 'path';
|
||||
import frameworkList, { Framework } from '@vercel/frameworks';
|
||||
import { frameworkList, Framework } from '@vercel/frameworks';
|
||||
import {
|
||||
detectFrameworks,
|
||||
getWorkspacePackagePaths,
|
||||
|
||||
@@ -97,7 +97,7 @@ async function getProjectLinkFromRepoLink(
|
||||
} else {
|
||||
const selectableProjects =
|
||||
projects.length > 0 ? projects : repoLink.repoConfig.projects;
|
||||
project = await client.select({
|
||||
project = await client.input.select({
|
||||
message: `Please select a Project:`,
|
||||
choices: selectableProjects.map(p => ({
|
||||
value: p,
|
||||
|
||||
6
packages/cli/test/dev/fixtures/08-hugo/package.json
Normal file
6
packages/cli/test/dev/fixtures/08-hugo/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,6 @@ menu:
|
||||
---
|
||||
|
||||
|
||||
This is an example of a custom shortcode that you can put right into your content. You will need to add a form action to the the shortcode to make it work. Check out [Formspree](https://formspree.io/) for a simple, free form service.
|
||||
This is an example of a custom shortcode that you can put right into your content. You will need to add a form action to the shortcode to make it work. Check out [Formspree](https://formspree.io/) for a simple, free form service.
|
||||
|
||||
{{< form-contact action="https://example.com" >}}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
}
|
||||
}
|
||||
6
packages/cli/test/dev/fixtures/go/package.json
Normal file
6
packages/cli/test/dev/fixtures/go/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
}
|
||||
}
|
||||
6
packages/cli/test/dev/fixtures/python-flask/package.json
Normal file
6
packages/cli/test/dev/fixtures/python-flask/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { name } from '../backend/app'
|
||||
|
||||
export default async function handler(request, response) {
|
||||
return response.status(200).send(`Hello, ${name}!`);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export const name = 'Batman'
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "vercel-ts-test",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
||||
4
packages/cli/test/dev/fixtures/vercel-ts-test/yarn.lock
Normal file
4
packages/cli/test/dev/fixtures/vercel-ts-test/yarn.lock
Normal file
@@ -0,0 +1,4 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
@@ -119,7 +119,6 @@ test(
|
||||
await testPath(200, `/api/date`, new RegExp(`Current date is ${year}`));
|
||||
await testPath(200, `/api/date.py`, new RegExp(`Current date is ${year}`));
|
||||
await testPath(200, `/api/headers`, (body: any, res: any) => {
|
||||
// @ts-ignore
|
||||
const { host } = new URL(res.url);
|
||||
expect(body).toBe(host);
|
||||
});
|
||||
@@ -198,7 +197,6 @@ test(
|
||||
);
|
||||
|
||||
await testPath(200, `/api/dump`, (body: any, res: any, isDev: any) => {
|
||||
// @ts-ignore
|
||||
const { host } = new URL(res.url);
|
||||
const { env, headers } = JSON.parse(body);
|
||||
|
||||
|
||||
@@ -544,4 +544,18 @@ describe('[vercel dev] ESM serverless functions', () => {
|
||||
{ skipDeploy: true }
|
||||
)
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] TypeScript importing another TS file, type=commonjs',
|
||||
testFixtureStdio(
|
||||
'vercel-ts-test',
|
||||
async (_testPath: any, port: number) => {
|
||||
const res = await fetch(`http://localhost:${port}/api/test`);
|
||||
validateResponseHeaders(res);
|
||||
const text = await res.text();
|
||||
expect(text).toEqual('Hello, Batman!');
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -7,5 +7,8 @@
|
||||
"dependencies": {
|
||||
"exeggcute": "^1.0.0",
|
||||
"once": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18.x"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,6 +348,9 @@ module.exports = async function prepare(session, binaryPath, tmpFixturesDir) {
|
||||
},
|
||||
'lambda-with-php-runtime': {
|
||||
'api/test.php': `<?php echo 'Hello from PHP'; ?>`,
|
||||
'package.json': JSON.stringify({
|
||||
engines: { node: '18.x' },
|
||||
}),
|
||||
'vercel.json': JSON.stringify({
|
||||
functions: {
|
||||
'api/**/*.php': {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { afterAll } from 'vitest';
|
||||
import findUp from 'find-up';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
2
packages/cli/test/integration-3.test.ts
vendored
2
packages/cli/test/integration-3.test.ts
vendored
@@ -625,7 +625,7 @@ test('ensure we render a prompt when deploying home directory', async () => {
|
||||
expect(exitCode, formatOutput({ stdout, stderr })).toBe(0);
|
||||
|
||||
expect(stderr).toContain(
|
||||
'You are deploying your home directory. Do you want to continue? (y/N)'
|
||||
'You are deploying your home directory. Do you want to continue?'
|
||||
);
|
||||
expect(stderr).toContain('Canceled');
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const originalCwd = process.cwd();
|
||||
import { afterAll, beforeAll, afterEach } from 'vitest';
|
||||
|
||||
// Register Jest matcher extensions for CLI unit tests
|
||||
import './matchers';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { beforeEach } from 'vitest';
|
||||
import { URL } from 'url';
|
||||
import chance from 'chance';
|
||||
import { client } from './client';
|
||||
|
||||
@@ -1,63 +1,13 @@
|
||||
/**
|
||||
* This file registers the custom Jest "matchers" that are useful for
|
||||
* writing CLI unit tests, and sets them up to be recognized by TypeScript.
|
||||
*
|
||||
* References:
|
||||
* - https://haspar.us/notes/adding-jest-custom-matchers-in-typescript
|
||||
* - https://gist.github.com/hasparus/4ebaa17ec5d3d44607f522bcb1cda9fb
|
||||
*/
|
||||
import { expect } from 'vitest';
|
||||
import { toOutput } from './matchers';
|
||||
|
||||
/// <reference types="@types/jest" />
|
||||
|
||||
import * as matchers from './matchers';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
type Tail<T extends unknown[]> = T extends [infer _Head, ...infer Tail]
|
||||
? Tail
|
||||
: never;
|
||||
|
||||
type AnyFunction = (...args: any[]) => any;
|
||||
type PromiseFunction = (...args: any[]) => Promise<any>;
|
||||
|
||||
type GetMatcherType<TP, TResult> = TP extends PromiseFunction
|
||||
? (...args: Tail<Parameters<TP>>) => Promise<TResult>
|
||||
: TP extends AnyFunction
|
||||
? (...args: Tail<Parameters<TP>>) => TResult
|
||||
: TP;
|
||||
|
||||
type GetMatchersType<TMatchers, TResult> = {
|
||||
[P in keyof TMatchers]: GetMatcherType<TMatchers[P], TResult>;
|
||||
};
|
||||
|
||||
type FirstParam<T extends AnyFunction> = Parameters<T>[0];
|
||||
|
||||
type OnlyMethodsWhereFirstArgIsOfType<TObject, TWantedFirstArg> = {
|
||||
[P in keyof TObject]: TObject[P] extends AnyFunction
|
||||
? TWantedFirstArg extends FirstParam<TObject[P]>
|
||||
? TObject[P]
|
||||
: [
|
||||
`Error: this function is present only when received is:`,
|
||||
FirstParam<TObject[P]>
|
||||
]
|
||||
: TObject[P];
|
||||
};
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace jest {
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface Matchers<R, T = {}>
|
||||
extends GetMatchersType<
|
||||
OnlyMethodsWhereFirstArgIsOfType<typeof matchers, T>,
|
||||
R
|
||||
> {}
|
||||
}
|
||||
interface ToOutputMatchers<R = unknown> {
|
||||
toOutput: (test: string, timeout?: number) => Promise<R>;
|
||||
}
|
||||
|
||||
const jestExpect = (global as any).expect;
|
||||
|
||||
if (jestExpect !== undefined) {
|
||||
jestExpect.extend(matchers);
|
||||
} else {
|
||||
console.error("Couldn't find Jest's global expect.");
|
||||
declare module 'vitest' {
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface Assertion<T = any> extends ToOutputMatchers<T> {}
|
||||
}
|
||||
|
||||
expect.extend({ toOutput });
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
printReceived,
|
||||
} from 'jest-matcher-utils';
|
||||
import type { Readable } from 'stream';
|
||||
import type { MatcherState } from 'expect';
|
||||
import type { MatcherState } from '@vitest/expect';
|
||||
import type { MatcherHintOptions } from 'jest-matcher-utils';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
|
||||
@@ -14,7 +14,10 @@ export async function toOutput(
|
||||
stream: Readable,
|
||||
test: string,
|
||||
timeout = 3000
|
||||
) {
|
||||
): Promise<{
|
||||
pass: boolean;
|
||||
message: () => string;
|
||||
}> {
|
||||
const { isNot } = this;
|
||||
const matcherName = 'toOutput';
|
||||
const matcherHintOptions: MatcherHintOptions = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`help command help output snapshots column width 40 1`] = `
|
||||
exports[`help command > help output snapshots > column width 40 1`] = `
|
||||
"
|
||||
▲ [1mvercel[22m [1mdeploy[22m [project-path] [options]
|
||||
|
||||
@@ -186,7 +186,7 @@ exports[`help command help output snapshots column width 40 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`help command help output snapshots column width 80 1`] = `
|
||||
exports[`help command > help output snapshots > column width 80 1`] = `
|
||||
"
|
||||
▲ [1mvercel[22m [1mdeploy[22m [project-path] [options]
|
||||
|
||||
@@ -260,7 +260,7 @@ exports[`help command help output snapshots column width 80 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`help command help output snapshots column width 120 1`] = `
|
||||
exports[`help command > help output snapshots > column width 120 1`] = `
|
||||
"
|
||||
▲ [1mvercel[22m [1mdeploy[22m [project-path] [options]
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { client } from '../../mocks/client';
|
||||
import alias from '../../../src/commands/alias';
|
||||
import { useUser } from '../../mocks/user';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { client } from '../../mocks/client';
|
||||
import { useUser } from '../../mocks/user';
|
||||
import bisect from '../../../src/commands/bisect';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||
import fs from 'fs-extra';
|
||||
import { join } from 'path';
|
||||
import { getWriteableDirectory } from '@vercel/build-utils';
|
||||
@@ -7,8 +8,9 @@ import { defaultProject, useProject } from '../../../mocks/project';
|
||||
import { useTeams } from '../../../mocks/team';
|
||||
import { useUser } from '../../../mocks/user';
|
||||
import { execSync } from 'child_process';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
jest.setTimeout(6 * 60 * 1000);
|
||||
vi.setConfig({ testTimeout: 6 * 60 * 1000 });
|
||||
|
||||
const fixture = (name: string) =>
|
||||
join(__dirname, '../../../fixtures/unit/commands/build', name);
|
||||
@@ -932,7 +934,7 @@ describe('build', () => {
|
||||
name: 'Error',
|
||||
message:
|
||||
'Invalid vercel.json - `rewrites[2]` should NOT have additional property `src`. Did you mean `source`?',
|
||||
stack: expect.stringContaining('at validateConfig'),
|
||||
stack: expect.stringContaining('at Module.validateConfig'),
|
||||
hideStackTrace: true,
|
||||
code: 'INVALID_VERCEL_CONFIG',
|
||||
link: 'https://vercel.com/docs/concepts/projects/project-configuration#rewrites',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { client } from '../../mocks/client';
|
||||
import certs from '../../../src/commands/certs';
|
||||
import { useUser } from '../../mocks/user';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import bytes from 'bytes';
|
||||
import fs from 'fs-extra';
|
||||
import { join } from 'path';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { client } from '../../mocks/client';
|
||||
import dns from '../../../src/commands/dns';
|
||||
import { useUser } from '../../mocks/user';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { client } from '../../mocks/client';
|
||||
import domains from '../../../src/commands/domains';
|
||||
import { useUser } from '../../mocks/user';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { parse } from 'dotenv';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { join } from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import { useUser } from '../../mocks/user';
|
||||
@@ -79,7 +80,7 @@ describe('git', () => {
|
||||
const gitPromise = git(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Set up');
|
||||
client.stdin.write('\n');
|
||||
client.stdin.write('y\n');
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
'Which scope should contain your project?'
|
||||
@@ -87,12 +88,12 @@ describe('git', () => {
|
||||
client.stdin.write('\r');
|
||||
|
||||
await expect(client.stderr).toOutput('Found project');
|
||||
client.stdin.write('\n');
|
||||
client.stdin.write('y\n');
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Do you still want to connect https://github.com/user2/repo2?`
|
||||
);
|
||||
client.stdin.write('h\n'); // vim left to switch from default no
|
||||
client.stdin.write('y\n');
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Connecting Git remote: https://github.com/user2/repo2`
|
||||
@@ -425,7 +426,7 @@ describe('git', () => {
|
||||
await expect(client.stderr).toOutput(
|
||||
`Are you sure you want to disconnect user/repo from your project?`
|
||||
);
|
||||
client.stdin.write('h\n'); // vim left to switch from default no
|
||||
client.stdin.write('y\n');
|
||||
await expect(client.stderr).toOutput('Disconnected user/repo.');
|
||||
|
||||
const newProjectData: Project = await client.fetch(
|
||||
@@ -526,9 +527,9 @@ describe('git', () => {
|
||||
`Found a repository in your local Git Config: https://github.com/user/repo`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
`Do you still want to connect https://github.com/user2/repo2?`
|
||||
`Do you still want to connect https://github.com/user2/repo2? (y/N)`
|
||||
);
|
||||
client.stdin.write('h\n'); // vim left to switch from default no
|
||||
client.stdin.write('y\n');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Connecting Git remote: https://github.com/user2/repo2`
|
||||
);
|
||||
@@ -574,9 +575,9 @@ describe('git', () => {
|
||||
`Found multiple Git repositories in your local Git config:\n • origin: https://github.com/user/repo.git\n • secondary: https://github.com/user/repo2.git`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
`Do you still want to connect https://github.com/user3/repo3?`
|
||||
`Do you still want to connect https://github.com/user3/repo3? (y/N)`
|
||||
);
|
||||
client.stdin.write('h\n'); // vim left to switch away from default no
|
||||
client.stdin.write('y\n');
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Connecting Git remote: https://github.com/user3/repo3`
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import {
|
||||
help,
|
||||
lineToString,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { client } from '../../mocks/client';
|
||||
import { useUser } from '../../mocks/user';
|
||||
import { useDeployment } from '../../mocks/deployment';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { basename, join } from 'path';
|
||||
import { readJSON } from 'fs-extra';
|
||||
import link from '../../../src/commands/link';
|
||||
@@ -26,17 +27,16 @@ describe('link', () => {
|
||||
client.cwd = cwd;
|
||||
const exitCodePromise = link(client);
|
||||
|
||||
client.stderr.pipe(process.stderr);
|
||||
await expect(client.stderr).toOutput('Set up');
|
||||
client.stdin.write('\n');
|
||||
client.stdin.write('y\n');
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
'Which scope should contain your project?'
|
||||
);
|
||||
client.stdin.write('\n');
|
||||
client.stdin.write('y\n');
|
||||
|
||||
await expect(client.stderr).toOutput('Link to it?');
|
||||
client.stdin.write('\n');
|
||||
client.stdin.write('y\n');
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Linked to ${user.username}/${project.name} (created .vercel and added it to .gitignore)`
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import createLineIterator from 'line-async-iterator';
|
||||
import { client } from '../../mocks/client';
|
||||
import { useUser } from '../../mocks/user';
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
import login from '../../../src/commands/login';
|
||||
import { emoji } from '../../../src/util/emoji';
|
||||
import { client } from '../../mocks/client';
|
||||
import { useUser } from '../../mocks/user';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
jest.setTimeout(10000);
|
||||
vi.setConfig({ testTimeout: 10000 });
|
||||
|
||||
describe('login', () => {
|
||||
it('should not allow the `--token` flag', async () => {
|
||||
@@ -54,7 +56,7 @@ describe('login', () => {
|
||||
client.stdin.write('\x1B[B'); // Down arrow
|
||||
client.stdin.write('\r'); // Return key
|
||||
|
||||
await expect(client.stderr).toOutput('Enter your email address:');
|
||||
await expect(client.stderr).toOutput('? Enter your email address:');
|
||||
|
||||
client.stdin.write(`${user.email}\n`);
|
||||
|
||||
@@ -77,7 +79,7 @@ describe('login', () => {
|
||||
client.stdin.write('\x1B[B'); // Down arrow
|
||||
client.stdin.write('\r'); // Return key
|
||||
|
||||
await expect(client.stderr).toOutput('Enter your email address:');
|
||||
await expect(client.stderr).toOutput('? Enter your email address:');
|
||||
|
||||
client.stdin.write(`${user.email}\n`);
|
||||
|
||||
@@ -119,7 +121,7 @@ describe('login', () => {
|
||||
client.stdin.write('\x1B[B'); // Down arrow
|
||||
client.stdin.write('\r'); // Return key
|
||||
|
||||
await expect(client.stderr).toOutput('Enter your email address:');
|
||||
await expect(client.stderr).toOutput('? Enter your email address:');
|
||||
|
||||
client.stdin.write(`${user.email}\n`);
|
||||
|
||||
@@ -162,7 +164,7 @@ describe('login', () => {
|
||||
client.stdin.write('\x1B[B'); // Down arrow
|
||||
client.stdin.write('\r'); // Return key
|
||||
|
||||
await expect(client.stderr).toOutput('Enter your email address:');
|
||||
await expect(client.stderr).toOutput('? Enter your email address:');
|
||||
|
||||
client.stdin.write(`${user.email}\n`);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import createLineIterator from 'line-async-iterator';
|
||||
import projects from '../../../src/commands/project';
|
||||
import { useUser } from '../../mocks/user';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import chalk from 'chalk';
|
||||
import { client } from '../../mocks/client';
|
||||
import { defaultProject, useProject } from '../../mocks/project';
|
||||
@@ -9,8 +10,9 @@ import { useDeployment } from '../../mocks/deployment';
|
||||
import { useTeams } from '../../mocks/team';
|
||||
import { useUser } from '../../mocks/user';
|
||||
import sleep from '../../../src/util/sleep';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
jest.setTimeout(60000);
|
||||
vi.setConfig({ testTimeout: 60000 });
|
||||
|
||||
describe('promote', () => {
|
||||
it('should error if timeout is invalid', async () => {
|
||||
@@ -113,7 +115,7 @@ describe('promote', () => {
|
||||
`Fetching deployment "${previousDeployment.url}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
'This deployment does not target production, therefore promotion will not \napply production environment variables. Are you sure you want to continue?'
|
||||
'? This deployment does not target production, therefore promotion will not apply\n production environment variables. Are you sure you want to continue?'
|
||||
);
|
||||
|
||||
// say "no" to the prompt
|
||||
@@ -136,7 +138,7 @@ describe('promote', () => {
|
||||
`Fetching deployment "${previousDeployment.url}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
'This deployment does not target production, therefore promotion will not \napply production environment variables. Are you sure you want to continue?'
|
||||
'? This deployment does not target production, therefore promotion will not apply\n production environment variables. Are you sure you want to continue?'
|
||||
);
|
||||
|
||||
// say "yes" to the prompt
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import pull from '../../../src/commands/pull';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { client } from '../../mocks/client';
|
||||
import { defaultProject, useProject } from '../../mocks/project';
|
||||
import redeploy from '../../../src/commands/redeploy';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { client } from '../../mocks/client';
|
||||
import {
|
||||
defaultProject,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user