|
1 | | -const { flags } = require('@oclif/command') |
2 | | -const execa = require('execa') |
3 | | -const http = require('http') |
4 | | -const httpProxy = require('http-proxy') |
5 | | -const waitPort = require('wait-port') |
6 | | -const getPort = require('get-port') |
7 | | -const { serveFunctions } = require('@netlify/zip-it-and-ship-it') |
8 | | -const { serverSettings } = require('../../detect-server') |
9 | | -const Command = require('@netlify/cli-utils') |
10 | | -const { getAddons } = require('netlify/src/addons') |
11 | | -const { track } = require('@netlify/cli-utils/src/utils/telemetry') |
12 | | -const chalk = require('chalk') |
13 | | -const boxen = require('boxen') |
14 | | -const { createTunnel, connectTunnel } = require('../../live-tunnel') |
| 1 | +const { flags } = require("@oclif/command"); |
| 2 | +const execa = require("execa"); |
| 3 | +const http = require("http"); |
| 4 | +const httpProxy = require("http-proxy"); |
| 5 | +const waitPort = require("wait-port"); |
| 6 | +const getPort = require("get-port"); |
| 7 | +const { serveFunctions } = require("@netlify/zip-it-and-ship-it"); |
| 8 | +const { serverSettings } = require("../../detect-server"); |
| 9 | +const Command = require("@netlify/cli-utils"); |
| 10 | +const { getAddons } = require("netlify/src/addons"); |
| 11 | +const { track } = require("@netlify/cli-utils/src/utils/telemetry"); |
| 12 | +const chalk = require("chalk"); |
| 13 | +const boxen = require("boxen"); |
| 14 | +const { createTunnel, connectTunnel } = require("../../live-tunnel"); |
15 | 15 |
|
16 | 16 | function isFunction(settings, req) { |
17 | | - return settings.functionsPort && req.url.match(/^\/.netlify\/functions\/.+/) |
| 17 | + return settings.functionsPort && req.url.match(/^\/.netlify\/functions\/.+/); |
18 | 18 | } |
19 | 19 |
|
20 | 20 | function addonUrl(addonUrls, req) { |
21 | | - const m = req.url.match(/^\/.netlify\/([^\/]+)(\/.*)/) |
22 | | - const addonUrl = m && addonUrls[m[1]] |
23 | | - return addonUrl ? `${addonUrl}${m[2]}` : null |
| 21 | + const m = req.url.match(/^\/.netlify\/([^\/]+)(\/.*)/); |
| 22 | + const addonUrl = m && addonUrls[m[1]]; |
| 23 | + return addonUrl ? `${addonUrl}${m[2]}` : null; |
24 | 24 | } |
25 | 25 |
|
26 | 26 | async function startProxy(settings, addonUrls) { |
27 | | - const rulesProxy = require('@netlify/rules-proxy') |
| 27 | + const rulesProxy = require("@netlify/rules-proxy"); |
28 | 28 |
|
29 | | - await waitPort({ port: settings.proxyPort }) |
| 29 | + await waitPort({ port: settings.proxyPort }); |
30 | 30 | if (settings.functionsPort) { |
31 | | - await waitPort({ port: settings.functionsPort }) |
| 31 | + await waitPort({ port: settings.functionsPort }); |
32 | 32 | } |
33 | | - const port = await getPort({ port: settings.port }) |
34 | | - const functionsServer = settings.functionsPort ? `http://localhost:${settings.functionsPort}` : null |
| 33 | + const port = await getPort({ port: settings.port }); |
| 34 | + const functionsServer = settings.functionsPort |
| 35 | + ? `http://localhost:${settings.functionsPort}` |
| 36 | + : null; |
35 | 37 |
|
36 | 38 | const proxy = httpProxy.createProxyServer({ |
37 | 39 | target: { |
38 | | - host: 'localhost', |
| 40 | + host: "localhost", |
39 | 41 | port: settings.proxyPort |
40 | 42 | } |
41 | | - }) |
| 43 | + }); |
42 | 44 |
|
43 | | - const rewriter = rulesProxy({ publicFolder: settings.dist }) |
| 45 | + const rewriter = rulesProxy({ publicFolder: settings.dist }); |
44 | 46 |
|
45 | 47 | const server = http.createServer(function(req, res) { |
46 | 48 | if (isFunction(settings, req)) { |
47 | | - return proxy.web(req, res, { target: functionsServer }) |
| 49 | + return proxy.web(req, res, { target: functionsServer }); |
48 | 50 | } |
49 | | - let url = addonUrl(addonUrls, req) |
| 51 | + let url = addonUrl(addonUrls, req); |
50 | 52 | if (url) { |
51 | | - return proxy.web(req, res, { target: url }) |
| 53 | + return proxy.web(req, res, { target: url }); |
52 | 54 | } |
53 | 55 |
|
54 | 56 | rewriter(req, res, () => { |
55 | 57 | if (isFunction(settings, req)) { |
56 | | - return proxy.web(req, res, { target: functionsServer }) |
| 58 | + return proxy.web(req, res, { target: functionsServer }); |
57 | 59 | } |
58 | | - url = addonUrl(addonUrls, req) |
| 60 | + url = addonUrl(addonUrls, req); |
59 | 61 | if (url) { |
60 | | - return proxy.web(req, res, { target: url }) |
| 62 | + return proxy.web(req, res, { target: url }); |
61 | 63 | } |
62 | 64 |
|
63 | | - proxy.web(req, res, { target: `http://localhost:${settings.proxyPort}` }) |
64 | | - }) |
65 | | - }) |
| 65 | + proxy.web(req, res, { target: `http://localhost:${settings.proxyPort}` }); |
| 66 | + }); |
| 67 | + }); |
66 | 68 |
|
67 | | - server.on('upgrade', function(req, socket, head) { |
68 | | - proxy.ws(req, socket, head) |
69 | | - }) |
| 69 | + server.on("upgrade", function(req, socket, head) { |
| 70 | + proxy.ws(req, socket, head); |
| 71 | + }); |
70 | 72 |
|
71 | | - server.listen(port) |
72 | | - return `http://localhost:${port}` |
| 73 | + server.listen(port); |
| 74 | + return `http://localhost:${port}`; |
73 | 75 | } |
74 | 76 |
|
75 | 77 | function startDevServer(settings, log, error) { |
76 | 78 | if (settings.noCmd) { |
77 | | - const StaticServer = require('static-server') |
| 79 | + const StaticServer = require("static-server"); |
78 | 80 | if (!settings.dist) { |
79 | 81 | log( |
80 | | - 'Unable to determine public folder for the dev server.\nSetup a netlify.toml file with a [dev] section to specify your dev server settings.' |
81 | | - ) |
82 | | - process.exit(1) |
| 82 | + "Unable to determine public folder for the dev server.\nSetup a netlify.toml file with a [dev] section to specify your dev server settings." |
| 83 | + ); |
| 84 | + process.exit(1); |
83 | 85 | } |
84 | 86 |
|
85 | 87 | const server = new StaticServer({ |
86 | 88 | rootPath: settings.dist, |
87 | | - name: 'netlify-dev', |
| 89 | + name: "netlify-dev", |
88 | 90 | port: settings.proxyPort, |
89 | 91 | templates: { |
90 | | - notFound: '404.html' |
| 92 | + notFound: "404.html" |
91 | 93 | } |
92 | | - }) |
| 94 | + }); |
93 | 95 |
|
94 | 96 | server.start(function() { |
95 | | - log('Server listening to', settings.proxyPort) |
96 | | - }) |
97 | | - return |
| 97 | + log("Server listening to", settings.proxyPort); |
| 98 | + }); |
| 99 | + return; |
98 | 100 | } else { |
99 | | - log(`Starting netlify dev with ${settings.type}`) |
100 | | - const ps = execa(settings.command, settings.args, { env: settings.env, stdio: 'inherit', shell: true }) |
101 | | - ps.on('close', code => process.exit(code)) |
102 | | - ps.on('SIGINT', process.exit) |
103 | | - ps.on('SIGTERM', process.exit) |
| 101 | + log(`Starting netlify dev with ${settings.type}`); |
| 102 | + const ps = execa(settings.command, settings.args, { |
| 103 | + env: settings.env, |
| 104 | + stdio: "inherit", |
| 105 | + shell: true |
| 106 | + }); |
| 107 | + ps.on("close", code => process.exit(code)); |
| 108 | + ps.on("SIGINT", process.exit); |
| 109 | + ps.on("SIGTERM", process.exit); |
104 | 110 | } |
105 | 111 | } |
106 | 112 |
|
107 | 113 | class DevCommand extends Command { |
108 | 114 | async run() { |
109 | | - const { flags, args } = this.parse(DevCommand) |
110 | | - const { api, site, config } = this.netlify |
| 115 | + const { flags, args } = this.parse(DevCommand); |
| 116 | + const { api, site, config } = this.netlify; |
111 | 117 | const functionsDir = |
112 | | - flags.functions || (config.dev && config.dev.functions) || (config.build && config.build.functions) |
113 | | - const addonUrls = {} |
| 118 | + flags.functions || |
| 119 | + (config.dev && config.dev.functions) || |
| 120 | + (config.build && config.build.functions); |
| 121 | + const addonUrls = {}; |
114 | 122 |
|
115 | | - let accessToken = api.accessToken |
| 123 | + let accessToken = api.accessToken; |
116 | 124 | if (site.id && !flags.offline) { |
117 | | - accessToken = await this.authenticate() |
118 | | - const addons = await getAddons(site.id, accessToken) |
| 125 | + accessToken = await this.authenticate(); |
| 126 | + const addons = await getAddons(site.id, accessToken); |
119 | 127 | if (Array.isArray(addons)) { |
120 | 128 | addons.forEach(addon => { |
121 | | - addonUrls[addon.slug] = `${addon.config.site_url}/.netlify/${addon.slug}` |
| 129 | + addonUrls[addon.slug] = `${addon.config.site_url}/.netlify/${ |
| 130 | + addon.slug |
| 131 | + }`; |
122 | 132 | for (const key in addon.env) { |
123 | | - process.env[key] = process.env[key] || addon.env[key] |
| 133 | + process.env[key] = process.env[key] || addon.env[key]; |
124 | 134 | } |
125 | | - }) |
| 135 | + }); |
126 | 136 | } |
127 | | - const api = this.netlify.api |
128 | | - const apiSite = await api.getSite({ site_id: site.id }) |
| 137 | + const api = this.netlify.api; |
| 138 | + const apiSite = await api.getSite({ site_id: site.id }); |
129 | 139 | // TODO: We should move the environment outside of build settings and possibly have a |
130 | 140 | // `/api/v1/sites/:site_id/environment` endpoint for it that we can also gate access to |
131 | 141 | // In the future and that we could make context dependend |
132 | 142 | if (apiSite.build_settings && apiSite.build_settings.env) { |
133 | 143 | for (const key in apiSite.build_settings.env) { |
134 | | - process.env[key] = process.env[key] || apiSite.build_settings.env[key] |
| 144 | + process.env[key] = |
| 145 | + process.env[key] || apiSite.build_settings.env[key]; |
135 | 146 | } |
136 | 147 | } |
137 | 148 | } |
138 | | - process.env.NETLIFY_DEV = 'true' |
| 149 | + process.env.NETLIFY_DEV = "true"; |
139 | 150 |
|
140 | | - let settings = serverSettings(config.dev) |
| 151 | + let settings = serverSettings(config.dev); |
141 | 152 | if (!(settings && settings.command)) { |
142 | | - this.log('No dev server detected, using simple static server') |
143 | | - const dist = (config.dev && config.dev.publish) || (config.build && config.build.publish) |
| 153 | + this.log("No dev server detected, using simple static server"); |
| 154 | + const dist = |
| 155 | + (config.dev && config.dev.publish) || |
| 156 | + (config.build && config.build.publish); |
144 | 157 | settings = { |
145 | 158 | noCmd: true, |
146 | 159 | port: 8888, |
147 | 160 | proxyPort: 3999, |
148 | 161 | dist |
149 | | - } |
| 162 | + }; |
150 | 163 | } |
151 | 164 |
|
152 | 165 | let url; |
153 | 166 | if (flags.live) { |
154 | | - const liveSession = await createTunnel(site.id, accessToken, this.log) |
155 | | - url = liveSession.session_url |
156 | | - process.env.BASE_URL = url |
157 | | - |
158 | | - await connectTunnel(liveSession, accessToken, settings.port, this.log, this.error) |
| 167 | + const liveSession = await createTunnel(site.id, accessToken, this.log); |
| 168 | + url = liveSession.session_url; |
| 169 | + process.env.BASE_URL = url; |
| 170 | + |
| 171 | + await connectTunnel( |
| 172 | + liveSession, |
| 173 | + accessToken, |
| 174 | + settings.port, |
| 175 | + this.log, |
| 176 | + this.error |
| 177 | + ); |
159 | 178 | } |
160 | 179 |
|
161 | | - startDevServer(settings, this.log, this.error) |
| 180 | + startDevServer(settings, this.log, this.error); |
162 | 181 |
|
163 | 182 | if (functionsDir) { |
164 | | - const fnSettings = await serveFunctions({ ...settings, functionsDir }) |
165 | | - settings.functionsPort = fnSettings.port |
| 183 | + const fnSettings = await serveFunctions({ ...settings, functionsDir }); |
| 184 | + settings.functionsPort = fnSettings.port; |
166 | 185 | } |
167 | 186 |
|
168 | | - const proxyUrl = await startProxy(settings, addonUrls) |
| 187 | + const proxyUrl = await startProxy(settings, addonUrls); |
169 | 188 | if (!url) { |
170 | | - url = proxyUrl |
| 189 | + url = proxyUrl; |
171 | 190 | } |
172 | 191 | // Todo hoist this telemetry `command` to CLI hook |
173 | | - track('command', { |
174 | | - command: 'dev', |
175 | | - projectType: settings.type || 'custom' |
176 | | - }) |
177 | | - |
178 | | - const banner = chalk.bold(`Netlify dev server is now ready on ${url}`) |
179 | | - this.log(boxen(banner, { padding: 1, margin: 1, align: 'center', borderColor: '#00c7b7' })) |
| 192 | + track("command", { |
| 193 | + command: "dev", |
| 194 | + projectType: settings.type || "custom" |
| 195 | + }); |
| 196 | + |
| 197 | + const banner = chalk.bold(`Netlify dev server is now ready on ${url}`); |
| 198 | + this.log( |
| 199 | + boxen(banner, { |
| 200 | + padding: 1, |
| 201 | + margin: 1, |
| 202 | + align: "center", |
| 203 | + borderColor: "#00c7b7" |
| 204 | + }) |
| 205 | + ); |
180 | 206 | } |
181 | 207 | } |
182 | 208 |
|
183 | 209 | DevCommand.description = `Local dev server |
184 | 210 | The dev command will run a local dev server with Netlify's proxy and redirect rules |
185 | | -` |
| 211 | +`; |
186 | 212 |
|
187 | | -DevCommand.examples = ['$ netlify dev', '$ netlify dev -c "yarn start"', '$ netlify dev -c hugo'] |
| 213 | +DevCommand.examples = [ |
| 214 | + "$ netlify dev", |
| 215 | + '$ netlify dev -c "yarn start"', |
| 216 | + "$ netlify dev -c hugo" |
| 217 | +]; |
188 | 218 |
|
189 | | -DevCommand.strict = false |
| 219 | +DevCommand.strict = false; |
190 | 220 |
|
191 | 221 | DevCommand.flags = { |
192 | | - cmd: flags.string({ char: 'c', description: 'command to run' }), |
| 222 | + cmd: flags.string({ char: "c", description: "command to run" }), |
193 | 223 | devport: flags.integer({ |
194 | | - char: 'd', |
195 | | - description: 'port of the dev server started by command' |
| 224 | + char: "d", |
| 225 | + description: "port of the dev server started by command" |
196 | 226 | }), |
197 | | - port: flags.integer({ char: 'p', description: 'port of netlify dev' }), |
198 | | - dir: flags.integer({ char: 'd', description: 'dir with static files' }), |
199 | | - functions: flags.string({ char: 'f', description: 'Specify a functions folder to serve' }), |
200 | | - offline: flags.boolean({ char: 'o', description: 'disables any features that require network access' }), |
201 | | - live: flags.boolean({char: 'l', description: 'Start a public live session'}) |
202 | | -} |
| 227 | + port: flags.integer({ char: "p", description: "port of netlify dev" }), |
| 228 | + dir: flags.integer({ char: "d", description: "dir with static files" }), |
| 229 | + functions: flags.string({ |
| 230 | + char: "f", |
| 231 | + description: "Specify a functions folder to serve" |
| 232 | + }), |
| 233 | + offline: flags.boolean({ |
| 234 | + char: "o", |
| 235 | + description: "disables any features that require network access" |
| 236 | + }), |
| 237 | + live: flags.boolean({ char: "l", description: "Start a public live session" }) |
| 238 | +}; |
203 | 239 |
|
204 | | -module.exports = DevCommand |
| 240 | +module.exports = DevCommand; |
0 commit comments