netlify-cli 15.3.1 → 15.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/npm-shrinkwrap.json +326 -637
- package/package.json +9 -9
- package/src/commands/dev/dev.mjs +27 -5
- package/src/commands/main.mjs +2 -4
- package/src/commands/recipes/recipes.mjs +6 -5
- package/src/functions-templates/javascript/token-hider/package-lock.json +6 -6
- package/src/functions-templates/typescript/hello-world/package-lock.json +6 -6
- package/src/utils/live-tunnel.mjs +30 -4
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
3
|
"description": "Netlify command line tool",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.4.0",
|
|
5
5
|
"author": "Netlify Inc.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -44,13 +44,13 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@bugsnag/js": "7.20.2",
|
|
46
46
|
"@fastify/static": "6.10.2",
|
|
47
|
-
"@netlify/build": "29.
|
|
48
|
-
"@netlify/build-info": "7.0.
|
|
49
|
-
"@netlify/config": "20.4.
|
|
50
|
-
"@netlify/edge-bundler": "8.16.
|
|
51
|
-
"@netlify/framework-info": "9.8.
|
|
47
|
+
"@netlify/build": "29.12.1",
|
|
48
|
+
"@netlify/build-info": "7.0.5",
|
|
49
|
+
"@netlify/config": "20.4.4",
|
|
50
|
+
"@netlify/edge-bundler": "8.16.1",
|
|
51
|
+
"@netlify/framework-info": "9.8.9",
|
|
52
52
|
"@netlify/local-functions-proxy": "1.1.1",
|
|
53
|
-
"@netlify/zip-it-and-ship-it": "9.
|
|
53
|
+
"@netlify/zip-it-and-ship-it": "9.8.0",
|
|
54
54
|
"@octokit/rest": "19.0.11",
|
|
55
55
|
"@skn0tt/lambda-local": "2.0.3",
|
|
56
56
|
"ansi-escapes": "6.2.0",
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
"express": "4.18.2",
|
|
84
84
|
"express-logging": "1.1.1",
|
|
85
85
|
"extract-zip": "2.0.1",
|
|
86
|
+
"fastest-levenshtein": "1.0.16",
|
|
86
87
|
"fastify": "4.17.0",
|
|
87
88
|
"find-up": "6.3.0",
|
|
88
89
|
"flush-write-stream": "2.0.0",
|
|
@@ -90,7 +91,7 @@
|
|
|
90
91
|
"from2-array": "0.0.4",
|
|
91
92
|
"fuzzy": "0.1.3",
|
|
92
93
|
"get-port": "5.1.1",
|
|
93
|
-
"gh-release-fetch": "4.0.
|
|
94
|
+
"gh-release-fetch": "4.0.1",
|
|
94
95
|
"git-repo-info": "2.1.1",
|
|
95
96
|
"gitconfiglocal": "2.1.0",
|
|
96
97
|
"hasbin": "1.2.3",
|
|
@@ -133,7 +134,6 @@
|
|
|
133
134
|
"read-pkg-up": "9.1.0",
|
|
134
135
|
"semver": "7.5.1",
|
|
135
136
|
"source-map-support": "0.5.21",
|
|
136
|
-
"string-similarity": "4.0.4",
|
|
137
137
|
"strip-ansi-control-characters": "2.0.0",
|
|
138
138
|
"tabtab": "3.0.2",
|
|
139
139
|
"tempy": "3.0.0",
|
package/src/commands/dev/dev.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import detectServerSettings, { getConfigWithPlugins } from '../../utils/detect-s
|
|
|
21
21
|
import { getDotEnvVariables, getSiteInformation, injectEnvVariables } from '../../utils/dev.mjs'
|
|
22
22
|
import { getEnvelopeEnv, normalizeContext } from '../../utils/env/index.mjs'
|
|
23
23
|
import { ensureNetlifyIgnore } from '../../utils/gitignore.mjs'
|
|
24
|
-
import { startLiveTunnel } from '../../utils/live-tunnel.mjs'
|
|
24
|
+
import { getLiveTunnelSlug, startLiveTunnel } from '../../utils/live-tunnel.mjs'
|
|
25
25
|
import openBrowser from '../../utils/open-browser.mjs'
|
|
26
26
|
import { generateInspectSettings, startProxyServer } from '../../utils/proxy-server.mjs'
|
|
27
27
|
import { getProxyUrl } from '../../utils/proxy.mjs'
|
|
@@ -37,16 +37,33 @@ import { createDevExecCommand } from './dev-exec.mjs'
|
|
|
37
37
|
* @param {import('commander').OptionValues} config.options
|
|
38
38
|
* @param {*} config.settings
|
|
39
39
|
* @param {*} config.site
|
|
40
|
+
* @param {*} config.state
|
|
40
41
|
* @returns
|
|
41
42
|
*/
|
|
42
|
-
const handleLiveTunnel = async ({ api, options, settings, site }) => {
|
|
43
|
-
|
|
43
|
+
const handleLiveTunnel = async ({ api, options, settings, site, state }) => {
|
|
44
|
+
const { live } = options
|
|
45
|
+
|
|
46
|
+
if (live) {
|
|
47
|
+
const customSlug = typeof live === 'string' && live.length !== 0 ? live : undefined
|
|
48
|
+
const slug = getLiveTunnelSlug(state, customSlug)
|
|
49
|
+
|
|
50
|
+
let message = `${NETLIFYDEVWARN} Creating live URL with ID ${chalk.yellow(slug)}`
|
|
51
|
+
|
|
52
|
+
if (!customSlug) {
|
|
53
|
+
message += ` (to generate a custom URL, use ${chalk.magenta('--live=<subdomain>')})`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
log(message)
|
|
57
|
+
|
|
44
58
|
const sessionUrl = await startLiveTunnel({
|
|
45
59
|
siteId: site.id,
|
|
46
60
|
netlifyApiToken: api.accessToken,
|
|
47
61
|
localPort: settings.port,
|
|
62
|
+
slug,
|
|
48
63
|
})
|
|
64
|
+
|
|
49
65
|
process.env.BASE_URL = sessionUrl
|
|
66
|
+
|
|
50
67
|
return sessionUrl
|
|
51
68
|
}
|
|
52
69
|
}
|
|
@@ -125,8 +142,9 @@ const dev = async (options, command) => {
|
|
|
125
142
|
|
|
126
143
|
command.setAnalyticsPayload({ live: options.live })
|
|
127
144
|
|
|
128
|
-
const liveTunnelUrl = await handleLiveTunnel({ options, site, api, settings })
|
|
145
|
+
const liveTunnelUrl = await handleLiveTunnel({ options, site, api, settings, state })
|
|
129
146
|
const url = liveTunnelUrl || getProxyUrl(settings)
|
|
147
|
+
|
|
130
148
|
process.env.URL = url
|
|
131
149
|
process.env.DEPLOY_URL = url
|
|
132
150
|
|
|
@@ -229,7 +247,11 @@ export const createDevCommand = (program) => {
|
|
|
229
247
|
.option('-d ,--dir <path>', 'dir with static files')
|
|
230
248
|
.option('-f ,--functions <folder>', 'specify a functions folder to serve')
|
|
231
249
|
.option('-o ,--offline', 'disables any features that require network access')
|
|
232
|
-
.option(
|
|
250
|
+
.option(
|
|
251
|
+
'-l, --live [subdomain]',
|
|
252
|
+
'start a public live session; optionally, supply a subdomain to generate a custom URL',
|
|
253
|
+
false,
|
|
254
|
+
)
|
|
233
255
|
.addOption(
|
|
234
256
|
new Option('--functionsPort <port>', 'Old, prefer --functions-port. Port of functions server')
|
|
235
257
|
.argParser((value) => Number.parseInt(value))
|
package/src/commands/main.mjs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import process from 'process'
|
|
3
3
|
|
|
4
4
|
import { Option } from 'commander'
|
|
5
|
+
import { closest } from 'fastest-levenshtein'
|
|
5
6
|
import inquirer from 'inquirer'
|
|
6
|
-
import { findBestMatch } from 'string-similarity'
|
|
7
7
|
|
|
8
8
|
import { BANG, chalk, error, exit, log, NETLIFY_CYAN, USER_AGENT, warn } from '../utils/command-helpers.mjs'
|
|
9
9
|
import execa from '../utils/execa.mjs'
|
|
@@ -118,9 +118,7 @@ const mainCommand = async function (options, command) {
|
|
|
118
118
|
warn(`${chalk.yellow(command.args[0])} is not a ${command.name()} command.`)
|
|
119
119
|
|
|
120
120
|
const allCommands = command.commands.map((cmd) => cmd.name())
|
|
121
|
-
const
|
|
122
|
-
bestMatch: { target: suggestion },
|
|
123
|
-
} = findBestMatch(command.args[0], allCommands)
|
|
121
|
+
const suggestion = closest(command.args[0], allCommands)
|
|
124
122
|
|
|
125
123
|
const applySuggestion = await new Promise((resolve) => {
|
|
126
124
|
const prompt = inquirer.prompt({
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import { basename } from 'path'
|
|
3
3
|
|
|
4
|
+
import { closest } from 'fastest-levenshtein'
|
|
4
5
|
import inquirer from 'inquirer'
|
|
5
|
-
import { findBestMatch } from 'string-similarity'
|
|
6
6
|
|
|
7
7
|
import { NETLIFYDEVERR, chalk, log } from '../../utils/command-helpers.mjs'
|
|
8
8
|
|
|
@@ -28,7 +28,10 @@ const recipesCommand = async (recipeName, options, command) => {
|
|
|
28
28
|
try {
|
|
29
29
|
return await runRecipe({ config, recipeName: sanitizedRecipeName, repositoryRoot })
|
|
30
30
|
} catch (error) {
|
|
31
|
-
if (
|
|
31
|
+
if (
|
|
32
|
+
// The ESM loader throws this instead of MODULE_NOT_FOUND
|
|
33
|
+
error.code !== 'ERR_MODULE_NOT_FOUND'
|
|
34
|
+
) {
|
|
32
35
|
throw error
|
|
33
36
|
}
|
|
34
37
|
|
|
@@ -36,9 +39,7 @@ const recipesCommand = async (recipeName, options, command) => {
|
|
|
36
39
|
|
|
37
40
|
const recipes = await listRecipes()
|
|
38
41
|
const recipeNames = recipes.map(({ name }) => name)
|
|
39
|
-
const
|
|
40
|
-
bestMatch: { target: suggestion },
|
|
41
|
-
} = findBestMatch(recipeName, recipeNames)
|
|
42
|
+
const suggestion = closest(recipeName, recipeNames)
|
|
42
43
|
const applySuggestion = await new Promise((resolve) => {
|
|
43
44
|
const prompt = inquirer.prompt({
|
|
44
45
|
type: 'confirm',
|
|
@@ -164,9 +164,9 @@
|
|
|
164
164
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
|
165
165
|
},
|
|
166
166
|
"node_modules/qs": {
|
|
167
|
-
"version": "6.11.
|
|
168
|
-
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.
|
|
169
|
-
"integrity": "sha512-
|
|
167
|
+
"version": "6.11.2",
|
|
168
|
+
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
|
|
169
|
+
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
|
|
170
170
|
"dependencies": {
|
|
171
171
|
"side-channel": "^1.0.4"
|
|
172
172
|
},
|
|
@@ -296,9 +296,9 @@
|
|
|
296
296
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
|
297
297
|
},
|
|
298
298
|
"qs": {
|
|
299
|
-
"version": "6.11.
|
|
300
|
-
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.
|
|
301
|
-
"integrity": "sha512-
|
|
299
|
+
"version": "6.11.2",
|
|
300
|
+
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
|
|
301
|
+
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
|
|
302
302
|
"requires": {
|
|
303
303
|
"side-channel": "^1.0.4"
|
|
304
304
|
}
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"node_modules/@types/node": {
|
|
29
|
-
"version": "14.18.
|
|
30
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.
|
|
31
|
-
"integrity": "sha512-
|
|
29
|
+
"version": "14.18.48",
|
|
30
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.48.tgz",
|
|
31
|
+
"integrity": "sha512-iL0PIMwejpmuVHgfibHpfDwOdsbmB50wr21X71VnF5d7SsBF7WK+ZvP/SCcFm7Iwb9iiYSap9rlrdhToNAWdxg=="
|
|
32
32
|
},
|
|
33
33
|
"node_modules/is-promise": {
|
|
34
34
|
"version": "4.0.0",
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"@types/node": {
|
|
61
|
-
"version": "14.18.
|
|
62
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.
|
|
63
|
-
"integrity": "sha512-
|
|
61
|
+
"version": "14.18.48",
|
|
62
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.48.tgz",
|
|
63
|
+
"integrity": "sha512-iL0PIMwejpmuVHgfibHpfDwOdsbmB50wr21X71VnF5d7SsBF7WK+ZvP/SCcFm7Iwb9iiYSap9rlrdhToNAWdxg=="
|
|
64
64
|
},
|
|
65
65
|
"is-promise": {
|
|
66
66
|
"version": "4.0.0",
|
|
@@ -3,6 +3,7 @@ import process from 'process'
|
|
|
3
3
|
|
|
4
4
|
import fetch from 'node-fetch'
|
|
5
5
|
import pWaitFor from 'p-wait-for'
|
|
6
|
+
import { v4 as uuidv4 } from 'uuid'
|
|
6
7
|
|
|
7
8
|
import { fetchLatestVersion, shouldFetchLatestVersion } from '../lib/exec-fetcher.mjs'
|
|
8
9
|
import { getPathInHome } from '../lib/settings.mjs'
|
|
@@ -12,13 +13,14 @@ import execa from './execa.mjs'
|
|
|
12
13
|
|
|
13
14
|
const PACKAGE_NAME = 'live-tunnel-client'
|
|
14
15
|
const EXEC_NAME = PACKAGE_NAME
|
|
16
|
+
const SLUG_LOCAL_STATE_KEY = 'liveTunnelSlug'
|
|
15
17
|
|
|
16
18
|
// 1 second
|
|
17
19
|
const TUNNEL_POLL_INTERVAL = 1e3
|
|
18
20
|
// 5 minutes
|
|
19
21
|
const TUNNEL_POLL_TIMEOUT = 3e5
|
|
20
22
|
|
|
21
|
-
const createTunnel = async function ({ netlifyApiToken, siteId }) {
|
|
23
|
+
const createTunnel = async function ({ netlifyApiToken, siteId, slug }) {
|
|
22
24
|
await installTunnelClient()
|
|
23
25
|
|
|
24
26
|
if (!siteId) {
|
|
@@ -29,9 +31,8 @@ const createTunnel = async function ({ netlifyApiToken, siteId }) {
|
|
|
29
31
|
)
|
|
30
32
|
process.exit(1)
|
|
31
33
|
}
|
|
32
|
-
log(`${NETLIFYDEVLOG} Creating Live Tunnel for ${siteId}`)
|
|
33
|
-
const url = `https://api.netlify.com/api/v1/live_sessions?site_id=${siteId}`
|
|
34
34
|
|
|
35
|
+
const url = `https://api.netlify.com/api/v1/live_sessions?site_id=${siteId}&slug=${slug}`
|
|
35
36
|
const response = await fetch(url, {
|
|
36
37
|
method: 'POST',
|
|
37
38
|
headers: {
|
|
@@ -87,10 +88,11 @@ const installTunnelClient = async function () {
|
|
|
87
88
|
})
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
export const startLiveTunnel = async ({ localPort, netlifyApiToken, siteId }) => {
|
|
91
|
+
export const startLiveTunnel = async ({ localPort, netlifyApiToken, siteId, slug }) => {
|
|
91
92
|
const session = await createTunnel({
|
|
92
93
|
siteId,
|
|
93
94
|
netlifyApiToken,
|
|
95
|
+
slug,
|
|
94
96
|
})
|
|
95
97
|
|
|
96
98
|
const isLiveTunnelReady = async function () {
|
|
@@ -121,3 +123,27 @@ export const startLiveTunnel = async ({ localPort, netlifyApiToken, siteId }) =>
|
|
|
121
123
|
|
|
122
124
|
return session.session_url
|
|
123
125
|
}
|
|
126
|
+
|
|
127
|
+
export const getLiveTunnelSlug = (state, override) => {
|
|
128
|
+
if (override !== undefined) {
|
|
129
|
+
return override
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const newSlug = generateRandomSlug()
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const existingSlug = state.get(SLUG_LOCAL_STATE_KEY)
|
|
136
|
+
|
|
137
|
+
if (existingSlug !== undefined) {
|
|
138
|
+
return existingSlug
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
state.set(SLUG_LOCAL_STATE_KEY, newSlug)
|
|
142
|
+
} catch (error) {
|
|
143
|
+
log(`${NETLIFYDEVERR} Could not read or write local state file: ${error.message}`)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return newSlug
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const generateRandomSlug = () => uuidv4().slice(0, 8)
|