netlify-cli 15.3.2 → 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 +2 -2
- package/package.json +1 -1
- package/src/commands/dev/dev.mjs +27 -5
- package/src/utils/live-tunnel.mjs +30 -4
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
|
-
"version": "15.
|
|
3
|
+
"version": "15.4.0",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "netlify-cli",
|
|
9
|
-
"version": "15.
|
|
9
|
+
"version": "15.4.0",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
package/package.json
CHANGED
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))
|
|
@@ -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)
|