From 67205023756cbe2b707258dd6399c3054debbad0 Mon Sep 17 00:00:00 2001 From: olensmar Date: Tue, 5 Jul 2022 09:49:17 +0200 Subject: [PATCH 1/7] feat: initial implementation of local server for hosting editor schemas --- electron/app/createWorker.ts | 53 ++++++++++++++++++++++++++++++ electron/app/ipc/ipcListeners.ts | 8 +++++ electron/app/openApplication.ts | 4 +++ package.json | 3 +- public/worker.html | 13 ++++++++ src/hooks/useResourceYamlSchema.ts | 24 ++++++++++++-- 6 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 electron/app/createWorker.ts create mode 100644 public/worker.html diff --git a/electron/app/createWorker.ts b/electron/app/createWorker.ts new file mode 100644 index 0000000000..3e607b3261 --- /dev/null +++ b/electron/app/createWorker.ts @@ -0,0 +1,53 @@ +import {BrowserWindow} from 'electron'; + +import getPort from 'get-port'; +import * as http from 'http'; + +const isDev = process.env.NODE_ENV === 'development'; +const localContent: Record = {}; +let server: http.Server | undefined; + +export const createWorker = () => { + let workerWindow = new BrowserWindow({show: false}); + + if (isDev) { + workerWindow.loadURL('http://localhost:3000/worker.html'); + workerWindow.webContents.openDevTools(); + } else { + workerWindow.loadURL(`file://${__dirname}/../../worker.html`); + } + + workerWindow.webContents.once('did-finish-load', () => { + console.log('Worker initialized..'); + }); +}; + +function initServer() { + console.log('Initializing local server'); + server = http.createServer((req, res) => { + if (req.url && localContent[req.url]) { + console.log(`Serving content for ${req.url}`); + res.write(localContent[req.url]); + res.end(); + } else { + res.end(`Not found - stranger things have happened.`); + } + }); + + if (server) { + getPort({port: 51038}).then(port => { + if (server) { + server.listen(port); + console.log(`Local server listening on port ${port}`); + } + }); + } +} + +export const addLocalServerContent = (path: string, schema: string) => { + if (!server) { + initServer(); + } + + localContent[path] = schema; +}; diff --git a/electron/app/ipc/ipcListeners.ts b/electron/app/ipc/ipcListeners.ts index eb63fcf6c7..3acd46f037 100644 --- a/electron/app/ipc/ipcListeners.ts +++ b/electron/app/ipc/ipcListeners.ts @@ -37,6 +37,8 @@ import {CommandOptions} from '@utils/commands'; import {ProjectNameChange, StorePropagation} from '@utils/global-electron-store'; import {UPDATE_APPLICATION, trackEvent} from '@utils/telemetry'; +import {addLocalServerContent} from '@root/electron/app/createWorker'; + import autoUpdater from '../autoUpdater'; import { checkNewVersion, @@ -263,3 +265,9 @@ ipcMain.on('global-electron-store-update', (event, args: any) => { log.warn(`received invalid event type for global electron store update ${args.eventType}`); } }); + +ipcMain.on('add-local-server-content', (event, args: any) => { + if (args.path && args.content) { + addLocalServerContent(args.path, args.content); + } +}); diff --git a/electron/app/openApplication.ts b/electron/app/openApplication.ts index 4c499ba998..d7754854be 100644 --- a/electron/app/openApplication.ts +++ b/electron/app/openApplication.ts @@ -7,6 +7,8 @@ import * as path from 'path'; import {logToFile} from '@utils/logToFile'; +import {createWorker} from '@root/electron/app/createWorker'; + import {createWindow} from './createWindow'; import {getDockMenu} from './menu'; @@ -30,6 +32,7 @@ export const openApplication = async (givenPath?: string) => { ElectronStore.initRenderer(); const win = createWindow(givenPath); + createWorker(); if (app.dock) { const image = nativeImage.createFromPath(path.join(app.getAppPath(), '/public/large-icon-256.png')); @@ -42,6 +45,7 @@ export const openApplication = async (givenPath?: string) => { app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(givenPath); + createWorker(); } }); diff --git a/package.json b/package.json index 52cd0cd099..a7931c01a8 100644 --- a/package.json +++ b/package.json @@ -119,8 +119,9 @@ "execa": "5.1.1", "fast-deep-equal": "3.1.3", "flat": "5.0.2", - "git-url-parse": "12.0.0", "framer-motion": "6.3.11", + "get-port": "5.1.1", + "git-url-parse": "12.0.0", "jsonpath-plus": "6.0.1", "lodash": "4.17.21", "loglevel": "1.8.0", diff --git a/public/worker.html b/public/worker.html new file mode 100644 index 0000000000..3b996f2e64 --- /dev/null +++ b/public/worker.html @@ -0,0 +1,13 @@ + + + + + Monokle Worker + + +

This is only shown in dev mode - to give access to DevTools

+ + + diff --git a/src/hooks/useResourceYamlSchema.ts b/src/hooks/useResourceYamlSchema.ts index c296c89888..e5b179da38 100644 --- a/src/hooks/useResourceYamlSchema.ts +++ b/src/hooks/useResourceYamlSchema.ts @@ -1,14 +1,19 @@ +import {ipcRenderer} from 'electron'; + import {useEffect} from 'react'; import {languages} from 'monaco-editor/esm/vs/editor/editor.api'; import {FileMapType} from '@models/appstate'; import {K8sResource} from '@models/k8sresource'; +import {ResourceKindHandler} from '@models/resourcekindhandler'; import {isKustomizationPatch} from '@redux/services/kustomize'; import {hasSupportedResourceContent} from '@redux/services/resource'; import {getResourceSchema, getSchemaForPath} from '@redux/services/schema'; +import {getResourceKindHandler} from '@src/kindhandlers'; + function useResourceYamlSchema( yaml: typeof languages.yaml, userDataDir: string, @@ -28,16 +33,30 @@ function useResourceYamlSchema( let resourceSchema; let validate = true; + let resourceKindHandler: ResourceKindHandler | undefined; if (resource) { resourceSchema = getResourceSchema(resource, k8sVersion, userDataDir); validate = resourceSchema && !isKustomizationPatch(resource) && hasSupportedResourceContent(resource); + resourceKindHandler = getResourceKindHandler(resource.kind); } else if (selectedPath && fileMap) { resourceSchema = getSchemaForPath(selectedPath, fileMap); validate = resourceSchema !== undefined; } - yaml && + if (yaml) { + let schemaUri = `https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v${k8sVersion}/${resource?.kind.toLowerCase()}.json`; + + // if this is a custom resource then host the schema using the local server + if (resourceKindHandler && resourceKindHandler.isCustom) { + const schemaPath = `/schemas/${resource?.kind.toLowerCase()}.json`; + ipcRenderer.send('add-local-server-content', { + path: schemaPath, + content: JSON.stringify(resourceSchema, null, 2), + }); + schemaUri = `http://localhost:51038${schemaPath}`; + } + yaml.yamlDefaults.setDiagnosticsOptions({ validate, enableSchemaRequest: true, @@ -47,12 +66,13 @@ function useResourceYamlSchema( format: true, schemas: [ { - uri: 'http://monokle/k8s.json', // id of the first schema + uri: schemaUri, fileMatch: ['*'], // associate with our model schema: resourceSchema || {}, }, ], }); + } // eslint-disable-next-line react-hooks/exhaustive-deps }, [resource, selectedPath, fileMap, k8sVersion]); } From bed1262470bd4b4b19b3ad3e7b6a79df7f61c334 Mon Sep 17 00:00:00 2001 From: olensmar Date: Tue, 5 Jul 2022 10:06:57 +0200 Subject: [PATCH 2/7] fix: housekeeping --- electron/app/createWorker.ts | 4 ++-- public/worker.html | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/electron/app/createWorker.ts b/electron/app/createWorker.ts index 3e607b3261..5d32eed8c4 100644 --- a/electron/app/createWorker.ts +++ b/electron/app/createWorker.ts @@ -44,10 +44,10 @@ function initServer() { } } -export const addLocalServerContent = (path: string, schema: string) => { +export const addLocalServerContent = (path: string, content: string) => { if (!server) { initServer(); } - localContent[path] = schema; + localContent[path] = content; }; diff --git a/public/worker.html b/public/worker.html index 3b996f2e64..f11cfd5447 100644 --- a/public/worker.html +++ b/public/worker.html @@ -5,7 +5,6 @@ Monokle Worker -

This is only shown in dev mode - to give access to DevTools

From 68ec3eb784a481b945536fb5b4d6ec122fa7833e Mon Sep 17 00:00:00 2001 From: olensmar Date: Tue, 5 Jul 2022 11:24:19 +0200 Subject: [PATCH 3/7] fix: added workaround for missing standalone CustomResourceDefinition schema --- src/hooks/useResourceYamlSchema.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hooks/useResourceYamlSchema.ts b/src/hooks/useResourceYamlSchema.ts index e5b179da38..64bb4d1687 100644 --- a/src/hooks/useResourceYamlSchema.ts +++ b/src/hooks/useResourceYamlSchema.ts @@ -45,7 +45,8 @@ function useResourceYamlSchema( } if (yaml) { - let schemaUri = `https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v${k8sVersion}/${resource?.kind.toLowerCase()}.json`; + const standalone = resource?.kind === 'CustomResourceDefinition' ? '' : '-standalone'; + let schemaUri = `https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v${k8sVersion}${standalone}/${resource?.kind.toLowerCase()}.json`; // if this is a custom resource then host the schema using the local server if (resourceKindHandler && resourceKindHandler.isCustom) { From 8140b8fd96850735fa23f6a983d449db56299131 Mon Sep 17 00:00:00 2001 From: olensmar Date: Tue, 5 Jul 2022 11:48:53 +0200 Subject: [PATCH 4/7] fix: refactoring and added support for file-based schemas --- src/hooks/useResourceYamlSchema.ts | 38 ++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/hooks/useResourceYamlSchema.ts b/src/hooks/useResourceYamlSchema.ts index 64bb4d1687..45ea00e05f 100644 --- a/src/hooks/useResourceYamlSchema.ts +++ b/src/hooks/useResourceYamlSchema.ts @@ -14,6 +14,18 @@ import {getResourceSchema, getSchemaForPath} from '@redux/services/schema'; import {getResourceKindHandler} from '@src/kindhandlers'; +function setLocalSchemaContent(name: string, resourceSchema: any) { + if (name.startsWith('/')) { + name = name.substring(1); + } + const schemaPath = `/schemas/${name}.json`; + ipcRenderer.send('add-local-server-content', { + path: schemaPath, + content: JSON.stringify(resourceSchema || {}, null, 2), + }); + return `http://localhost:51038${schemaPath}`; +} + function useResourceYamlSchema( yaml: typeof languages.yaml, userDataDir: string, @@ -45,17 +57,23 @@ function useResourceYamlSchema( } if (yaml) { - const standalone = resource?.kind === 'CustomResourceDefinition' ? '' : '-standalone'; - let schemaUri = `https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v${k8sVersion}${standalone}/${resource?.kind.toLowerCase()}.json`; + let schemaUri = ''; - // if this is a custom resource then host the schema using the local server - if (resourceKindHandler && resourceKindHandler.isCustom) { - const schemaPath = `/schemas/${resource?.kind.toLowerCase()}.json`; - ipcRenderer.send('add-local-server-content', { - path: schemaPath, - content: JSON.stringify(resourceSchema, null, 2), - }); - schemaUri = `http://localhost:51038${schemaPath}`; + if (resource) { + // if this is a custom resource then host the schema using the local server + if (resourceKindHandler && resourceKindHandler.isCustom) { + schemaUri = setLocalSchemaContent(resourceKindHandler.kind.toLowerCase(), resourceSchema); + } else { + // use public GitHub repo containing all schemas + const standalone = resource?.kind === 'CustomResourceDefinition' ? '' : '-standalone'; + schemaUri = `https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v${k8sVersion}${standalone}/${resource?.kind.toLowerCase()}.json`; + } + } + // for file-based schemas + else if (resourceSchema && selectedPath) { + schemaUri = setLocalSchemaContent(selectedPath, resourceSchema); + } else { + schemaUri = setLocalSchemaContent('empty', {}); } yaml.yamlDefaults.setDiagnosticsOptions({ From 70eddfeca5be721910da712eae6b123ccfec38fc Mon Sep 17 00:00:00 2001 From: olensmar Date: Wed, 6 Jul 2022 13:18:22 +0200 Subject: [PATCH 5/7] fix: removed local server and provided scaffolding for opening schemas using custom openHandler --- electron/app/createWorker.ts | 53 ---------------------- electron/app/ipc/ipcListeners.ts | 8 ---- electron/app/openApplication.ts | 4 -- package-lock.json | 17 +++++++ public/worker.html | 12 ----- src/components/molecules/Monaco/Monaco.tsx | 16 +++++++ src/hooks/useResourceYamlSchema.ts | 37 ++++----------- 7 files changed, 43 insertions(+), 104 deletions(-) delete mode 100644 electron/app/createWorker.ts delete mode 100644 public/worker.html diff --git a/electron/app/createWorker.ts b/electron/app/createWorker.ts deleted file mode 100644 index 5d32eed8c4..0000000000 --- a/electron/app/createWorker.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {BrowserWindow} from 'electron'; - -import getPort from 'get-port'; -import * as http from 'http'; - -const isDev = process.env.NODE_ENV === 'development'; -const localContent: Record = {}; -let server: http.Server | undefined; - -export const createWorker = () => { - let workerWindow = new BrowserWindow({show: false}); - - if (isDev) { - workerWindow.loadURL('http://localhost:3000/worker.html'); - workerWindow.webContents.openDevTools(); - } else { - workerWindow.loadURL(`file://${__dirname}/../../worker.html`); - } - - workerWindow.webContents.once('did-finish-load', () => { - console.log('Worker initialized..'); - }); -}; - -function initServer() { - console.log('Initializing local server'); - server = http.createServer((req, res) => { - if (req.url && localContent[req.url]) { - console.log(`Serving content for ${req.url}`); - res.write(localContent[req.url]); - res.end(); - } else { - res.end(`Not found - stranger things have happened.`); - } - }); - - if (server) { - getPort({port: 51038}).then(port => { - if (server) { - server.listen(port); - console.log(`Local server listening on port ${port}`); - } - }); - } -} - -export const addLocalServerContent = (path: string, content: string) => { - if (!server) { - initServer(); - } - - localContent[path] = content; -}; diff --git a/electron/app/ipc/ipcListeners.ts b/electron/app/ipc/ipcListeners.ts index 3acd46f037..eb63fcf6c7 100644 --- a/electron/app/ipc/ipcListeners.ts +++ b/electron/app/ipc/ipcListeners.ts @@ -37,8 +37,6 @@ import {CommandOptions} from '@utils/commands'; import {ProjectNameChange, StorePropagation} from '@utils/global-electron-store'; import {UPDATE_APPLICATION, trackEvent} from '@utils/telemetry'; -import {addLocalServerContent} from '@root/electron/app/createWorker'; - import autoUpdater from '../autoUpdater'; import { checkNewVersion, @@ -265,9 +263,3 @@ ipcMain.on('global-electron-store-update', (event, args: any) => { log.warn(`received invalid event type for global electron store update ${args.eventType}`); } }); - -ipcMain.on('add-local-server-content', (event, args: any) => { - if (args.path && args.content) { - addLocalServerContent(args.path, args.content); - } -}); diff --git a/electron/app/openApplication.ts b/electron/app/openApplication.ts index d7754854be..4c499ba998 100644 --- a/electron/app/openApplication.ts +++ b/electron/app/openApplication.ts @@ -7,8 +7,6 @@ import * as path from 'path'; import {logToFile} from '@utils/logToFile'; -import {createWorker} from '@root/electron/app/createWorker'; - import {createWindow} from './createWindow'; import {getDockMenu} from './menu'; @@ -32,7 +30,6 @@ export const openApplication = async (givenPath?: string) => { ElectronStore.initRenderer(); const win = createWindow(givenPath); - createWorker(); if (app.dock) { const image = nativeImage.createFromPath(path.join(app.getAppPath(), '/public/large-icon-256.png')); @@ -45,7 +42,6 @@ export const openApplication = async (givenPath?: string) => { app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(givenPath); - createWorker(); } }); diff --git a/package-lock.json b/package-lock.json index 2c0cf57ddd..b9b6700f67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "fast-deep-equal": "3.1.3", "flat": "5.0.2", "framer-motion": "6.3.11", + "get-port": "5.1.1", "git-url-parse": "12.0.0", "jsonpath-plus": "6.0.1", "lodash": "4.17.21", @@ -13375,6 +13376,17 @@ "node": ">=10" } }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", @@ -38698,6 +38710,11 @@ } } }, + "get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==" + }, "get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", diff --git a/public/worker.html b/public/worker.html deleted file mode 100644 index f11cfd5447..0000000000 --- a/public/worker.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - Monokle Worker - - - - - diff --git a/src/components/molecules/Monaco/Monaco.tsx b/src/components/molecules/Monaco/Monaco.tsx index 17947269d7..ac3aa858ae 100644 --- a/src/components/molecules/Monaco/Monaco.tsx +++ b/src/components/molecules/Monaco/Monaco.tsx @@ -213,6 +213,22 @@ const Monaco = (props: {diffSelectedResource: () => void; applySelection: () => e.revealLineNearTop(1); e.setSelection(new monaco.Selection(0, 0, 0, 0)); setEditorMounted(true); + + // add custom opener for schema links in hover popups + let contribution: any = e.getContribution('editor.linkDetector'); + if (contribution?.openerService) { + contribution.openerService.registerOpener({ + open: (resource: string) => { + let isSchemaRequest = resource.startsWith('schema://'); + if (isSchemaRequest) { + // we should open a popup with the schema here - the actual schema can be retrieved from + // the diagnosticsOptions.schemas array + console.log('opening schema', resource, yaml.yamlDefaults.diagnosticsOptions.schemas); + } + return Promise.resolve(isSchemaRequest); + }, + }); + } }; const onChange = (newValue: any) => { diff --git a/src/hooks/useResourceYamlSchema.ts b/src/hooks/useResourceYamlSchema.ts index 45ea00e05f..ea31a09666 100644 --- a/src/hooks/useResourceYamlSchema.ts +++ b/src/hooks/useResourceYamlSchema.ts @@ -1,5 +1,3 @@ -import {ipcRenderer} from 'electron'; - import {useEffect} from 'react'; import {languages} from 'monaco-editor/esm/vs/editor/editor.api'; @@ -8,24 +6,12 @@ import {FileMapType} from '@models/appstate'; import {K8sResource} from '@models/k8sresource'; import {ResourceKindHandler} from '@models/resourcekindhandler'; -import {isKustomizationPatch} from '@redux/services/kustomize'; +import {isKustomizationPatch, isKustomizationResource} from '@redux/services/kustomize'; import {hasSupportedResourceContent} from '@redux/services/resource'; import {getResourceSchema, getSchemaForPath} from '@redux/services/schema'; import {getResourceKindHandler} from '@src/kindhandlers'; -function setLocalSchemaContent(name: string, resourceSchema: any) { - if (name.startsWith('/')) { - name = name.substring(1); - } - const schemaPath = `/schemas/${name}.json`; - ipcRenderer.send('add-local-server-content', { - path: schemaPath, - content: JSON.stringify(resourceSchema || {}, null, 2), - }); - return `http://localhost:51038${schemaPath}`; -} - function useResourceYamlSchema( yaml: typeof languages.yaml, userDataDir: string, @@ -57,28 +43,25 @@ function useResourceYamlSchema( } if (yaml) { - let schemaUri = ''; + let schemaUri = 'schema://empty'; - if (resource) { - // if this is a custom resource then host the schema using the local server - if (resourceKindHandler && resourceKindHandler.isCustom) { - schemaUri = setLocalSchemaContent(resourceKindHandler.kind.toLowerCase(), resourceSchema); + if (resource?.kind) { + if (isKustomizationResource(resource)) { + schemaUri = 'schema://kustomize/Kustomize'; + } else if (resourceKindHandler && resourceKindHandler.isCustom) { + schemaUri = `schema://custom/${resourceKindHandler.kind}`; } else { - // use public GitHub repo containing all schemas - const standalone = resource?.kind === 'CustomResourceDefinition' ? '' : '-standalone'; - schemaUri = `https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v${k8sVersion}${standalone}/${resource?.kind.toLowerCase()}.json`; + schemaUri = `schema://kubernetes/${resource?.kind}-v${k8sVersion}`; } } // for file-based schemas else if (resourceSchema && selectedPath) { - schemaUri = setLocalSchemaContent(selectedPath, resourceSchema); - } else { - schemaUri = setLocalSchemaContent('empty', {}); + schemaUri = `schema://local${selectedPath}`; } yaml.yamlDefaults.setDiagnosticsOptions({ validate, - enableSchemaRequest: true, + enableSchemaRequest: false, hover: true, completion: true, isKubernetes: Boolean(resource), From 3cba22014b71a414d9bf474d935297d6de617367 Mon Sep 17 00:00:00 2001 From: olensmar Date: Wed, 6 Jul 2022 14:51:59 +0200 Subject: [PATCH 6/7] fix: removed get-port dependency --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index a7931c01a8..52cd0cd099 100644 --- a/package.json +++ b/package.json @@ -119,9 +119,8 @@ "execa": "5.1.1", "fast-deep-equal": "3.1.3", "flat": "5.0.2", - "framer-motion": "6.3.11", - "get-port": "5.1.1", "git-url-parse": "12.0.0", + "framer-motion": "6.3.11", "jsonpath-plus": "6.0.1", "lodash": "4.17.21", "loglevel": "1.8.0", From 67117c44e033b5c7e67484b38abe4463438470ca Mon Sep 17 00:00:00 2001 From: olensmar Date: Wed, 6 Jul 2022 15:36:23 +0200 Subject: [PATCH 7/7] fix: removed get-port dependency --- package-lock.json | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9b6700f67..2c0cf57ddd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,6 @@ "fast-deep-equal": "3.1.3", "flat": "5.0.2", "framer-motion": "6.3.11", - "get-port": "5.1.1", "git-url-parse": "12.0.0", "jsonpath-plus": "6.0.1", "lodash": "4.17.21", @@ -13376,17 +13375,6 @@ "node": ">=10" } }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", @@ -38710,11 +38698,6 @@ } } }, - "get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==" - }, "get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",