netlify-cli 17.3.1 → 17.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -139
- package/npm-shrinkwrap.json +82 -82
- package/package.json +16 -15
- package/src/commands/addons/addons-auth.mjs +27 -30
- package/src/commands/addons/addons-config.mjs +145 -154
- package/src/commands/addons/addons-create.mjs +94 -108
- package/src/commands/addons/addons-delete.mjs +36 -41
- package/src/commands/addons/addons-list.mjs +38 -42
- package/src/commands/addons/addons.mjs +26 -28
- package/src/commands/addons/index.mjs +1 -1
- package/src/commands/api/api.mjs +45 -53
- package/src/commands/api/index.mjs +1 -1
- package/src/commands/base-command.mjs +597 -684
- package/src/commands/blobs/blobs-delete.mjs +35 -0
- package/src/commands/blobs/blobs-get.mjs +44 -0
- package/src/commands/blobs/blobs-list.mjs +48 -0
- package/src/commands/blobs/blobs-set.mjs +54 -0
- package/src/commands/blobs/blobs.mjs +32 -0
- package/src/commands/blobs/index.mjs +1 -0
- package/src/commands/build/build.mjs +55 -67
- package/src/commands/build/index.mjs +1 -1
- package/src/commands/completion/completion.mjs +41 -46
- package/src/commands/completion/index.mjs +1 -1
- package/src/commands/deploy/deploy.mjs +675 -710
- package/src/commands/deploy/index.mjs +1 -1
- package/src/commands/dev/dev-exec.mjs +20 -32
- package/src/commands/dev/dev.mjs +217 -302
- package/src/commands/dev/index.mjs +1 -1
- package/src/commands/dev/types.d.ts +30 -0
- package/src/commands/env/env-clone.mjs +157 -184
- package/src/commands/env/env-get.mjs +49 -68
- package/src/commands/env/env-import.mjs +100 -119
- package/src/commands/env/env-list.mjs +104 -129
- package/src/commands/env/env-set.mjs +160 -185
- package/src/commands/env/env-unset.mjs +104 -122
- package/src/commands/env/env.mjs +28 -30
- package/src/commands/env/index.mjs +1 -1
- package/src/commands/functions/functions-build.mjs +29 -41
- package/src/commands/functions/functions-create.mjs +533 -601
- package/src/commands/functions/functions-invoke.mjs +193 -216
- package/src/commands/functions/functions-list.mjs +45 -55
- package/src/commands/functions/functions-serve.mjs +51 -61
- package/src/commands/functions/functions.mjs +26 -32
- package/src/commands/functions/index.mjs +1 -1
- package/src/commands/index.mjs +2 -2
- package/src/commands/init/index.mjs +1 -1
- package/src/commands/init/init.mjs +138 -167
- package/src/commands/integration/deploy.mjs +337 -399
- package/src/commands/integration/index.mjs +12 -13
- package/src/commands/link/index.mjs +1 -1
- package/src/commands/link/link.mjs +298 -317
- package/src/commands/lm/index.mjs +1 -1
- package/src/commands/lm/lm-info.mjs +23 -31
- package/src/commands/lm/lm-install.mjs +13 -17
- package/src/commands/lm/lm-setup.mjs +80 -84
- package/src/commands/lm/lm-uninstall.mjs +7 -12
- package/src/commands/lm/lm.mjs +18 -22
- package/src/commands/login/index.mjs +1 -1
- package/src/commands/login/login.mjs +35 -41
- package/src/commands/logout/index.mjs +1 -1
- package/src/commands/logout/logout.mjs +25 -31
- package/src/commands/main.mjs +166 -201
- package/src/commands/open/index.mjs +1 -1
- package/src/commands/open/open-admin.mjs +15 -18
- package/src/commands/open/open-site.mjs +16 -19
- package/src/commands/open/open.mjs +24 -27
- package/src/commands/recipes/common.mjs +23 -34
- package/src/commands/recipes/index.mjs +1 -1
- package/src/commands/recipes/recipes-list.mjs +13 -20
- package/src/commands/recipes/recipes.mjs +59 -72
- package/src/commands/serve/index.mjs +1 -1
- package/src/commands/serve/serve.mjs +142 -189
- package/src/commands/sites/index.mjs +2 -2
- package/src/commands/sites/sites-create-template.mjs +214 -236
- package/src/commands/sites/sites-create.mjs +145 -157
- package/src/commands/sites/sites-delete.mjs +75 -81
- package/src/commands/sites/sites-list.mjs +63 -66
- package/src/commands/sites/sites.mjs +18 -20
- package/src/commands/status/index.mjs +1 -1
- package/src/commands/status/status-hooks.mjs +32 -34
- package/src/commands/status/status.mjs +99 -106
- package/src/commands/switch/index.mjs +1 -1
- package/src/commands/switch/switch.mjs +32 -37
- package/src/commands/types.d.ts +31 -0
- package/src/commands/unlink/index.mjs +1 -1
- package/src/commands/unlink/unlink.mjs +23 -29
- package/src/commands/watch/index.mjs +1 -1
- package/src/commands/watch/watch.mjs +91 -105
- package/src/functions-templates/javascript/hello/{{name}}.js +2 -3
- package/src/lib/account.mjs +4 -5
- package/src/lib/api.mjs +22 -20
- package/src/lib/blobs/blobs.mjs +36 -45
- package/src/lib/build.mjs +82 -85
- package/src/lib/completion/constants.mjs +2 -4
- package/src/lib/completion/generate-autocompletion.mjs +33 -36
- package/src/lib/completion/get-autocompletion.mjs +31 -35
- package/src/lib/completion/index.mjs +1 -1
- package/src/lib/completion/script.mjs +12 -19
- package/src/lib/edge-functions/bootstrap.mjs +3 -5
- package/src/lib/edge-functions/consts.mjs +9 -10
- package/src/lib/edge-functions/deploy.mjs +28 -34
- package/src/lib/edge-functions/editor-helper.mjs +29 -42
- package/src/lib/edge-functions/headers.mjs +24 -26
- package/src/lib/edge-functions/internal.mjs +38 -44
- package/src/lib/edge-functions/proxy.mjs +229 -228
- package/src/lib/edge-functions/registry.mjs +473 -574
- package/src/lib/exec-fetcher.mjs +115 -122
- package/src/lib/fs.mjs +28 -27
- package/src/lib/functions/background.mjs +16 -20
- package/src/lib/functions/config.mjs +12 -9
- package/src/lib/functions/form-submissions-handler.mjs +143 -149
- package/src/lib/functions/local-proxy.mjs +40 -44
- package/src/lib/functions/memoized-build.mjs +19 -21
- package/src/lib/functions/netlify-function.mjs +269 -249
- package/src/lib/functions/registry.mjs +509 -568
- package/src/lib/functions/runtimes/go/index.mjs +62 -71
- package/src/lib/functions/runtimes/index.mjs +8 -15
- package/src/lib/functions/runtimes/js/builders/netlify-lambda.mjs +55 -64
- package/src/lib/functions/runtimes/js/builders/zisi.mjs +135 -154
- package/src/lib/functions/runtimes/js/constants.mjs +1 -1
- package/src/lib/functions/runtimes/js/index.mjs +92 -109
- package/src/lib/functions/runtimes/js/worker.mjs +43 -45
- package/src/lib/functions/runtimes/rust/index.mjs +64 -73
- package/src/lib/functions/scheduled.mjs +70 -88
- package/src/lib/functions/server.mjs +269 -327
- package/src/lib/functions/synchronous.mjs +118 -147
- package/src/lib/functions/utils.mjs +38 -46
- package/src/lib/geo-location.mjs +69 -81
- package/src/lib/http-agent.mjs +87 -90
- package/src/lib/images/proxy.mjs +97 -89
- package/src/lib/log.mjs +6 -9
- package/src/lib/path.mjs +2 -1
- package/src/lib/render-error-template.mjs +19 -20
- package/src/lib/settings.mjs +17 -19
- package/src/lib/spinner.mjs +21 -23
- package/src/lib/string.mjs +4 -2
- package/src/recipes/vscode/index.mjs +69 -85
- package/src/recipes/vscode/settings.mjs +53 -58
- package/src/utils/addons/compare.mjs +31 -32
- package/src/utils/addons/diffs/index.mjs +16 -17
- package/src/utils/addons/diffs/options.mjs +99 -101
- package/src/utils/addons/prepare.mjs +100 -97
- package/src/utils/addons/prompts.mjs +73 -76
- package/src/utils/addons/render.mjs +33 -36
- package/src/utils/addons/validation.mjs +19 -15
- package/src/utils/banner.mjs +11 -16
- package/src/utils/build-info.mjs +65 -66
- package/src/utils/command-helpers.mjs +185 -199
- package/src/utils/create-deferred.mjs +9 -12
- package/src/utils/create-stream-promise.mjs +54 -47
- package/src/utils/deploy/constants.mjs +9 -11
- package/src/utils/deploy/deploy-site.mjs +162 -182
- package/src/utils/deploy/hash-config.mjs +21 -21
- package/src/utils/deploy/hash-files.mjs +34 -38
- package/src/utils/deploy/hash-fns.mjs +149 -154
- package/src/utils/deploy/hasher-segments.mjs +58 -52
- package/src/utils/deploy/upload-files.mjs +99 -113
- package/src/utils/deploy/util.mjs +85 -91
- package/src/utils/detect-server-settings.mjs +236 -268
- package/src/utils/dev.mjs +163 -178
- package/src/utils/dot-env.mjs +37 -42
- package/src/utils/env/index.mjs +148 -148
- package/src/utils/execa.mjs +9 -13
- package/src/utils/feature-flags.mjs +6 -5
- package/src/utils/framework-server.mjs +43 -52
- package/src/utils/functions/constants.mjs +1 -1
- package/src/utils/functions/functions.mjs +30 -40
- package/src/utils/functions/get-functions.mjs +28 -29
- package/src/utils/functions/index.mjs +3 -3
- package/src/utils/get-global-config.mjs +33 -36
- package/src/utils/get-package-json.mjs +14 -15
- package/src/utils/get-repo-data.mjs +54 -64
- package/src/utils/get-site.mjs +14 -14
- package/src/utils/gh-auth.mjs +79 -100
- package/src/utils/gitignore.mjs +37 -40
- package/src/utils/headers.mjs +33 -35
- package/src/utils/hooks/requires-site-info.mjs +26 -22
- package/src/utils/init/config-github.mjs +207 -219
- package/src/utils/init/config-manual.mjs +83 -100
- package/src/utils/init/config.mjs +25 -26
- package/src/utils/init/node-version.mjs +23 -30
- package/src/utils/init/plugins.mjs +12 -8
- package/src/utils/init/utils.mjs +152 -172
- package/src/utils/live-tunnel.mjs +118 -141
- package/src/utils/lm/install.mjs +220 -259
- package/src/utils/lm/requirements.mjs +54 -63
- package/src/utils/lm/steps.mjs +31 -31
- package/src/utils/lm/ui.mjs +13 -20
- package/src/utils/open-browser.mjs +31 -32
- package/src/utils/parse-raw-flags.mjs +39 -35
- package/src/utils/proxy-server.mjs +84 -71
- package/src/utils/proxy.mjs +696 -750
- package/src/utils/read-repo-url.mjs +48 -47
- package/src/utils/redirects.mjs +49 -49
- package/src/utils/request-id.mjs +2 -4
- package/src/utils/rules-proxy.mjs +96 -100
- package/src/utils/run-build.mjs +109 -132
- package/src/utils/shell.mjs +99 -106
- package/src/utils/sign-redirect.mjs +14 -14
- package/src/utils/sites/utils.mjs +48 -55
- package/src/utils/state-config.mjs +101 -101
- package/src/utils/static-server.mjs +28 -34
- package/src/utils/telemetry/index.mjs +2 -2
- package/src/utils/telemetry/report-error.mjs +45 -49
- package/src/utils/telemetry/request.mjs +36 -43
- package/src/utils/telemetry/telemetry.mjs +90 -105
- package/src/utils/telemetry/utils.mjs +5 -6
- package/src/utils/telemetry/validation.mjs +55 -53
- package/src/utils/types.d.ts +46 -0
- package/src/utils/validation.mjs +10 -13
|
@@ -1,82 +1,64 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
import { DefaultLogger, Project } from '@netlify/build-info'
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import { join, relative, resolve } from 'path';
|
|
3
|
+
import process from 'process';
|
|
4
|
+
import { format } from 'util';
|
|
5
|
+
import { DefaultLogger, Project } from '@netlify/build-info';
|
|
8
6
|
// eslint-disable-next-line import/extensions, n/no-missing-import
|
|
9
|
-
import { NodeFS, NoopLogger } from '@netlify/build-info/node'
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
import
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
normalizeConfig,
|
|
29
|
-
padLeft,
|
|
30
|
-
pollForToken,
|
|
31
|
-
sortOptions,
|
|
32
|
-
warn,
|
|
33
|
-
} from '../utils/command-helpers.mjs'
|
|
34
|
-
import getGlobalConfig from '../utils/get-global-config.mjs'
|
|
35
|
-
import { getSiteByName } from '../utils/get-site.mjs'
|
|
36
|
-
import openBrowser from '../utils/open-browser.mjs'
|
|
37
|
-
import StateConfig from '../utils/state-config.mjs'
|
|
38
|
-
import { identify, reportError, track } from '../utils/telemetry/index.mjs'
|
|
39
|
-
|
|
7
|
+
import { NodeFS, NoopLogger } from '@netlify/build-info/node';
|
|
8
|
+
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module '@net... Remove this comment to see the full error message
|
|
9
|
+
import { resolveConfig } from '@netlify/config';
|
|
10
|
+
import { Command, Option } from 'commander';
|
|
11
|
+
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'debu... Remove this comment to see the full error message
|
|
12
|
+
import debug from 'debug';
|
|
13
|
+
import { findUp } from 'find-up';
|
|
14
|
+
import inquirer from 'inquirer';
|
|
15
|
+
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'inqu... Remove this comment to see the full error message
|
|
16
|
+
import inquirerAutocompletePrompt from 'inquirer-autocomplete-prompt';
|
|
17
|
+
import merge from 'lodash/merge.js';
|
|
18
|
+
import { NetlifyAPI } from 'netlify';
|
|
19
|
+
import { getAgent } from '../lib/http-agent.mjs';
|
|
20
|
+
import { NETLIFY_CYAN, USER_AGENT, chalk, error, exit, getToken, log, normalizeConfig, padLeft, pollForToken, sortOptions, warn, } from '../utils/command-helpers.mjs';
|
|
21
|
+
import getGlobalConfig from '../utils/get-global-config.mjs';
|
|
22
|
+
import { getSiteByName } from '../utils/get-site.mjs';
|
|
23
|
+
import openBrowser from '../utils/open-browser.mjs';
|
|
24
|
+
import StateConfig from '../utils/state-config.mjs';
|
|
25
|
+
import { identify, reportError, track } from '../utils/telemetry/index.mjs';
|
|
40
26
|
// load the autocomplete plugin
|
|
41
|
-
inquirer.registerPrompt('autocomplete', inquirerAutocompletePrompt)
|
|
27
|
+
inquirer.registerPrompt('autocomplete', inquirerAutocompletePrompt);
|
|
42
28
|
/** Netlify CLI client id. Lives in bot@netlify.com */
|
|
43
29
|
// TODO: setup client for multiple environments
|
|
44
|
-
const CLIENT_ID = 'd6f37de6614df7ae58664cfca524744d73807a377f5ee71f1a254f78412e3750'
|
|
45
|
-
|
|
46
|
-
const NANO_SECS_TO_MSECS = 1e6
|
|
30
|
+
const CLIENT_ID = 'd6f37de6614df7ae58664cfca524744d73807a377f5ee71f1a254f78412e3750';
|
|
31
|
+
const NANO_SECS_TO_MSECS = 1e6;
|
|
47
32
|
/** The fallback width for the help terminal */
|
|
48
|
-
const FALLBACK_HELP_CMD_WIDTH = 80
|
|
49
|
-
|
|
50
|
-
const HELP_$ = NETLIFY_CYAN('$')
|
|
33
|
+
const FALLBACK_HELP_CMD_WIDTH = 80;
|
|
34
|
+
const HELP_$ = NETLIFY_CYAN('$');
|
|
51
35
|
/** indent on commands or description on the help page */
|
|
52
|
-
const HELP_INDENT_WIDTH = 2
|
|
36
|
+
const HELP_INDENT_WIDTH = 2;
|
|
53
37
|
/** separator width between term and description */
|
|
54
|
-
const HELP_SEPARATOR_WIDTH = 5
|
|
55
|
-
|
|
38
|
+
const HELP_SEPARATOR_WIDTH = 5;
|
|
56
39
|
/**
|
|
57
40
|
* A list of commands where we don't have to perform the workspace selection at.
|
|
58
41
|
* Those commands work with the system or are not writing any config files that need to be
|
|
59
42
|
* workspace aware.
|
|
60
43
|
*/
|
|
61
|
-
const COMMANDS_WITHOUT_WORKSPACE_OPTIONS = new Set(['api', 'recipes', 'completion', 'status', 'switch', 'login', 'lm'])
|
|
62
|
-
|
|
44
|
+
const COMMANDS_WITHOUT_WORKSPACE_OPTIONS = new Set(['api', 'recipes', 'completion', 'status', 'switch', 'login', 'lm']);
|
|
63
45
|
/**
|
|
64
46
|
* Formats a help list correctly with the correct indent
|
|
65
47
|
* @param {string[]} textArray
|
|
66
48
|
* @returns
|
|
67
49
|
*/
|
|
68
|
-
|
|
69
|
-
|
|
50
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'textArray' implicitly has an 'any' type... Remove this comment to see the full error message
|
|
51
|
+
const formatHelpList = (textArray) => textArray.join('\n').replace(/^/gm, ' '.repeat(HELP_INDENT_WIDTH));
|
|
70
52
|
/**
|
|
71
53
|
* Get the duration between a start time and the current time
|
|
72
54
|
* @param {bigint} startTime
|
|
73
55
|
* @returns
|
|
74
56
|
*/
|
|
57
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'startTime' implicitly has an 'any' type... Remove this comment to see the full error message
|
|
75
58
|
const getDuration = function (startTime) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
59
|
+
const durationNs = process.hrtime.bigint() - startTime;
|
|
60
|
+
return Math.round(Number(durationNs / BigInt(NANO_SECS_TO_MSECS)));
|
|
61
|
+
};
|
|
80
62
|
/**
|
|
81
63
|
* Retrieves a workspace package based of the filter flag that is provided.
|
|
82
64
|
* If the filter flag does not match a workspace package or is not defined then it will prompt with an autocomplete to select a package
|
|
@@ -84,664 +66,595 @@ const getDuration = function (startTime) {
|
|
|
84
66
|
* @param {string=} filter
|
|
85
67
|
* @returns {Promise<string>}
|
|
86
68
|
*/
|
|
69
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'project' implicitly has an 'any' type.
|
|
87
70
|
async function selectWorkspace(project, filter) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
71
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'pkg' implicitly has an 'any' type.
|
|
72
|
+
const selected = project.workspace?.packages.find((pkg) => {
|
|
73
|
+
if (project.relativeBaseDirectory &&
|
|
74
|
+
project.relativeBaseDirectory.length !== 0 &&
|
|
75
|
+
pkg.path.startsWith(project.relativeBaseDirectory)) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return (pkg.name && pkg.name === filter) || pkg.path === filter;
|
|
79
|
+
});
|
|
80
|
+
if (!selected) {
|
|
81
|
+
log();
|
|
82
|
+
log(chalk.cyan(`We've detected multiple sites inside your repository`));
|
|
83
|
+
const { result } = await inquirer.prompt({
|
|
84
|
+
name: 'result',
|
|
85
|
+
// @ts-expect-error TS(2769) FIXME: No overload matches this call.
|
|
86
|
+
type: 'autocomplete',
|
|
87
|
+
message: 'Select the site you want to work with',
|
|
88
|
+
// @ts-expect-error TS(7006) FIXME: Parameter '_' implicitly has an 'any' type.
|
|
89
|
+
source: (/** @type {string} */ _, input = '') => (project.workspace?.packages || [])
|
|
90
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'pkg' implicitly has an 'any' type.
|
|
91
|
+
.filter((pkg) => pkg.path.includes(input))
|
|
92
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'pkg' implicitly has an 'any' type.
|
|
93
|
+
.map((pkg) => ({
|
|
94
|
+
name: `${pkg.name ? `${chalk.bold(pkg.name)} ` : ''}${pkg.path} ${chalk.dim(`--filter ${pkg.name || pkg.path}`)}`,
|
|
95
|
+
value: pkg.path,
|
|
96
|
+
})),
|
|
97
|
+
});
|
|
98
|
+
return result;
|
|
95
99
|
}
|
|
96
|
-
return
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
if (!selected) {
|
|
100
|
-
log()
|
|
101
|
-
log(chalk.cyan(`We've detected multiple sites inside your repository`))
|
|
102
|
-
|
|
103
|
-
const { result } = await inquirer.prompt({
|
|
104
|
-
name: 'result',
|
|
105
|
-
type: 'autocomplete',
|
|
106
|
-
message: 'Select the site you want to work with',
|
|
107
|
-
source: (/** @type {string} */ _, input = '') =>
|
|
108
|
-
(project.workspace?.packages || [])
|
|
109
|
-
.filter((pkg) => pkg.path.includes(input))
|
|
110
|
-
.map((pkg) => ({
|
|
111
|
-
name: `${pkg.name ? `${chalk.bold(pkg.name)} ` : ''}${pkg.path} ${chalk.dim(
|
|
112
|
-
`--filter ${pkg.name || pkg.path}`,
|
|
113
|
-
)}`,
|
|
114
|
-
value: pkg.path,
|
|
115
|
-
})),
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
return result
|
|
119
|
-
}
|
|
120
|
-
return selected.path
|
|
100
|
+
return selected.path;
|
|
121
101
|
}
|
|
122
|
-
|
|
123
102
|
/** Base command class that provides tracking and config initialization */
|
|
124
103
|
export default class BaseCommand extends Command {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
*/
|
|
142
|
-
// here we actually want to disable the lint rule as its value is set
|
|
143
|
-
// eslint-disable-next-line workspace/no-process-cwd
|
|
144
|
-
workingDir = process.cwd()
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* The workspace root if inside a mono repository.
|
|
148
|
-
* Must not be the repository root!
|
|
149
|
-
* @type {string|undefined}
|
|
150
|
-
*/
|
|
151
|
-
jsWorkspaceRoot
|
|
152
|
-
/**
|
|
153
|
-
* The current workspace package we should execute the commands in
|
|
154
|
-
* @type {string|undefined}
|
|
155
|
-
*/
|
|
156
|
-
workspacePackage
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* IMPORTANT this function will be called for each command!
|
|
160
|
-
* Don't do anything expensive in there.
|
|
161
|
-
* @param {string} name The command name
|
|
162
|
-
* @returns
|
|
163
|
-
*/
|
|
164
|
-
createCommand(name) {
|
|
165
|
-
const base = new BaseCommand(name)
|
|
166
|
-
// If --silent or --json flag passed disable logger
|
|
167
|
-
.addOption(new Option('--json', 'Output return values as JSON').hideHelp(true))
|
|
168
|
-
.addOption(new Option('--silent', 'Silence CLI output').hideHelp(true))
|
|
169
|
-
.addOption(new Option('--cwd <cwd>').hideHelp(true))
|
|
170
|
-
.addOption(new Option('-o, --offline').hideHelp(true))
|
|
171
|
-
.addOption(new Option('--auth <token>', 'Netlify auth token').hideHelp(true))
|
|
172
|
-
.addOption(
|
|
173
|
-
new Option('--httpProxy [address]', 'Old, prefer --http-proxy. Proxy server address to route requests through.')
|
|
174
|
-
.default(process.env.HTTP_PROXY || process.env.HTTPS_PROXY)
|
|
175
|
-
.hideHelp(true),
|
|
176
|
-
)
|
|
177
|
-
.addOption(
|
|
178
|
-
new Option(
|
|
179
|
-
'--httpProxyCertificateFilename [file]',
|
|
180
|
-
'Old, prefer --http-proxy-certificate-filename. Certificate file to use when connecting using a proxy server.',
|
|
181
|
-
)
|
|
182
|
-
.default(process.env.NETLIFY_PROXY_CERTIFICATE_FILENAME)
|
|
183
|
-
.hideHelp(true),
|
|
184
|
-
)
|
|
185
|
-
.addOption(
|
|
186
|
-
new Option(
|
|
187
|
-
'--http-proxy-certificate-filename [file]',
|
|
188
|
-
'Certificate file to use when connecting using a proxy server',
|
|
189
|
-
)
|
|
190
|
-
.default(process.env.NETLIFY_PROXY_CERTIFICATE_FILENAME)
|
|
191
|
-
.hideHelp(true),
|
|
192
|
-
)
|
|
193
|
-
.addOption(
|
|
194
|
-
new Option('--httpProxy [address]', 'Proxy server address to route requests through.')
|
|
195
|
-
.default(process.env.HTTP_PROXY || process.env.HTTPS_PROXY)
|
|
196
|
-
.hideHelp(true),
|
|
197
|
-
)
|
|
198
|
-
.option('--debug', 'Print debugging information')
|
|
199
|
-
|
|
200
|
-
// only add the `--filter` option to commands that are workspace aware
|
|
201
|
-
if (!COMMANDS_WITHOUT_WORKSPACE_OPTIONS.has(name)) {
|
|
202
|
-
base.option('--filter <app>', 'For monorepos, specify the name of the application to run the command in')
|
|
104
|
+
constructor() {
|
|
105
|
+
super(...arguments);
|
|
106
|
+
/** @type {{ startTime: bigint, payload?: any}} */
|
|
107
|
+
this.analytics = { startTime: process.hrtime.bigint() };
|
|
108
|
+
/**
|
|
109
|
+
* The working directory that is used for reading the `netlify.toml` file and storing the state.
|
|
110
|
+
* In a monorepo context this must not be the process working directory and can be an absolute path to the
|
|
111
|
+
* Package/Site that should be worked in.
|
|
112
|
+
*/
|
|
113
|
+
// here we actually want to disable the lint rule as its value is set
|
|
114
|
+
// eslint-disable-next-line workspace/no-process-cwd
|
|
115
|
+
this.workingDir = process.cwd();
|
|
116
|
+
/** @private */
|
|
117
|
+
this.noBaseOptions = false;
|
|
118
|
+
/** @type {string[]} The examples list for the command (used inside doc generation and help page) */
|
|
119
|
+
this.examples = [];
|
|
203
120
|
}
|
|
204
|
-
|
|
205
|
-
return base.hook('preAction', async (_parentCommand, actionCommand) => {
|
|
206
|
-
if (actionCommand.opts()?.debug) {
|
|
207
|
-
process.env.DEBUG = '*'
|
|
208
|
-
}
|
|
209
|
-
debug(`${name}:preAction`)('start')
|
|
210
|
-
this.analytics = { startTime: process.hrtime.bigint() }
|
|
211
|
-
// @ts-ignore cannot type actionCommand as BaseCommand
|
|
212
|
-
await this.init(actionCommand)
|
|
213
|
-
debug(`${name}:preAction`)('end')
|
|
214
|
-
})
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/** @private */
|
|
218
|
-
noBaseOptions = false
|
|
219
|
-
|
|
220
|
-
/** don't show help options on command overview (mostly used on top commands like `addons` where options only apply on children) */
|
|
221
|
-
noHelpOptions() {
|
|
222
|
-
this.noBaseOptions = true
|
|
223
|
-
return this
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/** @type {string[]} The examples list for the command (used inside doc generation and help page) */
|
|
227
|
-
examples = []
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Set examples for the command
|
|
231
|
-
* @param {string[]} examples
|
|
232
|
-
*/
|
|
233
|
-
addExamples(examples) {
|
|
234
|
-
this.examples = examples
|
|
235
|
-
return this
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Overrides the help output of commander with custom styling
|
|
240
|
-
* @returns {import('commander').Help}
|
|
241
|
-
*/
|
|
242
|
-
createHelp() {
|
|
243
|
-
const help = super.createHelp()
|
|
244
|
-
|
|
245
|
-
help.commandUsage = (command) => {
|
|
246
|
-
const term =
|
|
247
|
-
this.name() === 'netlify'
|
|
248
|
-
? `${HELP_$} ${command.name()} [COMMAND]`
|
|
249
|
-
: `${HELP_$} ${command.parent?.name()} ${command.name()} ${command.usage()}`
|
|
250
|
-
|
|
251
|
-
return padLeft(term, HELP_INDENT_WIDTH)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
121
|
/**
|
|
255
|
-
*
|
|
122
|
+
* IMPORTANT this function will be called for each command!
|
|
123
|
+
* Don't do anything expensive in there.
|
|
124
|
+
* @param {string} name The command name
|
|
125
|
+
* @returns
|
|
256
126
|
*/
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
127
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'name' implicitly has an 'any' type.
|
|
128
|
+
createCommand(name) {
|
|
129
|
+
const base = new BaseCommand(name)
|
|
130
|
+
// If --silent or --json flag passed disable logger
|
|
131
|
+
.addOption(new Option('--json', 'Output return values as JSON').hideHelp(true))
|
|
132
|
+
.addOption(new Option('--silent', 'Silence CLI output').hideHelp(true))
|
|
133
|
+
.addOption(new Option('--cwd <cwd>').hideHelp(true))
|
|
134
|
+
.addOption(new Option('-o, --offline').hideHelp(true))
|
|
135
|
+
.addOption(new Option('--auth <token>', 'Netlify auth token').hideHelp(true))
|
|
136
|
+
.addOption(new Option('--httpProxy [address]', 'Old, prefer --http-proxy. Proxy server address to route requests through.')
|
|
137
|
+
.default(process.env.HTTP_PROXY || process.env.HTTPS_PROXY)
|
|
138
|
+
.hideHelp(true))
|
|
139
|
+
.addOption(new Option('--httpProxyCertificateFilename [file]', 'Old, prefer --http-proxy-certificate-filename. Certificate file to use when connecting using a proxy server.')
|
|
140
|
+
.default(process.env.NETLIFY_PROXY_CERTIFICATE_FILENAME)
|
|
141
|
+
.hideHelp(true))
|
|
142
|
+
.addOption(new Option('--http-proxy-certificate-filename [file]', 'Certificate file to use when connecting using a proxy server')
|
|
143
|
+
.default(process.env.NETLIFY_PROXY_CERTIFICATE_FILENAME)
|
|
144
|
+
.hideHelp(true))
|
|
145
|
+
.addOption(new Option('--httpProxy [address]', 'Proxy server address to route requests through.')
|
|
146
|
+
.default(process.env.HTTP_PROXY || process.env.HTTPS_PROXY)
|
|
147
|
+
.hideHelp(true))
|
|
148
|
+
.option('--debug', 'Print debugging information');
|
|
149
|
+
// only add the `--filter` option to commands that are workspace aware
|
|
150
|
+
if (!COMMANDS_WITHOUT_WORKSPACE_OPTIONS.has(name)) {
|
|
151
|
+
base.option('--filter <app>', 'For monorepos, specify the name of the application to run the command in');
|
|
152
|
+
}
|
|
153
|
+
return base.hook('preAction', async (_parentCommand, actionCommand) => {
|
|
154
|
+
if (actionCommand.opts()?.debug) {
|
|
155
|
+
process.env.DEBUG = '*';
|
|
156
|
+
}
|
|
157
|
+
debug(`${name}:preAction`)('start');
|
|
158
|
+
this.analytics = { startTime: process.hrtime.bigint() };
|
|
159
|
+
await this.init(actionCommand);
|
|
160
|
+
debug(`${name}:preAction`)('end');
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/** don't show help options on command overview (mostly used on top commands like `addons` where options only apply on children) */
|
|
164
|
+
noHelpOptions() {
|
|
165
|
+
this.noBaseOptions = true;
|
|
166
|
+
return this;
|
|
270
167
|
}
|
|
271
|
-
|
|
272
168
|
/**
|
|
273
|
-
*
|
|
274
|
-
* @param {
|
|
275
|
-
* @returns {number}
|
|
169
|
+
* Set examples for the command
|
|
170
|
+
* @param {string[]} examples
|
|
276
171
|
*/
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
172
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'examples' implicitly has an 'any' type.
|
|
173
|
+
addExamples(examples) {
|
|
174
|
+
this.examples = examples;
|
|
175
|
+
return this;
|
|
176
|
+
}
|
|
280
177
|
/**
|
|
281
|
-
*
|
|
282
|
-
* @
|
|
283
|
-
* @param {import('commander').Help} helper
|
|
284
|
-
* @returns {number}
|
|
178
|
+
* Overrides the help output of commander with custom styling
|
|
179
|
+
* @returns {import('commander').Help}
|
|
285
180
|
*/
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
181
|
+
createHelp() {
|
|
182
|
+
const help = super.createHelp();
|
|
183
|
+
help.commandUsage = (command) => {
|
|
184
|
+
const term = this.name() === 'netlify'
|
|
185
|
+
? `${HELP_$} ${command.name()} [COMMAND]`
|
|
186
|
+
: `${HELP_$} ${command.parent?.name()} ${command.name()} ${command.usage()}`;
|
|
187
|
+
return padLeft(term, HELP_INDENT_WIDTH);
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* @param {BaseCommand} command
|
|
191
|
+
*/
|
|
192
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'command' implicitly has an 'any' type.
|
|
193
|
+
const getCommands = (command) => {
|
|
194
|
+
const parentCommand = this.name() === 'netlify' ? command : command.parent;
|
|
195
|
+
return (
|
|
196
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'cmd' implicitly has an 'any' type.
|
|
197
|
+
parentCommand?.commands.filter((cmd) => {
|
|
198
|
+
if (cmd._hidden)
|
|
199
|
+
return false;
|
|
200
|
+
// the root command
|
|
201
|
+
if (this.name() === 'netlify') {
|
|
202
|
+
// don't include subcommands on the main page
|
|
203
|
+
return !cmd.name().includes(':');
|
|
204
|
+
}
|
|
205
|
+
return cmd.name().startsWith(`${command.name()}:`);
|
|
206
|
+
}) || []);
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* override the longestSubcommandTermLength
|
|
210
|
+
* @param {BaseCommand} command
|
|
211
|
+
* @returns {number}
|
|
212
|
+
*/
|
|
213
|
+
help.longestSubcommandTermLength = (command) =>
|
|
214
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'max' implicitly has an 'any' type.
|
|
215
|
+
getCommands(command).reduce((max, cmd) => Math.max(max, cmd.name().length), 0);
|
|
216
|
+
/**
|
|
217
|
+
* override the longestOptionTermLength to react on hide options flag
|
|
218
|
+
* @param {BaseCommand} command
|
|
219
|
+
* @param {import('commander').Help} helper
|
|
220
|
+
* @returns {number}
|
|
221
|
+
*/
|
|
222
|
+
help.longestOptionTermLength = (command, helper) =>
|
|
223
|
+
// @ts-expect-error TS(2551) FIXME: Property 'noBaseOptions' does not exist on type 'C... Remove this comment to see the full error message
|
|
224
|
+
(command.noBaseOptions === false &&
|
|
225
|
+
helper.visibleOptions(command).reduce((max, option) => Math.max(max, helper.optionTerm(option).length), 0)) ||
|
|
226
|
+
0;
|
|
227
|
+
/**
|
|
228
|
+
* override the format help function to style it correctly
|
|
229
|
+
* @param {BaseCommand} command
|
|
230
|
+
* @param {import('commander').Help} helper
|
|
231
|
+
* @returns {string}
|
|
232
|
+
*/
|
|
233
|
+
help.formatHelp = (command, helper) => {
|
|
234
|
+
const parentCommand = this.name() === 'netlify' ? command : command.parent;
|
|
235
|
+
const termWidth = helper.padWidth(command, helper);
|
|
236
|
+
const helpWidth = helper.helpWidth || FALLBACK_HELP_CMD_WIDTH;
|
|
237
|
+
/**
|
|
238
|
+
* formats a term correctly
|
|
239
|
+
* @param {string} term
|
|
240
|
+
* @param {string} [description]
|
|
241
|
+
* @param {boolean} [isCommand]
|
|
242
|
+
* @returns {string}
|
|
243
|
+
*/
|
|
244
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'term' implicitly has an 'any' type.
|
|
245
|
+
const formatItem = (term, description, isCommand = false) => {
|
|
246
|
+
const bang = isCommand ? `${HELP_$} ` : '';
|
|
247
|
+
if (description) {
|
|
248
|
+
const pad = termWidth + HELP_SEPARATOR_WIDTH;
|
|
249
|
+
const fullText = `${bang}${term.padEnd(pad - (isCommand ? 2 : 0))}${chalk.grey(description)}`;
|
|
250
|
+
return helper.wrap(fullText, helpWidth - HELP_INDENT_WIDTH, pad);
|
|
251
|
+
}
|
|
252
|
+
return `${bang}${term}`;
|
|
253
|
+
};
|
|
254
|
+
/** @type {string[]} */
|
|
255
|
+
// @ts-expect-error TS(7034) FIXME: Variable 'output' implicitly has type 'any[]' in s... Remove this comment to see the full error message
|
|
256
|
+
let output = [];
|
|
257
|
+
// Description
|
|
258
|
+
const [topDescription, ...commandDescription] = (helper.commandDescription(command) || '').split('\n');
|
|
259
|
+
if (topDescription.length !== 0) {
|
|
260
|
+
// @ts-expect-error TS(7005) FIXME: Variable 'output' implicitly has an 'any[]' type.
|
|
261
|
+
output = [...output, topDescription, ''];
|
|
262
|
+
}
|
|
263
|
+
// on the parent help command the version should be displayed
|
|
264
|
+
if (this.name() === 'netlify') {
|
|
265
|
+
// @ts-expect-error TS(7005) FIXME: Variable 'output' implicitly has an 'any[]' type.
|
|
266
|
+
output = [...output, chalk.bold('VERSION'), formatHelpList([formatItem(USER_AGENT)]), ''];
|
|
267
|
+
}
|
|
268
|
+
// Usage
|
|
269
|
+
// @ts-expect-error TS(7005) FIXME: Variable 'output' implicitly has an 'any[]' type.
|
|
270
|
+
output = [...output, chalk.bold('USAGE'), helper.commandUsage(command), ''];
|
|
271
|
+
// Arguments
|
|
272
|
+
const argumentList = helper
|
|
273
|
+
.visibleArguments(command)
|
|
274
|
+
.map((argument) => formatItem(helper.argumentTerm(argument), helper.argumentDescription(argument)));
|
|
275
|
+
if (argumentList.length !== 0) {
|
|
276
|
+
output = [...output, chalk.bold('ARGUMENTS'), formatHelpList(argumentList), ''];
|
|
277
|
+
}
|
|
278
|
+
// @ts-expect-error TS(2551) FIXME: Property 'noBaseOptions' does not exist on type 'C... Remove this comment to see the full error message
|
|
279
|
+
if (command.noBaseOptions === false) {
|
|
280
|
+
// Options
|
|
281
|
+
const optionList = helper
|
|
282
|
+
.visibleOptions(command)
|
|
283
|
+
.sort(sortOptions)
|
|
284
|
+
.map((option) => formatItem(helper.optionTerm(option), helper.optionDescription(option)));
|
|
285
|
+
if (optionList.length !== 0) {
|
|
286
|
+
output = [...output, chalk.bold('OPTIONS'), formatHelpList(optionList), ''];
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Description
|
|
290
|
+
if (commandDescription.length !== 0) {
|
|
291
|
+
output = [...output, chalk.bold('DESCRIPTION'), formatHelpList(commandDescription), ''];
|
|
292
|
+
}
|
|
293
|
+
// Aliases
|
|
294
|
+
// @ts-expect-error TS(2551) FIXME: Property '_aliases' does not exist on type 'Comman... Remove this comment to see the full error message
|
|
295
|
+
if (command._aliases.length !== 0) {
|
|
296
|
+
// @ts-expect-error TS(2551) FIXME: Property '_aliases' does not exist on type 'Comman... Remove this comment to see the full error message
|
|
297
|
+
const aliases = command._aliases.map((alias) => formatItem(`${parentCommand.name()} ${alias}`, null, true));
|
|
298
|
+
output = [...output, chalk.bold('ALIASES'), formatHelpList(aliases), ''];
|
|
299
|
+
}
|
|
300
|
+
// @ts-expect-error TS(2339) FIXME: Property 'examples' does not exist on type 'Comman... Remove this comment to see the full error message
|
|
301
|
+
if (command.examples.length !== 0) {
|
|
302
|
+
output = [
|
|
303
|
+
...output,
|
|
304
|
+
chalk.bold('EXAMPLES'),
|
|
305
|
+
// @ts-expect-error TS(2339) FIXME: Property 'examples' does not exist on type 'Comman... Remove this comment to see the full error message
|
|
306
|
+
formatHelpList(command.examples.map((example) => `${HELP_$} ${example}`)),
|
|
307
|
+
'',
|
|
308
|
+
];
|
|
309
|
+
}
|
|
310
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'cmd' implicitly has an 'any' type.
|
|
311
|
+
const commandList = getCommands(command).map((cmd) => formatItem(cmd.name(), helper.subcommandDescription(cmd).split('\n')[0], true));
|
|
312
|
+
if (commandList.length !== 0) {
|
|
313
|
+
output = [...output, chalk.bold('COMMANDS'), formatHelpList(commandList), ''];
|
|
314
|
+
}
|
|
315
|
+
return [...output, ''].join('\n');
|
|
316
|
+
};
|
|
317
|
+
return help;
|
|
318
|
+
}
|
|
291
319
|
/**
|
|
292
|
-
*
|
|
293
|
-
* @param {
|
|
294
|
-
* @param {import('commander').Help} helper
|
|
295
|
-
* @returns {string}
|
|
320
|
+
* Will be called on the end of an action to track the metrics
|
|
321
|
+
* @param {*} [error_]
|
|
296
322
|
*/
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const pad = termWidth + HELP_SEPARATOR_WIDTH
|
|
313
|
-
const fullText = `${bang}${term.padEnd(pad - (isCommand ? 2 : 0))}${chalk.grey(description)}`
|
|
314
|
-
return helper.wrap(fullText, helpWidth - HELP_INDENT_WIDTH, pad)
|
|
323
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'error_' implicitly has an 'any' type.
|
|
324
|
+
async onEnd(error_) {
|
|
325
|
+
// @ts-expect-error TS(2339) FIXME: Property 'payload' does not exist on type '{ start... Remove this comment to see the full error message
|
|
326
|
+
const { payload, startTime } = this.analytics;
|
|
327
|
+
const duration = getDuration(startTime);
|
|
328
|
+
const status = error_ === undefined ? 'success' : 'error';
|
|
329
|
+
const command = Array.isArray(this.args) ? this.args[0] : this.name();
|
|
330
|
+
debug(`${this.name()}:onEnd`)(`Command: ${command}. Status: ${status}. Duration: ${duration}ms`);
|
|
331
|
+
try {
|
|
332
|
+
await track('command', {
|
|
333
|
+
...payload,
|
|
334
|
+
command,
|
|
335
|
+
duration,
|
|
336
|
+
status,
|
|
337
|
+
});
|
|
315
338
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
let output = []
|
|
322
|
-
|
|
323
|
-
// Description
|
|
324
|
-
const [topDescription, ...commandDescription] = (helper.commandDescription(command) || '').split('\n')
|
|
325
|
-
if (topDescription.length !== 0) {
|
|
326
|
-
output = [...output, topDescription, '']
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// on the parent help command the version should be displayed
|
|
330
|
-
if (this.name() === 'netlify') {
|
|
331
|
-
output = [...output, chalk.bold('VERSION'), formatHelpList([formatItem(USER_AGENT)]), '']
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Usage
|
|
335
|
-
output = [...output, chalk.bold('USAGE'), helper.commandUsage(command), '']
|
|
336
|
-
|
|
337
|
-
// Arguments
|
|
338
|
-
const argumentList = helper
|
|
339
|
-
.visibleArguments(command)
|
|
340
|
-
.map((argument) => formatItem(helper.argumentTerm(argument), helper.argumentDescription(argument)))
|
|
341
|
-
if (argumentList.length !== 0) {
|
|
342
|
-
output = [...output, chalk.bold('ARGUMENTS'), formatHelpList(argumentList), '']
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (command.noBaseOptions === false) {
|
|
346
|
-
// Options
|
|
347
|
-
const optionList = helper
|
|
348
|
-
.visibleOptions(command)
|
|
349
|
-
.sort(sortOptions)
|
|
350
|
-
.map((option) => formatItem(helper.optionTerm(option), helper.optionDescription(option)))
|
|
351
|
-
if (optionList.length !== 0) {
|
|
352
|
-
output = [...output, chalk.bold('OPTIONS'), formatHelpList(optionList), '']
|
|
339
|
+
catch { }
|
|
340
|
+
if (error_ !== undefined) {
|
|
341
|
+
// @ts-expect-error TS(2345) FIXME: Argument of type 'string | Error' is not assignabl... Remove this comment to see the full error message
|
|
342
|
+
error(error_ instanceof Error ? error_ : format(error_), { exit: false });
|
|
343
|
+
exit(1);
|
|
353
344
|
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Description
|
|
357
|
-
if (commandDescription.length !== 0) {
|
|
358
|
-
output = [...output, chalk.bold('DESCRIPTION'), formatHelpList(commandDescription), '']
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Aliases
|
|
362
|
-
|
|
363
|
-
if (command._aliases.length !== 0) {
|
|
364
|
-
const aliases = command._aliases.map((alias) => formatItem(`${parentCommand.name()} ${alias}`, null, true))
|
|
365
|
-
output = [...output, chalk.bold('ALIASES'), formatHelpList(aliases), '']
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (command.examples.length !== 0) {
|
|
369
|
-
output = [
|
|
370
|
-
...output,
|
|
371
|
-
chalk.bold('EXAMPLES'),
|
|
372
|
-
formatHelpList(command.examples.map((example) => `${HELP_$} ${example}`)),
|
|
373
|
-
'',
|
|
374
|
-
]
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const commandList = getCommands(command).map((cmd) =>
|
|
378
|
-
formatItem(cmd.name(), helper.subcommandDescription(cmd).split('\n')[0], true),
|
|
379
|
-
)
|
|
380
|
-
if (commandList.length !== 0) {
|
|
381
|
-
output = [...output, chalk.bold('COMMANDS'), formatHelpList(commandList), '']
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
return [...output, ''].join('\n')
|
|
385
|
-
}
|
|
386
|
-
return help
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Will be called on the end of an action to track the metrics
|
|
391
|
-
* @param {*} [error_]
|
|
392
|
-
*/
|
|
393
|
-
async onEnd(error_) {
|
|
394
|
-
const { payload, startTime } = this.analytics
|
|
395
|
-
const duration = getDuration(startTime)
|
|
396
|
-
const status = error_ === undefined ? 'success' : 'error'
|
|
397
|
-
|
|
398
|
-
const command = Array.isArray(this.args) ? this.args[0] : this.name()
|
|
399
|
-
|
|
400
|
-
debug(`${this.name()}:onEnd`)(`Command: ${command}. Status: ${status}. Duration: ${duration}ms`)
|
|
401
|
-
|
|
402
|
-
try {
|
|
403
|
-
await track('command', {
|
|
404
|
-
...payload,
|
|
405
|
-
command,
|
|
406
|
-
duration,
|
|
407
|
-
status,
|
|
408
|
-
})
|
|
409
|
-
} catch {}
|
|
410
|
-
|
|
411
|
-
if (error_ !== undefined) {
|
|
412
|
-
error(error_ instanceof Error ? error_ : format(error_), { exit: false })
|
|
413
|
-
exit(1)
|
|
414
345
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
return this.expensivelyAuthenticate()
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
async expensivelyAuthenticate() {
|
|
431
|
-
const webUI = process.env.NETLIFY_WEB_UI || 'https://app.netlify.com'
|
|
432
|
-
log(`Logging into your Netlify account...`)
|
|
433
|
-
|
|
434
|
-
// Create ticket for auth
|
|
435
|
-
// @ts-ignore Types from api are wrong and they don't recognize `createTicket`
|
|
436
|
-
const ticket = await this.netlify.api.createTicket({
|
|
437
|
-
clientId: CLIENT_ID,
|
|
438
|
-
})
|
|
439
|
-
|
|
440
|
-
// Open browser for authentication
|
|
441
|
-
const authLink = `${webUI}/authorize?response_type=ticket&ticket=${ticket.id}`
|
|
442
|
-
|
|
443
|
-
log(`Opening ${authLink}`)
|
|
444
|
-
await openBrowser({ url: authLink })
|
|
445
|
-
|
|
446
|
-
const accessToken = await pollForToken({
|
|
447
|
-
api: this.netlify.api,
|
|
448
|
-
ticket,
|
|
449
|
-
})
|
|
450
|
-
|
|
451
|
-
// @ts-ignore Types from api are wrong and they don't recognize `getCurrentUser`
|
|
452
|
-
const { email, full_name: name, id: userId } = await this.netlify.api.getCurrentUser()
|
|
453
|
-
|
|
454
|
-
const userData = merge(this.netlify.globalConfig.get(`users.${userId}`), {
|
|
455
|
-
id: userId,
|
|
456
|
-
name,
|
|
457
|
-
email,
|
|
458
|
-
auth: {
|
|
459
|
-
token: accessToken,
|
|
460
|
-
github: {
|
|
461
|
-
user: undefined,
|
|
462
|
-
token: undefined,
|
|
463
|
-
},
|
|
464
|
-
},
|
|
465
|
-
})
|
|
466
|
-
// Set current userId
|
|
467
|
-
this.netlify.globalConfig.set('userId', userId)
|
|
468
|
-
// Set user data
|
|
469
|
-
this.netlify.globalConfig.set(`users.${userId}`, userData)
|
|
470
|
-
|
|
471
|
-
await identify({
|
|
472
|
-
name,
|
|
473
|
-
email,
|
|
474
|
-
userId,
|
|
475
|
-
})
|
|
476
|
-
await track('user_login', {
|
|
477
|
-
email,
|
|
478
|
-
})
|
|
479
|
-
|
|
480
|
-
// Log success
|
|
481
|
-
log()
|
|
482
|
-
log(`${chalk.greenBright('You are now logged into your Netlify account!')}`)
|
|
483
|
-
log()
|
|
484
|
-
log(`Run ${chalk.cyanBright('netlify status')} for account details`)
|
|
485
|
-
log()
|
|
486
|
-
log(`To see all available commands run: ${chalk.cyanBright('netlify help')}`)
|
|
487
|
-
log()
|
|
488
|
-
return accessToken
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* Adds some data to the analytics payload
|
|
493
|
-
* @param {Record<string, unknown>} payload
|
|
494
|
-
*/
|
|
495
|
-
setAnalyticsPayload(payload) {
|
|
496
|
-
const newPayload = { ...this.analytics.payload, ...payload }
|
|
497
|
-
this.analytics = { ...this.analytics, payload: newPayload }
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
/**
|
|
501
|
-
* Initializes the options and parses the configuration needs to be called on start of a command function
|
|
502
|
-
* @param {BaseCommand} actionCommand The command of the action that is run (`this.` gets the parent command)
|
|
503
|
-
* @private
|
|
504
|
-
*/
|
|
505
|
-
async init(actionCommand) {
|
|
506
|
-
debug(`${actionCommand.name()}:init`)('start')
|
|
507
|
-
const flags = actionCommand.opts()
|
|
508
|
-
// here we actually want to use the process.cwd as we are setting the workingDir
|
|
509
|
-
// eslint-disable-next-line workspace/no-process-cwd
|
|
510
|
-
this.workingDir = flags.cwd ? resolve(flags.cwd) : process.cwd()
|
|
511
|
-
|
|
512
|
-
// ==================================================
|
|
513
|
-
// Create a Project and run the Heuristics to detect
|
|
514
|
-
// if we are running inside a monorepo or not.
|
|
515
|
-
// ==================================================
|
|
516
|
-
|
|
517
|
-
// retrieve the repository root
|
|
518
|
-
const rootDir = await getRepositoryRoot()
|
|
519
|
-
// Get framework, add to analytics payload for every command, if a framework is set
|
|
520
|
-
const fs = new NodeFS()
|
|
521
|
-
// disable logging inside the project and FS if not in debug mode
|
|
522
|
-
fs.logger = actionCommand.opts()?.debug ? new DefaultLogger('debug') : new NoopLogger()
|
|
523
|
-
this.project = new Project(fs, this.workingDir, rootDir)
|
|
524
|
-
.setEnvironment(process.env)
|
|
525
|
-
.setNodeVersion(process.version)
|
|
526
|
-
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
|
527
|
-
.setReportFn((err, reportConfig) => {
|
|
528
|
-
reportError(err, {
|
|
529
|
-
severity: reportConfig?.severity || 'error',
|
|
530
|
-
metadata: reportConfig?.metadata,
|
|
531
|
-
})
|
|
532
|
-
})
|
|
533
|
-
const frameworks = await this.project.detectFrameworks()
|
|
534
|
-
/** @type { string|undefined} */
|
|
535
|
-
let packageConfig = flags.config ? resolve(flags.config) : undefined
|
|
536
|
-
// check if we have detected multiple projects inside which one we have to perform our operations.
|
|
537
|
-
// only ask to select one if on the workspace root
|
|
538
|
-
if (
|
|
539
|
-
!COMMANDS_WITHOUT_WORKSPACE_OPTIONS.has(actionCommand.name()) &&
|
|
540
|
-
this.project.workspace?.packages.length &&
|
|
541
|
-
this.project.workspace.isRoot
|
|
542
|
-
) {
|
|
543
|
-
this.workspacePackage = await selectWorkspace(this.project, actionCommand.opts().filter)
|
|
544
|
-
this.workingDir = join(this.project.jsWorkspaceRoot, this.workspacePackage)
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
this.jsWorkspaceRoot = this.project.jsWorkspaceRoot
|
|
548
|
-
// detect if a toml exists in this package.
|
|
549
|
-
const tomlFile = join(this.workingDir, 'netlify.toml')
|
|
550
|
-
if (!packageConfig && existsSync(tomlFile)) {
|
|
551
|
-
packageConfig = tomlFile
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
// ==================================================
|
|
555
|
-
// Retrieve Site id and build state from the state.json
|
|
556
|
-
// ==================================================
|
|
557
|
-
const state = new StateConfig(this.workingDir)
|
|
558
|
-
const [token] = await getToken(flags.auth)
|
|
559
|
-
|
|
560
|
-
const apiUrlOpts = {
|
|
561
|
-
userAgent: USER_AGENT,
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
if (process.env.NETLIFY_API_URL) {
|
|
565
|
-
const apiUrl = new URL(process.env.NETLIFY_API_URL)
|
|
566
|
-
apiUrlOpts.scheme = apiUrl.protocol.slice(0, -1)
|
|
567
|
-
apiUrlOpts.host = apiUrl.host
|
|
568
|
-
apiUrlOpts.pathPrefix =
|
|
569
|
-
process.env.NETLIFY_API_URL === `${apiUrl.protocol}//${apiUrl.host}` ? '/api/v1' : apiUrl.pathname
|
|
346
|
+
/**
|
|
347
|
+
*
|
|
348
|
+
* @param {string|undefined} tokenFromFlag
|
|
349
|
+
* @returns
|
|
350
|
+
*/
|
|
351
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'tokenFromFlag' implicitly has an 'any' ... Remove this comment to see the full error message
|
|
352
|
+
async authenticate(tokenFromFlag) {
|
|
353
|
+
const [token] = await getToken(tokenFromFlag);
|
|
354
|
+
if (token) {
|
|
355
|
+
return token;
|
|
356
|
+
}
|
|
357
|
+
return this.expensivelyAuthenticate();
|
|
570
358
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
359
|
+
async expensivelyAuthenticate() {
|
|
360
|
+
const webUI = process.env.NETLIFY_WEB_UI || 'https://app.netlify.com';
|
|
361
|
+
log(`Logging into your Netlify account...`);
|
|
362
|
+
// Create ticket for auth
|
|
363
|
+
const ticket = await this.netlify.api.createTicket({
|
|
364
|
+
clientId: CLIENT_ID,
|
|
365
|
+
});
|
|
366
|
+
// Open browser for authentication
|
|
367
|
+
const authLink = `${webUI}/authorize?response_type=ticket&ticket=${ticket.id}`;
|
|
368
|
+
log(`Opening ${authLink}`);
|
|
369
|
+
// @ts-expect-error TS(2345) FIXME: Argument of type '{ url: string; }' is not assigna... Remove this comment to see the full error message
|
|
370
|
+
await openBrowser({ url: authLink });
|
|
371
|
+
const accessToken = await pollForToken({
|
|
372
|
+
api: this.netlify.api,
|
|
373
|
+
ticket,
|
|
374
|
+
});
|
|
375
|
+
const { email, full_name: name, id: userId } = await this.netlify.api.getCurrentUser();
|
|
376
|
+
const userData = merge(this.netlify.globalConfig.get(`users.${userId}`), {
|
|
377
|
+
id: userId,
|
|
378
|
+
name,
|
|
379
|
+
email,
|
|
380
|
+
auth: {
|
|
381
|
+
token: accessToken,
|
|
382
|
+
github: {
|
|
383
|
+
user: undefined,
|
|
384
|
+
token: undefined,
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
// Set current userId
|
|
389
|
+
this.netlify.globalConfig.set('userId', userId);
|
|
390
|
+
// Set user data
|
|
391
|
+
this.netlify.globalConfig.set(`users.${userId}`, userData);
|
|
392
|
+
await identify({
|
|
393
|
+
name,
|
|
394
|
+
email,
|
|
395
|
+
userId,
|
|
396
|
+
});
|
|
397
|
+
await track('user_login', {
|
|
398
|
+
email,
|
|
399
|
+
});
|
|
400
|
+
// Log success
|
|
401
|
+
log();
|
|
402
|
+
log(`${chalk.greenBright('You are now logged into your Netlify account!')}`);
|
|
403
|
+
log();
|
|
404
|
+
log(`Run ${chalk.cyanBright('netlify status')} for account details`);
|
|
405
|
+
log();
|
|
406
|
+
log(`To see all available commands run: ${chalk.cyanBright('netlify help')}`);
|
|
407
|
+
log();
|
|
408
|
+
return accessToken;
|
|
604
409
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
//
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
410
|
+
/**
|
|
411
|
+
* Adds some data to the analytics payload
|
|
412
|
+
* @param {Record<string, unknown>} payload
|
|
413
|
+
*/
|
|
414
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'payload' implicitly has an 'any' type.
|
|
415
|
+
setAnalyticsPayload(payload) {
|
|
416
|
+
// @ts-expect-error TS(2339) FIXME: Property 'payload' does not exist on type '{ start... Remove this comment to see the full error message
|
|
417
|
+
const newPayload = { ...this.analytics.payload, ...payload };
|
|
418
|
+
// @ts-expect-error TS(2322) FIXME: Type '{ payload: any; startTime: bigint; }' is not... Remove this comment to see the full error message
|
|
419
|
+
this.analytics = { ...this.analytics, payload: newPayload };
|
|
614
420
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
421
|
+
/**
|
|
422
|
+
* Initializes the options and parses the configuration needs to be called on start of a command function
|
|
423
|
+
* @param {BaseCommand} actionCommand The command of the action that is run (`this.` gets the parent command)
|
|
424
|
+
* @private
|
|
425
|
+
*/
|
|
426
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'actionCommand' implicitly has an 'any' ... Remove this comment to see the full error message
|
|
427
|
+
async init(actionCommand) {
|
|
428
|
+
debug(`${actionCommand.name()}:init`)('start');
|
|
429
|
+
const flags = actionCommand.opts();
|
|
430
|
+
// here we actually want to use the process.cwd as we are setting the workingDir
|
|
431
|
+
// eslint-disable-next-line workspace/no-process-cwd
|
|
432
|
+
this.workingDir = flags.cwd ? resolve(flags.cwd) : process.cwd();
|
|
433
|
+
// ==================================================
|
|
434
|
+
// Create a Project and run the Heuristics to detect
|
|
435
|
+
// if we are running inside a monorepo or not.
|
|
436
|
+
// ==================================================
|
|
437
|
+
// retrieve the repository root
|
|
438
|
+
// @ts-expect-error TS(2554) FIXME: Expected 1 arguments, but got 0.
|
|
439
|
+
const rootDir = await getRepositoryRoot();
|
|
440
|
+
// Get framework, add to analytics payload for every command, if a framework is set
|
|
441
|
+
const fs = new NodeFS();
|
|
442
|
+
// disable logging inside the project and FS if not in debug mode
|
|
443
|
+
fs.logger = actionCommand.opts()?.debug ? new DefaultLogger('debug') : new NoopLogger();
|
|
444
|
+
this.project = new Project(fs, this.workingDir, rootDir)
|
|
445
|
+
.setEnvironment(process.env)
|
|
446
|
+
.setNodeVersion(process.version)
|
|
447
|
+
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
|
448
|
+
.setReportFn((err, reportConfig) => {
|
|
449
|
+
reportError(err, {
|
|
450
|
+
severity: reportConfig?.severity || 'error',
|
|
451
|
+
metadata: reportConfig?.metadata,
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
const frameworks = await this.project.detectFrameworks();
|
|
455
|
+
/** @type { string|undefined} */
|
|
456
|
+
let packageConfig = flags.config ? resolve(flags.config) : undefined;
|
|
457
|
+
// check if we have detected multiple projects inside which one we have to perform our operations.
|
|
458
|
+
// only ask to select one if on the workspace root
|
|
459
|
+
if (!COMMANDS_WITHOUT_WORKSPACE_OPTIONS.has(actionCommand.name()) &&
|
|
460
|
+
this.project.workspace?.packages.length &&
|
|
461
|
+
this.project.workspace.isRoot) {
|
|
462
|
+
this.workspacePackage = await selectWorkspace(this.project, actionCommand.opts().filter);
|
|
463
|
+
this.workingDir = join(this.project.jsWorkspaceRoot, this.workspacePackage);
|
|
464
|
+
}
|
|
465
|
+
this.jsWorkspaceRoot = this.project.jsWorkspaceRoot;
|
|
466
|
+
// detect if a toml exists in this package.
|
|
467
|
+
const tomlFile = join(this.workingDir, 'netlify.toml');
|
|
468
|
+
if (!packageConfig && existsSync(tomlFile)) {
|
|
469
|
+
packageConfig = tomlFile;
|
|
470
|
+
}
|
|
471
|
+
// ==================================================
|
|
472
|
+
// Retrieve Site id and build state from the state.json
|
|
473
|
+
// ==================================================
|
|
474
|
+
const state = new StateConfig(this.workingDir);
|
|
475
|
+
const [token] = await getToken(flags.auth);
|
|
476
|
+
const apiUrlOpts = {
|
|
477
|
+
userAgent: USER_AGENT,
|
|
478
|
+
};
|
|
479
|
+
if (process.env.NETLIFY_API_URL) {
|
|
480
|
+
const apiUrl = new URL(process.env.NETLIFY_API_URL);
|
|
481
|
+
// @ts-expect-error TS(2339) FIXME: Property 'scheme' does not exist on type '{ userAg... Remove this comment to see the full error message
|
|
482
|
+
apiUrlOpts.scheme = apiUrl.protocol.slice(0, -1);
|
|
483
|
+
// @ts-expect-error TS(2339) FIXME: Property 'host' does not exist on type '{ userAgen... Remove this comment to see the full error message
|
|
484
|
+
apiUrlOpts.host = apiUrl.host;
|
|
485
|
+
// @ts-expect-error TS(2339) FIXME: Property 'pathPrefix' does not exist on type '{ us... Remove this comment to see the full error message
|
|
486
|
+
apiUrlOpts.pathPrefix =
|
|
487
|
+
process.env.NETLIFY_API_URL === `${apiUrl.protocol}//${apiUrl.host}` ? '/api/v1' : apiUrl.pathname;
|
|
488
|
+
}
|
|
489
|
+
// ==================================================
|
|
490
|
+
// Start retrieving the configuration through the
|
|
491
|
+
// configuration file and the API
|
|
492
|
+
// ==================================================
|
|
493
|
+
const cachedConfig = await actionCommand.getConfig({
|
|
494
|
+
cwd: flags.cwd ? this.workingDir : this.jsWorkspaceRoot || this.workingDir,
|
|
495
|
+
repositoryRoot: rootDir,
|
|
496
|
+
packagePath: this.workspacePackage,
|
|
497
|
+
// The config flag needs to be resolved from the actual process working directory
|
|
498
|
+
configFilePath: packageConfig,
|
|
499
|
+
state,
|
|
500
|
+
token,
|
|
501
|
+
...apiUrlOpts,
|
|
502
|
+
});
|
|
503
|
+
const { buildDir, config, configPath, repositoryRoot, siteInfo } = cachedConfig;
|
|
504
|
+
const normalizedConfig = normalizeConfig(config);
|
|
505
|
+
const agent = await getAgent({
|
|
506
|
+
httpProxy: flags.httpProxy,
|
|
507
|
+
certificateFile: flags.httpProxyCertificateFilename,
|
|
508
|
+
});
|
|
509
|
+
const apiOpts = { ...apiUrlOpts, agent };
|
|
510
|
+
const api = new NetlifyAPI(token || '', apiOpts);
|
|
511
|
+
// If a user passes a site name as an option instead of a site ID to options.site, the siteInfo object
|
|
512
|
+
// will only have the property siteInfo.id. Checking for one of the other properties ensures that we can do
|
|
513
|
+
// a re-call of the api.getSite() that is done in @netlify/config so we have the proper site object in all
|
|
514
|
+
// commands.
|
|
515
|
+
// options.site as a site name (and not just site id) was introduced for the deploy command, so users could
|
|
516
|
+
// deploy by name along with by id
|
|
517
|
+
let siteData = siteInfo;
|
|
518
|
+
if (!siteData.url && flags.site) {
|
|
519
|
+
siteData = await getSiteByName(api, flags.site);
|
|
520
|
+
}
|
|
521
|
+
const globalConfig = await getGlobalConfig();
|
|
522
|
+
// ==================================================
|
|
523
|
+
// Perform analytics reporting
|
|
524
|
+
// ==================================================
|
|
525
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'framework' implicitly has an 'any' type... Remove this comment to see the full error message
|
|
526
|
+
const frameworkIDs = frameworks?.map((framework) => framework.id);
|
|
527
|
+
if (frameworkIDs?.length !== 0) {
|
|
528
|
+
this.setAnalyticsPayload({ frameworks: frameworkIDs });
|
|
529
|
+
}
|
|
530
|
+
this.setAnalyticsPayload({
|
|
531
|
+
monorepo: Boolean(this.project.workspace),
|
|
532
|
+
packageManager: this.project.packageManager?.name,
|
|
533
|
+
// @ts-expect-error TS(7031) FIXME: Binding element 'id' implicitly has an 'any' type.
|
|
534
|
+
buildSystem: this.project.buildSystems.map(({ id }) => id),
|
|
535
|
+
});
|
|
536
|
+
// set the project and the netlify api object on the command,
|
|
537
|
+
// to be accessible inside each command.
|
|
538
|
+
actionCommand.project = this.project;
|
|
539
|
+
actionCommand.workingDir = this.workingDir;
|
|
540
|
+
actionCommand.workspacePackage = this.workspacePackage;
|
|
541
|
+
actionCommand.jsWorkspaceRoot = this.jsWorkspaceRoot;
|
|
542
|
+
// Either an existing configuration file from `@netlify/config` or a file path
|
|
543
|
+
// that should be used for creating it.
|
|
544
|
+
const configFilePath = configPath || join(this.workingDir, 'netlify.toml');
|
|
545
|
+
actionCommand.netlify = {
|
|
546
|
+
// api methods
|
|
547
|
+
api,
|
|
548
|
+
apiOpts,
|
|
549
|
+
// The absolute repository root (detected through @netlify/config)
|
|
550
|
+
repositoryRoot,
|
|
551
|
+
configFilePath,
|
|
552
|
+
relConfigFilePath: relative(repositoryRoot, configFilePath),
|
|
553
|
+
// current site context
|
|
554
|
+
site: {
|
|
555
|
+
root: buildDir,
|
|
556
|
+
configPath,
|
|
557
|
+
get id() {
|
|
558
|
+
return state.get('siteId');
|
|
559
|
+
},
|
|
560
|
+
set id(id) {
|
|
561
|
+
state.set('siteId', id);
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
// Site information retrieved using the API (api.getSite())
|
|
565
|
+
siteInfo: siteData,
|
|
566
|
+
// Configuration from netlify.[toml/yml]
|
|
567
|
+
config: normalizedConfig,
|
|
568
|
+
// Used to avoid calling @netlify/config again
|
|
569
|
+
cachedConfig,
|
|
570
|
+
// global cli config
|
|
571
|
+
globalConfig,
|
|
572
|
+
// state of current site dir
|
|
573
|
+
state,
|
|
574
|
+
};
|
|
575
|
+
debug(`${this.name()}:init`)('end');
|
|
661
576
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
577
|
+
/**
|
|
578
|
+
* Find and resolve the Netlify configuration
|
|
579
|
+
* @param {object} config
|
|
580
|
+
* @param {string} config.cwd
|
|
581
|
+
* @param {string|null=} config.token
|
|
582
|
+
* @param {*} config.state
|
|
583
|
+
* @param {boolean=} config.offline
|
|
584
|
+
* @param {string=} config.configFilePath An optional path to the netlify configuration file e.g. netlify.toml
|
|
585
|
+
* @param {string=} config.packagePath
|
|
586
|
+
* @param {string=} config.repositoryRoot
|
|
587
|
+
* @param {string=} config.host
|
|
588
|
+
* @param {string=} config.pathPrefix
|
|
589
|
+
* @param {string=} config.scheme
|
|
590
|
+
* @returns {ReturnType<typeof resolveConfig>}
|
|
591
|
+
*/
|
|
592
|
+
// @ts-expect-error TS(7023) FIXME: 'getConfig' implicitly has return type 'any' becau... Remove this comment to see the full error message
|
|
593
|
+
async getConfig(config) {
|
|
594
|
+
// the flags that are passed to the command like `--debug` or `--offline`
|
|
595
|
+
const flags = this.opts();
|
|
596
|
+
try {
|
|
597
|
+
return await resolveConfig({
|
|
598
|
+
config: config.configFilePath,
|
|
599
|
+
packagePath: config.packagePath,
|
|
600
|
+
repositoryRoot: config.repositoryRoot,
|
|
601
|
+
cwd: config.cwd,
|
|
602
|
+
context: flags.context || process.env.CONTEXT || this.getDefaultContext(),
|
|
603
|
+
debug: flags.debug,
|
|
604
|
+
siteId: flags.siteId || (typeof flags.site === 'string' && flags.site) || config.state.get('siteId'),
|
|
605
|
+
token: config.token,
|
|
606
|
+
mode: 'cli',
|
|
607
|
+
host: config.host,
|
|
608
|
+
pathPrefix: config.pathPrefix,
|
|
609
|
+
scheme: config.scheme,
|
|
610
|
+
offline: config.offline ?? flags.offline,
|
|
611
|
+
siteFeatureFlagPrefix: 'cli',
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
catch (error_) {
|
|
615
|
+
// @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'.
|
|
616
|
+
const isUserError = error_.customErrorInfo !== undefined && error_.customErrorInfo.type === 'resolveConfig';
|
|
617
|
+
// If we're failing due to an error thrown by us, it might be because the token we're using is invalid.
|
|
618
|
+
// To account for that, we try to retrieve the config again, this time without a token, to avoid making
|
|
619
|
+
// any API calls.
|
|
620
|
+
//
|
|
621
|
+
// @todo Replace this with a mechanism for calling `resolveConfig` with more granularity (i.e. having
|
|
622
|
+
// the option to say that we don't need API data.)
|
|
623
|
+
if (isUserError && !config.offline && config.token) {
|
|
624
|
+
if (flags.debug) {
|
|
625
|
+
// @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message
|
|
626
|
+
error(error_, { exit: false });
|
|
627
|
+
warn('Failed to resolve config, falling back to offline resolution');
|
|
628
|
+
}
|
|
629
|
+
// recursive call with trying to resolve offline
|
|
630
|
+
return this.getConfig({ ...config, offline: true });
|
|
631
|
+
}
|
|
632
|
+
// @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'.
|
|
633
|
+
const message = isUserError ? error_.message : error_.stack;
|
|
634
|
+
error(message, { exit: true });
|
|
714
635
|
}
|
|
715
|
-
// recursive call with trying to resolve offline
|
|
716
|
-
return this.getConfig({ ...config, offline: true })
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
const message = isUserError ? error_.message : error_.stack
|
|
720
|
-
error(message, { exit: true })
|
|
721
636
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
return this.name() === 'serve' ? 'production' : 'dev'
|
|
733
|
-
}
|
|
637
|
+
/**
|
|
638
|
+
* Returns the context that should be used in case one hasn't been explicitly
|
|
639
|
+
* set. The default context is `dev` most of the time, but some commands may
|
|
640
|
+
* wish to override that.
|
|
641
|
+
*
|
|
642
|
+
* @returns {'production' | 'dev'}
|
|
643
|
+
*/
|
|
644
|
+
getDefaultContext() {
|
|
645
|
+
return this.name() === 'serve' ? 'production' : 'dev';
|
|
646
|
+
}
|
|
734
647
|
}
|
|
735
|
-
|
|
736
648
|
/**
|
|
737
649
|
* Retrieves the repository root through a git command.
|
|
738
650
|
* Returns undefined if not a git project.
|
|
739
651
|
* @param {string} [cwd] The optional current working directory
|
|
740
652
|
* @returns {Promise<string|undefined>}
|
|
741
653
|
*/
|
|
654
|
+
// @ts-expect-error TS(7006) FIXME: Parameter 'cwd' implicitly has an 'any' type.
|
|
742
655
|
async function getRepositoryRoot(cwd) {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
656
|
+
const res = await findUp('.git', { cwd, type: 'directory' });
|
|
657
|
+
if (res) {
|
|
658
|
+
return join(res, '..');
|
|
659
|
+
}
|
|
747
660
|
}
|