diff --git a/packages/gatsby-plugin-netlify-cms/src/gatsby-node.js b/packages/gatsby-plugin-netlify-cms/src/gatsby-node.js index 16f7c5300ab13..37afc0412ed92 100644 --- a/packages/gatsby-plugin-netlify-cms/src/gatsby-node.js +++ b/packages/gatsby-plugin-netlify-cms/src/gatsby-node.js @@ -173,7 +173,7 @@ exports.onCreateWebpackConfig = ( messages: [ `Netlify CMS is running at ${ program.https ? `https://` : `http://` - }${program.host}:${program.proxyPort}/${publicPathClean}/`, + }${program.host}:${program.port}/${publicPathClean}/`, ], }, }), diff --git a/packages/gatsby/src/commands/develop.ts b/packages/gatsby/src/commands/develop.ts index 77dac27e248bb..047ed48832943 100644 --- a/packages/gatsby/src/commands/develop.ts +++ b/packages/gatsby/src/commands/develop.ts @@ -1,28 +1,15 @@ // NOTE(@mxstbr): Do not use the reporter in this file, as that has side-effects on import which break structured logging import path from "path" -import http from "http" -import https from "https" import tmp from "tmp" import { ChildProcess } from "child_process" import execa from "execa" -import chokidar from "chokidar" -import getRandomPort from "detect-port" import { detectPortInUseAndPrompt } from "../utils/detect-port-in-use-and-prompt" -import { Server as SocketIO } from "socket.io" import fs from "fs-extra" import onExit from "signal-exit" -import { - isCI, - slash, - createServiceLock, - getService, - updateInternalSiteMetadata, - UnlockFn, - uuid, -} from "gatsby-core-utils" +import { v4 } from "gatsby-core-utils/uuid" +import { slash } from "gatsby-core-utils/path" import reporter from "gatsby-cli/lib/reporter" import { getSslCert } from "../utils/get-ssl-cert" -import { IProxyControls, startDevelopProxy } from "../utils/develop-proxy" import { IProgram, IDebugInfo } from "./types" import { flush as telemetryFlush } from "gatsby-telemetry" @@ -41,34 +28,6 @@ const requireUncached = (file: string): any => { } } -// Heuristics for gatsby-config.js, as not all changes to it require a full restart to take effect -const doesConfigChangeRequireRestart = ( - lastConfig: Record, - newConfig: Record -): boolean => { - // Ignore changes to siteMetadata - const replacer = (_, v): string | void => { - if (typeof v === `function` || v instanceof RegExp) { - return v.toString() - } else { - return v - } - } - - const oldConfigString = JSON.stringify( - { ...lastConfig, siteMetadata: null }, - replacer - ) - const newConfigString = JSON.stringify( - { ...newConfig, siteMetadata: null }, - replacer - ) - - if (oldConfigString === newConfigString) return false - - return true -} - // Return a user-supplied port otherwise the default Node.js debugging port const getDebugPort = (port?: number): number => port ?? 9229 @@ -200,7 +159,7 @@ const REGEX_IP = module.exports = async (program: IProgram): Promise => { global.__GATSBY = { - buildId: uuid.v4(), + buildId: v4(), root: program.directory, } @@ -208,9 +167,6 @@ module.exports = async (program: IProgram): Promise => { // So we want to early just force it to a number to ensure we always act on a correct type. program.port = parseInt(program.port + ``, 10) const developProcessPath = slash(require.resolve(`./develop-process`)) - const telemetryServerPath = slash( - require.resolve(`../utils/telemetry-server`) - ) try { program.port = await detectPortInUseAndPrompt(program.port) @@ -224,27 +180,15 @@ module.exports = async (program: IProgram): Promise => { // Run the actual develop server on a random port, and the proxy on the program port // which users will access - const proxyPort = program.port const debugInfo = getDebugInfo(program) const rootFile = (file: string): string => path.join(program.directory, file) // Require gatsby-config.js before accessing process.env, to enable the user to change // environment variables from the config file. - let lastConfig = requireUncached(rootFile(`gatsby-config`)) - - // INTERNAL_STATUS_PORT allows for setting the websocket port used for monitoring - // when the browser should prompt the user to restart the develop process. - // This port is randomized by default and in most cases should never be required to configure. - // It is exposed for environments where port access needs to be explicit, such as with Docker. - // As the port is meant for internal usage only, any attempt to interface with features - // it exposes via third-party software is not supported. - const [statusServerPort, developPort, telemetryServerPort] = - await Promise.all([ - getRandomPort(process.env.INTERNAL_STATUS_PORT), - getRandomPort(), - getRandomPort(), - ]) + requireUncached(rootFile(`gatsby-config`)) + + const developPort = program.port // In order to enable custom ssl, --cert-file --key-file and -https flags must all be // used together @@ -284,21 +228,12 @@ module.exports = async (program: IProgram): Promise => { } } - // NOTE(@mxstbr): We need to start the develop proxy before the develop process to ensure - // codesandbox detects the right port to expose by default - const proxy = startDevelopProxy({ - proxyPort: proxyPort, - targetPort: developPort, - program, - }) - const developProcess = new ControllableScript( ` const cmd = require(${JSON.stringify(developProcessPath)}); const args = ${JSON.stringify({ ...program, port: developPort, - proxyPort, // Don't pass SSL options down to the develop process, it should always use HTTP ssl: null, debugInfo, @@ -308,106 +243,17 @@ module.exports = async (program: IProgram): Promise => { debugInfo ) - const telemetryServerProcess = new ControllableScript( - `require(${JSON.stringify(telemetryServerPath)}).default(${JSON.stringify( - telemetryServerPort - )})`, - null - ) - - let unlocks: Array = [] - if (!isCI()) { - const statusUnlock = await createServiceLock( - program.directory, - `developstatusserver`, - { - port: statusServerPort, - } - ) - const developUnlock = await createServiceLock( - program.directory, - `developproxy`, - { - port: proxyPort, - } - ) - const telemetryUnlock = await createServiceLock( - program.directory, - `telemetryserver`, - { - port: telemetryServerPort, - } - ) - await updateInternalSiteMetadata({ - name: program.sitePackageJson.name, - sitePath: program.directory, - pid: process.pid, - lastRun: Date.now(), - }) - - if (!statusUnlock || !developUnlock) { - const data = await getService(program.directory, `developproxy`) - const port = data?.port || 8000 - console.error( - `Looks like develop for this site is already running, can you visit ${ - program.ssl ? `https:` : `http:` - }//localhost:${port} ? If it is not, try again in five seconds!` - ) - process.exit(1) - } - - unlocks = unlocks.concat([statusUnlock, developUnlock, telemetryUnlock]) - } - - const statusServer = program.ssl - ? https.createServer(program.ssl) - : http.createServer() - statusServer.listen(statusServerPort) - - const io = new SocketIO(statusServer, { - // whitelist all (https://github.com/expressjs/cors#configuration-options) - cors: { - origin: true, - }, - cookie: true, - }) - const handleChildProcessIPC = (msg): void => { if (msg.type === `HEARTBEAT`) return if (process.send) { // Forward IPC process.send(msg) } - - io.emit(`structured-log`, msg) - - if ( - msg.type === `LOG_ACTION` && - msg.action.type === `SET_STATUS` && - msg.action.payload === `SUCCESS` - ) { - proxy.serveSite() - } } - io.on(`connection`, socket => { - socket.on(`develop:restart`, async respond => { - isRestarting = true - proxy.serveRestartingScreen() - // respond() responds to the client, which in our case prompts it to reload the page to show the restarting screen - if (respond) respond(`develop:is-starting`) - await developProcess.stop() - developProcess.start() - developProcess.onMessage(handleChildProcessIPC) - isRestarting = false - }) - }) - developProcess.start() developProcess.onMessage(handleChildProcessIPC) - telemetryServerProcess.start() - // Plugins can call `process.exit` which would be sent to `develop-process` (child process) // This needs to be propagated back to the parent process developProcess.onExit( @@ -440,58 +286,10 @@ module.exports = async (program: IProgram): Promise => { } ) - const files = [ - rootFile(`gatsby-config.js`), - rootFile(`gatsby-node.js`), - rootFile(`gatsby-config.ts`), - rootFile(`gatsby-node.ts`), - ] - const GATSBY_CONFIG_REGEX = /^gatsby-config\.[jt]s$/ - let watcher: chokidar.FSWatcher - - if (!isCI()) { - watcher = chokidar.watch(files).on(`change`, filePath => { - const file = path.basename(filePath) - - if (file.match(GATSBY_CONFIG_REGEX)) { - const newConfig = requireUncached(rootFile(`gatsby-config`)) - - if (!doesConfigChangeRequireRestart(lastConfig, newConfig)) { - lastConfig = newConfig - return - } - - lastConfig = newConfig - } - - console.warn( - `develop process needs to be restarted to apply the changes to ${file}` - ) - io.emit(`structured-log`, { - type: `LOG_ACTION`, - action: { - type: `DEVELOP`, - payload: `RESTART_REQUIRED`, - dirtyFile: file, - }, - }) - }) - } - - // route ipc messaging to the original develop process - process.on(`message`, msg => { - developProcess.send(msg) - }) - process.on(`SIGINT`, async () => { await shutdownServices( { developProcess, - telemetryServerProcess, - unlocks, - statusServer, - proxy, - watcher, }, `SIGINT` ) @@ -503,11 +301,6 @@ module.exports = async (program: IProgram): Promise => { await shutdownServices( { developProcess, - telemetryServerProcess, - unlocks, - statusServer, - proxy, - watcher, }, `SIGTERM` ) @@ -519,11 +312,6 @@ module.exports = async (program: IProgram): Promise => { shutdownServices( { developProcess, - telemetryServerProcess, - unlocks, - statusServer, - proxy, - watcher, }, signal as NodeJS.Signals ) @@ -531,23 +319,11 @@ module.exports = async (program: IProgram): Promise => { } interface IShutdownServicesOptions { - statusServer: https.Server | http.Server developProcess: ControllableScript - proxy: IProxyControls - unlocks: Array - watcher: chokidar.FSWatcher - telemetryServerProcess: ControllableScript } function shutdownServices( - { - statusServer, - developProcess, - proxy, - unlocks, - watcher, - telemetryServerProcess, - }: IShutdownServicesOptions, + { developProcess }: IShutdownServicesOptions, signal: NodeJS.Signals ): Promise { try { @@ -555,19 +331,7 @@ function shutdownServices( } catch (e) { // nop } - const services = [ - developProcess.stop(signal), - telemetryServerProcess.stop(), - watcher?.close(), - new Promise(resolve => statusServer.close(resolve)), - new Promise(resolve => proxy.server.close(resolve)), - ] - - unlocks.forEach(unlock => { - if (unlock) { - services.push(unlock()) - } - }) + const services = [developProcess.stop(signal)] return Promise.all(services) .catch(() => {}) diff --git a/packages/gatsby/src/commands/types.ts b/packages/gatsby/src/commands/types.ts index 1cc9c692aef7c..36c8c8f80e407 100644 --- a/packages/gatsby/src/commands/types.ts +++ b/packages/gatsby/src/commands/types.ts @@ -19,7 +19,6 @@ export interface IProgram { open: boolean openTracingConfigFile: string port: number - proxyPort: number host: string report: Reporter [`cert-file`]?: string diff --git a/packages/gatsby/src/internal-plugins/internal-data-bridge/gatsby-node.js b/packages/gatsby/src/internal-plugins/internal-data-bridge/gatsby-node.js index 50a6943b1dc3e..73b0ed335adba 100644 --- a/packages/gatsby/src/internal-plugins/internal-data-bridge/gatsby-node.js +++ b/packages/gatsby/src/internal-plugins/internal-data-bridge/gatsby-node.js @@ -80,7 +80,7 @@ exports.sourceNodes = ({ siteMetadata: { ...configCopy.siteMetadata, }, - port: program.proxyPort, + port: program.port, host: program.host, ...configCopy, } diff --git a/packages/gatsby/src/redux/reducers/program.ts b/packages/gatsby/src/redux/reducers/program.ts index 07bcbbd70ffa6..6fae530c98943 100644 --- a/packages/gatsby/src/redux/reducers/program.ts +++ b/packages/gatsby/src/redux/reducers/program.ts @@ -9,7 +9,6 @@ const initialState: IStateProgram = { open: false, openTracingConfigFile: ``, port: 80, - proxyPort: 80, host: `localhost`, sitePackageJson: {}, extensions: [], diff --git a/packages/gatsby/src/services/start-webpack-server.ts b/packages/gatsby/src/services/start-webpack-server.ts index c2128060dc759..9733209b2ea0e 100644 --- a/packages/gatsby/src/services/start-webpack-server.ts +++ b/packages/gatsby/src/services/start-webpack-server.ts @@ -75,7 +75,7 @@ export async function startWebpackServer({ const urls = prepareUrls( program.https ? `https` : `http`, program.host, - program.proxyPort + program.port ) const isSuccessful = !stats.hasErrors() diff --git a/packages/gatsby/src/utils/develop-proxy.ts b/packages/gatsby/src/utils/develop-proxy.ts deleted file mode 100644 index dc1aaef7c1735..0000000000000 --- a/packages/gatsby/src/utils/develop-proxy.ts +++ /dev/null @@ -1,84 +0,0 @@ -import http from "http" -import https from "https" -import httpProxy from "http-proxy" -import fs from "fs-extra" -import { getServices } from "gatsby-core-utils" -import restartingScreen from "./restarting-screen" -import { IProgram } from "../commands/types" - -export interface IProxyControls { - serveRestartingScreen: () => void - serveSite: () => void - server: https.Server | http.Server -} - -const noop = (): void => {} - -export const startDevelopProxy = (input: { - proxyPort: number - targetPort: number - program: IProgram -}): IProxyControls => { - let shouldServeRestartingScreen = false - - const proxy = httpProxy.createProxyServer({ - target: `http://localhost:${input.targetPort}`, - changeOrigin: true, - preserveHeaderKeyCase: true, - autoRewrite: true, - ws: true, - }) - - // Noop on proxy errors, as this throws a bunch of "Socket hang up" - // ones whenever the page is refreshed - proxy.on(`error`, noop) - - const app: http.RequestListener = (req, res): void => { - // Add a route at localhost:8000/___services for service discovery - if (req.url === `/___services`) { - getServices(input.program.directory).then(services => { - res.setHeader(`Content-Type`, `application/json`) - res.end(JSON.stringify(services)) - }) - return - } - - if (req.url === `/socket.io/socket.io.js`) { - res.setHeader(`Content-Type`, `application/javascript`) - res.end( - fs.readFileSync(require.resolve(`socket.io-client/dist/socket.io.js`)) - ) - return - } - - if ( - shouldServeRestartingScreen || - req.url === `/___debug-restarting-screen` - ) { - res.end(restartingScreen) - return - } - - proxy.web(req, res) - } - - const server = input.program.ssl - ? https.createServer(input.program.ssl, app) - : http.createServer(app) - - server.on(`upgrade`, function (req, socket, head) { - proxy.ws(req, socket, head) - }) - - server.listen(input.proxyPort, input.program.host) - - return { - server, - serveRestartingScreen: (): void => { - shouldServeRestartingScreen = true - }, - serveSite: (): void => { - shouldServeRestartingScreen = false - }, - } -} diff --git a/packages/gatsby/src/utils/telemetry-server.ts b/packages/gatsby/src/utils/telemetry-server.ts deleted file mode 100644 index 65bdda74dcead..0000000000000 --- a/packages/gatsby/src/utils/telemetry-server.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This exposes gatsby-telemetry functions over HTTP. POST an array of arguments to the path. - * For example: - * curl -X POST http://localhost:2345/setVersion - * -H "Content-Type: application/json" - * -d "[\"1.2.3\"]" - */ -import express from "express" -import bodyParser from "body-parser" -import cors from "cors" -import { - setDefaultComponentId, - trackCli, - trackError, - startBackgroundUpdate, -} from "gatsby-telemetry" - -setDefaultComponentId(`gatsby-admin`) - -// These routes will exist in the API at the keys, e.g. -// http://localhost:1234/trackEvent -const ROUTES = { - trackEvent: trackCli, - trackError, -} - -const app = express() - -app.use(cors()) - -// Overview over all possible routes at / -app.get(`/`, (_, res) => { - res.set(`Content-Type`, `text/html`) - res.send( - `
    - ${Object.keys(ROUTES) - .map(route => `
  • /${route}
  • `) - .join(`\n`)} -
` - ) -}) - -Object.keys(ROUTES).map(route => { - app.post(`/${route}`, bodyParser.json(), (req, res) => { - if (!req.body || !Array.isArray(req.body)) { - res.json({ - status: `error`, - error: `Please provide a body array with the arguments for the function.`, - }) - return - } - - try { - ROUTES[route](...req.body) - } catch (err) { - console.error(err) - res.json({ status: `error`, error: err.message }) - return - } - - res.json({ status: `success` }) - }) -}) - -export default function startTelemetryServer(port: number): void { - startBackgroundUpdate() - app.listen(port) -}