From bde0a9ef91d3b621df119511815bf1d9fde919ec Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 17 Dec 2021 08:28:47 -0500 Subject: [PATCH 01/12] Change isEnabledForCluster to use KubernetesCluster instead of Cluster (#4584) --- src/common/ipc/catalog.ts | 32 +++++++++++++++++++ .../extension-loader/extension-loader.ts | 9 +++--- src/extensions/lens-renderer-extension.ts | 9 ++++-- src/main/catalog-pusher.ts | 21 ++++++------ src/renderer/api/catalog-entity-registry.ts | 7 +++- src/renderer/cluster-frame.tsx | 16 ++++++++-- 6 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 src/common/ipc/catalog.ts diff --git a/src/common/ipc/catalog.ts b/src/common/ipc/catalog.ts new file mode 100644 index 0000000000..7477648594 --- /dev/null +++ b/src/common/ipc/catalog.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +export enum CatalogIpcEvents { + /** + * This is broadcast on whenever there is an update to any catalog item + */ + ITEMS = "catalog:items", + + /** + * This can be sent from renderer to main to initialize a broadcast of ITEMS + */ + INIT = "catalog:init", +} diff --git a/src/extensions/extension-loader/extension-loader.ts b/src/extensions/extension-loader/extension-loader.ts index 43d3eece63..b7d53402ed 100644 --- a/src/extensions/extension-loader/extension-loader.ts +++ b/src/extensions/extension-loader/extension-loader.ts @@ -25,10 +25,10 @@ import { isEqual } from "lodash"; import { action, computed, makeObservable, observable, observe, reaction, when } from "mobx"; import path from "path"; import { AppPaths } from "../../common/app-paths"; -import { ClusterStore } from "../../common/cluster-store"; import { broadcastMessage, ipcMainOn, ipcRendererOn, requestMain, ipcMainHandle } from "../../common/ipc"; -import { Disposer, getHostedClusterId, toJS } from "../../common/utils"; +import { Disposer, toJS } from "../../common/utils"; import logger from "../../main/logger"; +import type { KubernetesCluster } from "../common-api/catalog"; import type { InstalledExtension } from "../extension-discovery"; import { ExtensionsStore } from "../extensions-store"; import type { LensExtension, LensExtensionConstructor, LensExtensionId } from "../lens-extension"; @@ -280,12 +280,11 @@ export class ExtensionLoader { }); } - loadOnClusterRenderer() { + loadOnClusterRenderer(entity: KubernetesCluster) { logger.debug(`${logModule}: load on cluster renderer (dashboard)`); - const cluster = ClusterStore.getInstance().getById(getHostedClusterId()); this.autoInitExtensions(async (extension: LensRendererExtension) => { - if ((await extension.isEnabledForCluster(cluster)) === false) { + if ((await extension.isEnabledForCluster(entity)) === false) { return []; } diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index 4a5abadbad..67567969ee 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -20,13 +20,13 @@ */ import type * as registries from "./registries"; -import type { Cluster } from "../main/cluster"; import { Disposers, LensExtension } from "./lens-extension"; import { getExtensionPageUrl } from "./registries/page-registry"; import type { CatalogEntity } from "../common/catalog"; import type { Disposer } from "../common/utils"; import { catalogEntityRegistry, EntityFilter } from "../renderer/api/catalog-entity-registry"; import { catalogCategoryRegistry, CategoryFilter } from "../renderer/api/catalog-category-registry"; +import type { KubernetesCluster } from "../common/catalog-entities"; export class LensRendererExtension extends LensExtension { globalPages: registries.PageRegistration[] = []; @@ -57,9 +57,12 @@ export class LensRendererExtension extends LensExtension { } /** - * Defines if extension is enabled for a given cluster. Defaults to `true`. + * Defines if extension is enabled for a given cluster. This method is only + * called when the extension is created within a cluster frame. + * + * The default implementation is to return `true` */ - async isEnabledForCluster(cluster: Cluster): Promise { + async isEnabledForCluster(cluster: KubernetesCluster): Promise { return (void cluster) || true; } diff --git a/src/main/catalog-pusher.ts b/src/main/catalog-pusher.ts index b662c36f69..b86a03c7b1 100644 --- a/src/main/catalog-pusher.ts +++ b/src/main/catalog-pusher.ts @@ -20,22 +20,25 @@ */ import { reaction } from "mobx"; -import { broadcastMessage } from "../common/ipc"; +import { broadcastMessage, ipcMainOn } from "../common/ipc"; import type { CatalogEntityRegistry } from "./catalog"; import "../common/catalog-entities/kubernetes-cluster"; -import { toJS } from "../common/utils"; +import { disposer, toJS } from "../common/utils"; import { debounce } from "lodash"; import type { CatalogEntity } from "../common/catalog"; - +import { CatalogIpcEvents } from "../common/ipc/catalog"; const broadcaster = debounce((items: CatalogEntity[]) => { - broadcastMessage("catalog:items", items); + broadcastMessage(CatalogIpcEvents.ITEMS, items); }, 1_000, { leading: true, trailing: true }); export function pushCatalogToRenderer(catalog: CatalogEntityRegistry) { - return reaction(() => toJS(catalog.items), (items) => { - broadcaster(items); - }, { - fireImmediately: true, - }); + return disposer( + ipcMainOn(CatalogIpcEvents.INIT, () => broadcaster(toJS(catalog.items))), + reaction(() => toJS(catalog.items), (items) => { + broadcaster(items); + }, { + fireImmediately: true, + }), + ); } diff --git a/src/renderer/api/catalog-entity-registry.ts b/src/renderer/api/catalog-entity-registry.ts index abeaae6f68..4885c8f265 100644 --- a/src/renderer/api/catalog-entity-registry.ts +++ b/src/renderer/api/catalog-entity-registry.ts @@ -30,6 +30,8 @@ import { once } from "lodash"; import logger from "../../common/logger"; import { catalogEntityRunContext } from "./catalog-entity"; import { CatalogRunEvent } from "../../common/catalog/catalog-run-event"; +import { ipcRenderer } from "electron"; +import { CatalogIpcEvents } from "../../common/ipc/catalog"; export type EntityFilter = (entity: CatalogEntity) => any; export type CatalogEntityOnBeforeRun = (event: CatalogRunEvent) => void | Promise; @@ -70,9 +72,12 @@ export class CatalogEntityRegistry { } init() { - ipcRendererOn("catalog:items", (event, items: (CatalogEntityData & CatalogEntityKindData)[]) => { + ipcRendererOn(CatalogIpcEvents.ITEMS, (event, items: (CatalogEntityData & CatalogEntityKindData)[]) => { this.updateItems(items); }); + + // Make sure that we get items ASAP and not the next time one of them changes + ipcRenderer.send(CatalogIpcEvents.INIT); } @action updateItems(items: (CatalogEntityData & CatalogEntityKindData)[]) { diff --git a/src/renderer/cluster-frame.tsx b/src/renderer/cluster-frame.tsx index fe867da04a..5113377116 100755 --- a/src/renderer/cluster-frame.tsx +++ b/src/renderer/cluster-frame.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import React from "react"; -import { observable, makeObservable } from "mobx"; +import { observable, makeObservable, when } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { Redirect, Route, Router, Switch } from "react-router"; import { history } from "./navigation"; @@ -74,6 +74,7 @@ import { PortForwardDialog } from "./port-forward"; import { DeleteClusterDialog } from "./components/delete-cluster-dialog"; import { WorkloadsOverview } from "./components/+workloads-overview/overview"; import { KubeObjectListLayout } from "./components/kube-object-list-layout"; +import type { KubernetesCluster } from "../common/catalog-entities"; @observer export class ClusterFrame extends React.Component { @@ -101,7 +102,18 @@ export class ClusterFrame extends React.Component { catalogEntityRegistry.activeEntity = ClusterFrame.clusterId; - extensionLoader.loadOnClusterRenderer(); + // Only load the extensions once the catalog has been populated + when( + () => Boolean(catalogEntityRegistry.activeEntity), + () => extensionLoader.loadOnClusterRenderer(catalogEntityRegistry.activeEntity as KubernetesCluster), + { + timeout: 15_000, + onError: (error) => { + console.warn("[CLUSTER-FRAME]: error from activeEntity when()", error); + Notifications.error("Failed to get KubernetesCluster for this view. Extensions will not be loaded."); + }, + }, + ); setTimeout(() => { appEventBus.emit({ From 96309814dc5d8835f733a7387f4e085029a37461 Mon Sep 17 00:00:00 2001 From: Juho Heikka Date: Fri, 17 Dec 2021 15:30:05 +0200 Subject: [PATCH 02/12] Add ability from extension to add preferences to application preferences (#4579) --- .../components/+preferences/application.tsx | 7 +++ .../+preferences/extension-settings.tsx | 50 +++++++++++++++++++ .../components/+preferences/extensions.tsx | 8 +-- .../components/+preferences/preferences.tsx | 19 +------ .../components/+preferences/telemetry.tsx | 4 +- 5 files changed, 64 insertions(+), 24 deletions(-) create mode 100644 src/renderer/components/+preferences/extension-settings.tsx diff --git a/src/renderer/components/+preferences/application.tsx b/src/renderer/components/+preferences/application.tsx index 9962176688..0fd1087953 100644 --- a/src/renderer/components/+preferences/application.tsx +++ b/src/renderer/components/+preferences/application.tsx @@ -30,6 +30,8 @@ import { isWindows } from "../../../common/vars"; import { FormSwitch, Switcher } from "../switch"; import moment from "moment-timezone"; import { CONSTANTS } from "../../../common/user-store/preferences-helpers"; +import { AppPreferenceRegistry } from "../../../extensions/registries"; +import { ExtensionSettings } from "./extension-settings"; const timezoneOptions: SelectOption[] = moment.tz.names().map(zone => ({ label: zone, @@ -50,6 +52,7 @@ export const Application = observer(() => { ); const [shell, setShell] = React.useState(UserStore.getInstance().shell || ""); + const extensionSettings = AppPreferenceRegistry.getInstance().getItems().filter((preference) => preference.showInPreferencesTab === "application"); return (
@@ -109,6 +112,10 @@ export const Application = observer(() => {
+ {extensionSettings.map(setting => ( + + ))} +
+
+ +
+
+
+ + ); +} diff --git a/src/renderer/components/+preferences/extensions.tsx b/src/renderer/components/+preferences/extensions.tsx index b19e494a16..3631eb9f58 100644 --- a/src/renderer/components/+preferences/extensions.tsx +++ b/src/renderer/components/+preferences/extensions.tsx @@ -22,16 +22,16 @@ import { observer } from "mobx-react"; import React from "react"; import { AppPreferenceRegistry } from "../../../extensions/registries"; -import { ExtensionSettings } from "./preferences"; +import { ExtensionSettings } from "./extension-settings"; export const Extensions = observer(() => { - const extensions = AppPreferenceRegistry.getInstance().getItems(); + const settings = AppPreferenceRegistry.getInstance().getItems(); return (

Extensions

- {extensions.filter(e => !e.showInPreferencesTab).map((extension) => - , + {settings.filter(e => !e.showInPreferencesTab).map((setting) => + , )}
); diff --git a/src/renderer/components/+preferences/preferences.tsx b/src/renderer/components/+preferences/preferences.tsx index 2b1a536b9f..ffa7121d1a 100644 --- a/src/renderer/components/+preferences/preferences.tsx +++ b/src/renderer/components/+preferences/preferences.tsx @@ -24,7 +24,6 @@ import { makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; import React from "react"; import { matchPath, Redirect, Route, RouteProps, Switch } from "react-router"; - import { appRoute, appURL, @@ -40,10 +39,9 @@ import { telemetryRoute, telemetryURL, } from "../../../common/routes"; -import { AppPreferenceRegistry, RegisteredAppPreference } from "../../../extensions/registries/app-preference-registry"; +import { AppPreferenceRegistry } from "../../../extensions/registries/app-preference-registry"; import { navigateWithoutHistoryChange, navigation } from "../../navigation"; import { SettingLayout } from "../layout/setting-layout"; -import { SubTitle } from "../layout/sub-title"; import { Tab, Tabs } from "../tabs"; import { Application } from "./application"; import { Kubernetes } from "./kubernetes"; @@ -105,18 +103,3 @@ export class Preferences extends React.Component { ); } } - -export function ExtensionSettings({ title, id, components: { Hint, Input }}: RegisteredAppPreference) { - return ( - -
- - -
- -
-
-
-
- ); -} diff --git a/src/renderer/components/+preferences/telemetry.tsx b/src/renderer/components/+preferences/telemetry.tsx index 5414382f92..4cec8bfccf 100644 --- a/src/renderer/components/+preferences/telemetry.tsx +++ b/src/renderer/components/+preferences/telemetry.tsx @@ -25,7 +25,7 @@ import { sentryDsn } from "../../../common/vars"; import { AppPreferenceRegistry } from "../../../extensions/registries"; import { Checkbox } from "../checkbox"; import { SubTitle } from "../layout/sub-title"; -import { ExtensionSettings } from "./preferences"; +import { ExtensionSettings } from "./extension-settings"; export const Telemetry = observer(() => { const extensions = AppPreferenceRegistry.getInstance().getItems(); @@ -34,7 +34,7 @@ export const Telemetry = observer(() => { return (

Telemetry

- {telemetryExtensions.map((extension) => )} + {telemetryExtensions.map((extension) => )} {sentryDsn ? (
From a711499bb6a60fb792c4fdd9ac3009f7c4e91ac1 Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Fri, 17 Dec 2021 17:28:26 +0200 Subject: [PATCH 03/12] Update ogre-tools for improved types of withInjectables (#4587) --- package.json | 4 +- .../components/+extensions/extensions.tsx | 56 +++++++-------- .../kube-object-menu/kube-object-menu.tsx | 70 +++++++++---------- yarn.lock | 18 ++--- 4 files changed, 68 insertions(+), 80 deletions(-) diff --git a/package.json b/package.json index 352ee617a8..52f521edb7 100644 --- a/package.json +++ b/package.json @@ -196,8 +196,8 @@ "@hapi/call": "^8.0.1", "@hapi/subtext": "^7.0.3", "@kubernetes/client-node": "^0.16.1", - "@ogre-tools/injectable": "^1.4.1", - "@ogre-tools/injectable-react": "^1.4.1", + "@ogre-tools/injectable": "1.5.0", + "@ogre-tools/injectable-react": "1.5.2", "@sentry/electron": "^2.5.4", "@sentry/integrations": "^6.15.0", "abort-controller": "^3.0.0", diff --git a/src/renderer/components/+extensions/extensions.tsx b/src/renderer/components/+extensions/extensions.tsx index 10438e4aca..0ba9deba65 100644 --- a/src/renderer/components/+extensions/extensions.tsx +++ b/src/renderer/components/+extensions/extensions.tsx @@ -49,34 +49,28 @@ import type { LensExtensionId } from "../../../extensions/lens-extension"; import installOnDropInjectable from "./install-on-drop/install-on-drop.injectable"; import { supportedExtensionFormats } from "./supported-extension-formats"; -interface Props { - dependencies: { - userExtensions: IComputedValue; - enableExtension: (id: LensExtensionId) => void; - disableExtension: (id: LensExtensionId) => void; - confirmUninstallExtension: (extension: InstalledExtension) => Promise; - installFromInput: (input: string) => Promise; - installFromSelectFileDialog: () => Promise; - installOnDrop: (files: File[]) => Promise; - }; +interface Dependencies { + userExtensions: IComputedValue; + enableExtension: (id: LensExtensionId) => void; + disableExtension: (id: LensExtensionId) => void; + confirmUninstallExtension: (extension: InstalledExtension) => Promise; + installFromInput: (input: string) => Promise; + installFromSelectFileDialog: () => Promise; + installOnDrop: (files: File[]) => Promise; } @observer -class NonInjectedExtensions extends React.Component { +class NonInjectedExtensions extends React.Component { @observable installPath = ""; - constructor(props: Props) { + constructor(props: Dependencies) { super(props); makeObservable(this); } - - get dependencies() { - return this.props.dependencies; - } componentDidMount() { disposeOnUnmount(this, [ - reaction(() => this.dependencies.userExtensions.get().length, (curSize, prevSize) => { + reaction(() => this.props.userExtensions.get().length, (curSize, prevSize) => { if (curSize > prevSize) { disposeOnUnmount(this, [ when(() => !ExtensionInstallationStateStore.anyInstalling, () => this.installPath = ""), @@ -87,10 +81,10 @@ class NonInjectedExtensions extends React.Component { } render() { - const userExtensions = this.dependencies.userExtensions.get(); + const userExtensions = this.props.userExtensions.get(); return ( - +

Extensions

@@ -106,8 +100,8 @@ class NonInjectedExtensions extends React.Component { (this.installPath = value)} - installFromInput={() => this.dependencies.installFromInput(this.installPath)} - installFromSelectFileDialog={this.dependencies.installFromSelectFileDialog} + installFromInput={() => this.props.installFromInput(this.installPath)} + installFromSelectFileDialog={this.props.installFromSelectFileDialog} installPath={this.installPath} /> @@ -115,9 +109,9 @@ class NonInjectedExtensions extends React.Component {
@@ -126,10 +120,10 @@ class NonInjectedExtensions extends React.Component { } } - -export const Extensions = withInjectables(NonInjectedExtensions, { - getProps: di => ({ - dependencies: { +export const Extensions = withInjectables( + NonInjectedExtensions, + { + getProps: (di) => ({ userExtensions: di.inject(userExtensionsInjectable), enableExtension: di.inject(enableExtensionInjectable), disableExtension: di.inject(disableExtensionInjectable), @@ -140,6 +134,6 @@ export const Extensions = withInjectables(NonInjectedExtensions, { installFromSelectFileDialog: di.inject( installFromSelectFileDialogInjectable, ), - }, - }), -}); + }), + }, +); diff --git a/src/renderer/components/kube-object-menu/kube-object-menu.tsx b/src/renderer/components/kube-object-menu/kube-object-menu.tsx index 989094023f..29c822be89 100644 --- a/src/renderer/components/kube-object-menu/kube-object-menu.tsx +++ b/src/renderer/components/kube-object-menu/kube-object-menu.tsx @@ -32,38 +32,28 @@ import hideDetailsInjectable from "./dependencies/hide-details.injectable"; import kubeObjectMenuItemsInjectable from "./dependencies/kube-object-menu-items/kube-object-menu-items.injectable"; import apiManagerInjectable from "./dependencies/api-manager.injectable"; -// TODO: Replace with KubeObjectMenuProps2 -export interface KubeObjectMenuProps extends MenuActionsProps { +export interface KubeObjectMenuProps extends MenuActionsProps { object: TKubeObject | null | undefined; editable?: boolean; removable?: boolean; } -interface KubeObjectMenuProps2 extends MenuActionsProps { - object: KubeObject | null | undefined; - editable?: boolean; - removable?: boolean; - - dependencies: { - apiManager: ApiManager; - kubeObjectMenuItems: React.ElementType[]; - clusterName: string; - hideDetails: () => void; - editResourceTab: (kubeObject: KubeObject) => void; - }; +interface Dependencies { + apiManager: ApiManager; + kubeObjectMenuItems: React.ElementType[]; + clusterName: string; + hideDetails: () => void; + editResourceTab: (kubeObject: KubeObject) => void; } -class NonInjectedKubeObjectMenu extends React.Component { - get dependencies() { - return this.props.dependencies; - } - +class NonInjectedKubeObjectMenu extends React.Component & Dependencies> { + get store() { const { object } = this.props; if (!object) return null; - return this.props.dependencies.apiManager.getStore(object.selfLink); + return this.props.apiManager.getStore(object.selfLink); } get isEditable() { @@ -76,13 +66,13 @@ class NonInjectedKubeObjectMenu extends React.Component { @boundMethod async update() { - this.props.dependencies.hideDetails(); - this.props.dependencies.editResourceTab(this.props.object); + this.props.hideDetails(); + this.props.editResourceTab(this.props.object); } @boundMethod async remove() { - this.props.dependencies.hideDetails(); + this.props.hideDetails(); const { object, removeAction } = this.props; if (removeAction) await removeAction(); @@ -103,7 +93,7 @@ class NonInjectedKubeObjectMenu extends React.Component { return (

- Remove {object.kind} {breadcrumb} from {this.dependencies.clusterName}? + Remove {object.kind} {breadcrumb} from {this.props.clusterName}?

); } @@ -111,7 +101,7 @@ class NonInjectedKubeObjectMenu extends React.Component { getMenuItems(): React.ReactChild[] { const { object, toolbar } = this.props; - return this.props.dependencies.kubeObjectMenuItems.map((MenuItem, index) => ( + return this.props.kubeObjectMenuItems.map((MenuItem, index) => ( )); } @@ -134,19 +124,23 @@ class NonInjectedKubeObjectMenu extends React.Component { } } -export const KubeObjectMenu = withInjectables(NonInjectedKubeObjectMenu, { - getProps: (di, props) => ({ - dependencies: { - clusterName: di.inject(clusterNameInjectable), - apiManager: di.inject(apiManagerInjectable), - editResourceTab: di.inject(editResourceTabInjectable), - hideDetails: di.inject(hideDetailsInjectable), +export function KubeObjectMenu( + props: KubeObjectMenuProps, +) { + return withInjectables>( + NonInjectedKubeObjectMenu, + { + getProps: (di, props) => ({ + clusterName: di.inject(clusterNameInjectable), + apiManager: di.inject(apiManagerInjectable), + editResourceTab: di.inject(editResourceTabInjectable), + hideDetails: di.inject(hideDetailsInjectable), - kubeObjectMenuItems: di.inject(kubeObjectMenuItemsInjectable, { - kubeObject: props.object, + kubeObjectMenuItems: di.inject(kubeObjectMenuItemsInjectable, { + kubeObject: props.object, + }), + ...props, }), }, - - ...props, - }), -}); + )(props); +} diff --git a/yarn.lock b/yarn.lock index 5e1c8442aa..952789bf84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -979,19 +979,19 @@ dependencies: lodash "^4.17.21" -"@ogre-tools/injectable-react@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-1.4.1.tgz#48d8633462189939292596a66631d6717e39e47f" - integrity sha512-SRk3QXvFCEQk4MeVG8TAomGcOt0Pf06hZ5kBh+iNIug3FLYeyWagH6OSVylZRu4u2Izd89J0taS1GmSfYDoHaA== +"@ogre-tools/injectable-react@1.5.2": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-1.5.2.tgz#7df925ca5abda86210f527333774ddbf027f0693" + integrity sha512-n26NyGLYjwyIbfwsLj1tg0uNPK6SI/H0vGisMNpNfrcdci2QiLQBSKemnLMMRn7LF/+JhA/NQClTetiMkcSCuw== dependencies: "@ogre-tools/fp" "^1.4.0" - "@ogre-tools/injectable" "^1.4.1" + "@ogre-tools/injectable" "^1.5.0" lodash "^4.17.21" -"@ogre-tools/injectable@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-1.4.1.tgz#45414c6e13c870d7d84f4fa8e0dd67b33f6cc23e" - integrity sha512-vX4QXS/2d3g7oUenOKcv3mZRnJ5XewUMPsSsELjCyhL2caJlD0eB9J7y3y0eeFu/I18L8GC3DRs9o3QNshwN5Q== +"@ogre-tools/injectable@1.5.0", "@ogre-tools/injectable@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-1.5.0.tgz#edf911f360e73bb5f10ac669f147108d1465fc45" + integrity sha512-k0Wgc8QqB+p/gHcWPVWJV8N8xX5cMpagEjZ1C5bPVeRyB+73od/yHgb1HOgL2pPzlE6qOtTdkiUrWuTAoqnqUw== dependencies: "@ogre-tools/fp" "^1.4.0" lodash "^4.17.21" From 78678bdf2f03b3add559d9c9ef78a20908f04ee8 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 17 Dec 2021 10:28:59 -0500 Subject: [PATCH 04/12] Add support for customizing the extension install registry URL (#4503) --- src/common/user-store/preferences-helpers.ts | 31 +++++++++ src/common/user-store/user-store.ts | 5 +- src/common/utils/index.ts | 1 + .../attempt-install-by-info.tsx | 41 +++++++----- .../components/+extensions/extensions.tsx | 4 ++ .../get-base-registry-url.injectable.ts | 36 ++++++++++ .../get-base-registry-url.tsx | 57 ++++++++++++++++ .../components/+preferences/application.tsx | 65 +++++++++++++++---- 8 files changed, 208 insertions(+), 32 deletions(-) create mode 100644 src/renderer/components/+extensions/get-base-registry-url/get-base-registry-url.injectable.ts create mode 100644 src/renderer/components/+extensions/get-base-registry-url/get-base-registry-url.tsx diff --git a/src/common/user-store/preferences-helpers.ts b/src/common/user-store/preferences-helpers.ts index 1f6050017e..450c75ae89 100644 --- a/src/common/user-store/preferences-helpers.ts +++ b/src/common/user-store/preferences-helpers.ts @@ -306,6 +306,36 @@ const updateChannel: PreferenceDescription = { }, }; +export enum ExtensionRegistryLocation { + DEFAULT = "default", + NPMRC = "npmrc", + CUSTOM = "custom", +} +export type ExtensionRegistry = { + location: ExtensionRegistryLocation.DEFAULT | ExtensionRegistryLocation.NPMRC; + customUrl?: undefined; +} | { + location: ExtensionRegistryLocation.CUSTOM, + customUrl: string; +}; + +export const defaultExtensionRegistryUrl = "https://registry.npmjs.org"; + +const extensionRegistryUrl: PreferenceDescription = { + fromStore(val) { + return val ?? { + location: ExtensionRegistryLocation.DEFAULT, + }; + }, + toStore(val) { + if (val.location === ExtensionRegistryLocation.DEFAULT) { + return undefined; + } + + return val; + }, +}; + type PreferencesModelType = typeof DESCRIPTORS[field] extends PreferenceDescription ? T : never; type UserStoreModelType = typeof DESCRIPTORS[field] extends PreferenceDescription ? T : never; @@ -335,6 +365,7 @@ export const DESCRIPTORS = { editorConfiguration, terminalCopyOnSelect, updateChannel, + extensionRegistryUrl, }; export const CONSTANTS = { diff --git a/src/common/user-store/user-store.ts b/src/common/user-store/user-store.ts index 3de94113d6..31238f0ded 100644 --- a/src/common/user-store/user-store.ts +++ b/src/common/user-store/user-store.ts @@ -29,7 +29,7 @@ import { kubeConfigDefaultPath } from "../kube-helpers"; import { appEventBus } from "../event-bus"; import path from "path"; import { ObservableToggleSet, toJS } from "../../renderer/utils"; -import { DESCRIPTORS, EditorConfiguration, KubeconfigSyncValue, UserPreferencesModel } from "./preferences-helpers"; +import { DESCRIPTORS, EditorConfiguration, ExtensionRegistry, KubeconfigSyncValue, UserPreferencesModel } from "./preferences-helpers"; import logger from "../../main/logger"; import { AppPaths } from "../app-paths"; @@ -75,6 +75,7 @@ export class UserStore extends BaseStore /* implements UserStore @observable kubectlBinariesPath?: string; @observable terminalCopyOnSelect: boolean; @observable updateChannel?: string; + @observable extensionRegistryUrl: ExtensionRegistry; /** * Download kubectl binaries matching cluster version @@ -201,6 +202,7 @@ export class UserStore extends BaseStore /* implements UserStore this.editorConfiguration = DESCRIPTORS.editorConfiguration.fromStore(preferences?.editorConfiguration); this.terminalCopyOnSelect = DESCRIPTORS.terminalCopyOnSelect.fromStore(preferences?.terminalCopyOnSelect); this.updateChannel = DESCRIPTORS.updateChannel.fromStore(preferences?.updateChannel); + this.extensionRegistryUrl = DESCRIPTORS.extensionRegistryUrl.fromStore(preferences?.extensionRegistryUrl); } toJSON(): UserStoreModel { @@ -224,6 +226,7 @@ export class UserStore extends BaseStore /* implements UserStore editorConfiguration: DESCRIPTORS.editorConfiguration.toStore(this.editorConfiguration), terminalCopyOnSelect: DESCRIPTORS.terminalCopyOnSelect.toStore(this.terminalCopyOnSelect), updateChannel: DESCRIPTORS.updateChannel.toStore(this.updateChannel), + extensionRegistryUrl: DESCRIPTORS.extensionRegistryUrl.toStore(this.extensionRegistryUrl), }, }; diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index 733867f1cb..ea2f742bc7 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -48,6 +48,7 @@ export * from "./n-fircate"; export * from "./objects"; export * from "./openExternal"; export * from "./paths"; +export * from "./promise-exec"; export * from "./reject-promise"; export * from "./singleton"; export * from "./sort-compare"; diff --git a/src/renderer/components/+extensions/attempt-install-by-info/attempt-install-by-info.tsx b/src/renderer/components/+extensions/attempt-install-by-info/attempt-install-by-info.tsx index bc5f54c147..c898fa0cc0 100644 --- a/src/renderer/components/+extensions/attempt-install-by-info/attempt-install-by-info.tsx +++ b/src/renderer/components/+extensions/attempt-install-by-info/attempt-install-by-info.tsx @@ -36,32 +36,39 @@ export interface ExtensionInfo { } export interface Dependencies { - attemptInstall: (request: InstallRequest, d: ExtendableDisposer) => Promise + attemptInstall: (request: InstallRequest, d: ExtendableDisposer) => Promise; + getBaseRegistryUrl: () => Promise; } -export const attemptInstallByInfo = ({ attemptInstall }: Dependencies) => async ({ +export const attemptInstallByInfo = ({ attemptInstall, getBaseRegistryUrl }: Dependencies) => async ({ name, version, requireConfirmation = false, }: ExtensionInfo) => { const disposer = ExtensionInstallationStateStore.startPreInstall(); - const registryUrl = new URLParse("https://registry.npmjs.com") - .set("pathname", name) - .toString(); - const { promise } = downloadJson({ url: registryUrl }); - const json = await promise.catch(console.error); + const baseUrl = await getBaseRegistryUrl(); + const registryUrl = new URLParse(baseUrl).set("pathname", name).toString(); + let json: any; - if ( - !json || - json.error || - typeof json.versions !== "object" || - !json.versions - ) { - const message = json?.error ? `: ${json.error}` : ""; + try { + json = await downloadJson({ url: registryUrl }).promise; - Notifications.error( - `Failed to get registry information for that extension${message}`, - ); + if (!json || json.error || typeof json.versions !== "object" || !json.versions) { + const message = json?.error ? `: ${json.error}` : ""; + + Notifications.error(`Failed to get registry information for that extension${message}`); + + return disposer(); + } + } catch (error) { + if (error instanceof SyntaxError) { + // assume invalid JSON + console.warn("Set registry has invalid json", { url: baseUrl }, error); + Notifications.error("Failed to get valid registry information for that extension. Registry did not return valid JSON"); + } else { + console.error("Failed to download registry information", error); + Notifications.error(`Failed to get valid registry information for that extension. ${error}`); + } return disposer(); } diff --git a/src/renderer/components/+extensions/extensions.tsx b/src/renderer/components/+extensions/extensions.tsx index 0ba9deba65..838635660d 100644 --- a/src/renderer/components/+extensions/extensions.tsx +++ b/src/renderer/components/+extensions/extensions.tsx @@ -68,6 +68,10 @@ class NonInjectedExtensions extends React.Component { makeObservable(this); } + get dependencies() { + return this.props.dependencies; + } + componentDidMount() { disposeOnUnmount(this, [ reaction(() => this.props.userExtensions.get().length, (curSize, prevSize) => { diff --git a/src/renderer/components/+extensions/get-base-registry-url/get-base-registry-url.injectable.ts b/src/renderer/components/+extensions/get-base-registry-url/get-base-registry-url.injectable.ts new file mode 100644 index 0000000000..7f46d4a7b8 --- /dev/null +++ b/src/renderer/components/+extensions/get-base-registry-url/get-base-registry-url.injectable.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { Injectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { UserStore } from "../../../../common/user-store"; +import { Dependencies, getBaseRegistryUrl } from "./get-base-registry-url"; + +const getBaseRegistryUrlInjectable: Injectable<() => Promise, Dependencies> = { + getDependencies: () => ({ + // TODO: use injection + getRegistryUrlPreference: () => UserStore.getInstance().extensionRegistryUrl, + }), + + instantiate: getBaseRegistryUrl, + lifecycle: lifecycleEnum.singleton, +}; + +export default getBaseRegistryUrlInjectable; diff --git a/src/renderer/components/+extensions/get-base-registry-url/get-base-registry-url.tsx b/src/renderer/components/+extensions/get-base-registry-url/get-base-registry-url.tsx new file mode 100644 index 0000000000..0d012ccf55 --- /dev/null +++ b/src/renderer/components/+extensions/get-base-registry-url/get-base-registry-url.tsx @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import React from "react"; +import { defaultExtensionRegistryUrl, ExtensionRegistry, ExtensionRegistryLocation } from "../../../../common/user-store/preferences-helpers"; +import { promiseExecFile } from "../../../utils"; +import { Notifications } from "../../notifications"; + +export interface Dependencies { + getRegistryUrlPreference: () => ExtensionRegistry, +} + +export const getBaseRegistryUrl = ({ getRegistryUrlPreference }: Dependencies) => async () => { + const extensionRegistryUrl = getRegistryUrlPreference(); + + switch (extensionRegistryUrl.location) { + case ExtensionRegistryLocation.CUSTOM: + return extensionRegistryUrl.customUrl; + + case ExtensionRegistryLocation.NPMRC: { + try { + const filteredEnv = Object.fromEntries( + Object.entries(process.env) + .filter(([key]) => !key.startsWith("npm")), + ); + const { stdout } = await promiseExecFile("npm", ["config", "get", "registry"], { env: filteredEnv }); + + return stdout.trim(); + } catch (error) { + Notifications.error(

Failed to get configured registry from .npmrc. Falling back to default registry

); + console.warn("[EXTENSIONS]: failed to get configured registry from .npmrc", error); + // fallthrough + } + } + default: + case ExtensionRegistryLocation.DEFAULT: + return defaultExtensionRegistryUrl; + } +}; diff --git a/src/renderer/components/+preferences/application.tsx b/src/renderer/components/+preferences/application.tsx index 0fd1087953..40859cd5e0 100644 --- a/src/renderer/components/+preferences/application.tsx +++ b/src/renderer/components/+preferences/application.tsx @@ -29,7 +29,9 @@ import { Input } from "../input"; import { isWindows } from "../../../common/vars"; import { FormSwitch, Switcher } from "../switch"; import moment from "moment-timezone"; -import { CONSTANTS } from "../../../common/user-store/preferences-helpers"; +import { CONSTANTS, defaultExtensionRegistryUrl, ExtensionRegistryLocation } from "../../../common/user-store/preferences-helpers"; +import { action } from "mobx"; +import { isUrl } from "../input/input_validators"; import { AppPreferenceRegistry } from "../../../extensions/registries"; import { ExtensionSettings } from "./extension-settings"; @@ -43,6 +45,7 @@ const updateChannelOptions: SelectOption[] = Array.from( ); export const Application = observer(() => { + const userStore = UserStore.getInstance(); const defaultShell = process.env.SHELL || process.env.PTYSHELL || ( @@ -51,7 +54,8 @@ export const Application = observer(() => { : "System default shell" ); - const [shell, setShell] = React.useState(UserStore.getInstance().shell || ""); + const [customUrl, setCustomUrl] = React.useState(userStore.extensionRegistryUrl.customUrl || ""); + const [shell, setShell] = React.useState(userStore.shell || ""); const extensionSettings = AppPreferenceRegistry.getInstance().getItems().filter((preference) => preference.showInPreferencesTab === "application"); return ( @@ -61,8 +65,8 @@ export const Application = observer(() => { { + userStore.extensionRegistryUrl.location = value; + + if (userStore.extensionRegistryUrl.location === ExtensionRegistryLocation.CUSTOM) { + userStore.extensionRegistryUrl.customUrl = ""; + } + })} + themeName="lens" + /> +

+ This setting is to change the registry URL for installing extensions by name.{" "} + If you are unable to access the default registry ({defaultExtensionRegistryUrl}){" "} + you can change it in your .npmrc file or in the input below. +

+ + userStore.extensionRegistryUrl.customUrl = customUrl} + placeholder="Custom Extension Registry URL..." + disabled={userStore.extensionRegistryUrl.location !== ExtensionRegistryLocation.CUSTOM} + /> +
+ +
+
UserStore.getInstance().openAtLogin = v.target.checked} + checked={userStore.openAtLogin} + onChange={v => userStore.openAtLogin = v.target.checked} name="startup" /> } @@ -120,8 +157,8 @@ export const Application = observer(() => { UserStore.getInstance().setLocaleTimezone(value)} + value={userStore.localeTimezone} + onChange={({ value }) => userStore.setLocaleTimezone(value)} themeName="lens" />
From e9d99d8485d46be1bc6786ceffafbc4d5cbd954f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 17 Dec 2021 10:29:09 -0500 Subject: [PATCH 05/12] Replace all uses of promiseExec with promiseExecFile (#4514) --- .../__tests__/app-preferences.tests.ts | 3 +- src/common/system-ca.ts | 22 +- src/common/utils/promise-exec.ts | 3 +- src/main/helm/helm-release-manager.ts | 264 +++++++++++------- src/main/helm/helm-repo-manager.ts | 104 ++++--- src/main/helm/helm-service.ts | 16 +- src/main/kubectl.ts | 9 +- .../+preferences/add-helm-repo-dialog.tsx | 4 +- 8 files changed, 270 insertions(+), 155 deletions(-) diff --git a/integration/__tests__/app-preferences.tests.ts b/integration/__tests__/app-preferences.tests.ts index 47dffd6a90..8cdf19a548 100644 --- a/integration/__tests__/app-preferences.tests.ts +++ b/integration/__tests__/app-preferences.tests.ts @@ -71,7 +71,8 @@ describe("preferences page tests", () => { } }, 10*60*1000); - utils.itIf(process.platform !== "win32")("ensures helm repos", async () => { + // Skipping, but will turn it on again in the follow up PR + it.skip("ensures helm repos", async () => { await window.click("[data-testid=kubernetes-tab]"); await window.waitForSelector("[data-testid=repository-name]", { timeout: 140_000, diff --git a/src/common/system-ca.ts b/src/common/system-ca.ts index 22d85e3b42..bc7231a5f6 100644 --- a/src/common/system-ca.ts +++ b/src/common/system-ca.ts @@ -22,7 +22,7 @@ import { isMac, isWindows } from "./vars"; import wincaAPI from "win-ca/api"; import https from "https"; -import { promiseExec } from "./utils/promise-exec"; +import { promiseExecFile } from "./utils/promise-exec"; // DST Root CA X3, which was expired on 9.30.2021 export const DSTRootCAX3 = "-----BEGIN CERTIFICATE-----\nMIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\nMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\nDkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\nPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\nEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\nrz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\nOLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\nxiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\n7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\naeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\nHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\nSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\nikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\nAvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\nR8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\nJDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\nOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n-----END CERTIFICATE-----\n"; @@ -33,19 +33,25 @@ export function isCertActive(cert: string) { return !isExpired; } +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet#other_assertions +const certSplitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g; + +async function execSecurity(...args: string[]): Promise { + const { stdout } = await promiseExecFile("/usr/bin/security", args); + + return stdout.split(certSplitPattern); +} + /** * Get root CA certificate from MacOSX system keychain * Only return non-expred certificates. */ export async function getMacRootCA() { // inspired mac-ca https://github.com/jfromaniello/mac-ca - const args = "find-certificate -a -p"; - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet#other_assertions - const splitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g; - const systemRootCertsPath = "/System/Library/Keychains/SystemRootCertificates.keychain"; - const bin = "/usr/bin/security"; - const trusted = (await promiseExec(`${bin} ${args}`)).stdout.toString().split(splitPattern); - const rootCA = (await promiseExec(`${bin} ${args} ${systemRootCertsPath}`)).stdout.toString().split(splitPattern); + const [trusted, rootCA] = await Promise.all([ + execSecurity("find-certificate", "-a", "-p"), + execSecurity("find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain"), + ]); return [...new Set([...trusted, ...rootCA])].filter(isCertActive); } diff --git a/src/common/utils/promise-exec.ts b/src/common/utils/promise-exec.ts index 8b9d5b72cc..b2d39ee110 100644 --- a/src/common/utils/promise-exec.ts +++ b/src/common/utils/promise-exec.ts @@ -20,7 +20,6 @@ */ import * as util from "util"; -import { exec, execFile } from "child_process"; +import { execFile } from "child_process"; -export const promiseExec = util.promisify(exec); export const promiseExecFile = util.promisify(execFile); diff --git a/src/main/helm/helm-release-manager.ts b/src/main/helm/helm-release-manager.ts index 4f96562e5d..5a5f1e6ef2 100644 --- a/src/main/helm/helm-release-manager.ts +++ b/src/main/helm/helm-release-manager.ts @@ -22,161 +22,229 @@ import * as tempy from "tempy"; import fse from "fs-extra"; import * as yaml from "js-yaml"; -import { promiseExec } from "../../common/utils/promise-exec"; +import { promiseExecFile } from "../../common/utils/promise-exec"; import { helmCli } from "./helm-cli"; -import type { Cluster } from "../cluster"; import { toCamelCase } from "../../common/utils/camelCase"; +import type { BaseEncodingOptions } from "fs"; +import { execFile, ExecFileOptions } from "child_process"; -export async function listReleases(pathToKubeconfig: string, namespace?: string) { - const helm = await helmCli.binaryPath(); - const namespaceFlag = namespace ? `-n ${namespace}` : "--all-namespaces"; +async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise { + const helmCliPath = await helmCli.binaryPath(); try { - const { stdout } = await promiseExec(`"${helm}" ls --output json ${namespaceFlag} --kubeconfig ${pathToKubeconfig}`); - const output = JSON.parse(stdout); + const { stdout } = await promiseExecFile(helmCliPath, args, options); - if (output.length == 0) { - return output; - } - output.forEach((release: any, index: number) => { - output[index] = toCamelCase(release); - }); - - return output; + return stdout; } catch (error) { throw error?.stderr || error; } } +export async function listReleases(pathToKubeconfig: string, namespace?: string): Promise[]> { + const args = [ + "ls", + "--output", "json", + ]; -export async function installChart(chart: string, values: any, name: string | undefined, namespace: string, version: string, pathToKubeconfig: string) { - const helm = await helmCli.binaryPath(); - const fileName = tempy.file({ name: "values.yaml" }); + if (namespace) { + args.push("-n", namespace); + } else { + args.push("--all-namespaces"); + } - await fse.writeFile(fileName, yaml.dump(values)); + args.push("--kubeconfig", pathToKubeconfig); + + const output = JSON.parse(await execHelm(args)); + + if (!Array.isArray(output) || output.length == 0) { + return []; + } + + return output.map(toCamelCase); +} + + +export async function installChart(chart: string, values: any, name: string | undefined = "", namespace: string, version: string, kubeconfigPath: string) { + const valuesFilePath = tempy.file({ name: "values.yaml" }); + + await fse.writeFile(valuesFilePath, yaml.dump(values)); + + const args = ["install"]; + + if (name) { + args.push(name); + } + + args.push( + chart, + "--version", version, + "--values", valuesFilePath, + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + ); + + if (!name) { + args.push("--generate-name"); + } try { - let generateName = ""; - - if (!name) { - generateName = "--generate-name"; - name = ""; - } - const { stdout } = await promiseExec(`"${helm}" install ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} ${generateName}`); - const releaseName = stdout.split("\n")[0].split(" ")[1].trim(); + const output = await execHelm(args); + const releaseName = output.split("\n")[0].split(" ")[1].trim(); return { - log: stdout, + log: output, release: { name: releaseName, namespace, }, }; - } catch (error) { - throw error?.stderr || error; } finally { - await fse.unlink(fileName); + await fse.unlink(valuesFilePath); } } -export async function upgradeRelease(name: string, chart: string, values: any, namespace: string, version: string, cluster: Cluster) { - const helm = await helmCli.binaryPath(); - const fileName = tempy.file({ name: "values.yaml" }); +export async function upgradeRelease(name: string, chart: string, values: any, namespace: string, version: string, kubeconfigPath: string, kubectlPath: string) { + const valuesFilePath = tempy.file({ name: "values.yaml" }); - await fse.writeFile(fileName, yaml.dump(values)); + await fse.writeFile(valuesFilePath, yaml.dump(values)); + + const args = [ + "upgrade", + name, + chart, + "--version", version, + "--values", valuesFilePath, + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + ]; try { - const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); - const { stdout } = await promiseExec(`"${helm}" upgrade ${name} ${chart} --version ${version} -f ${fileName} --namespace ${namespace} --kubeconfig ${proxyKubeconfig}`); + const output = await execHelm(args); return { - log: stdout, - release: getRelease(name, namespace, cluster), + log: output, + release: getRelease(name, namespace, kubeconfigPath, kubectlPath), }; - } catch (error) { - throw error?.stderr || error; } finally { - await fse.unlink(fileName); + await fse.unlink(valuesFilePath); } } -export async function getRelease(name: string, namespace: string, cluster: Cluster) { - try { - const helm = await helmCli.binaryPath(); - const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); +export async function getRelease(name: string, namespace: string, kubeconfigPath: string, kubectlPath: string) { + const args = [ + "status", + name, + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + "--output", "json", + ]; - const { stdout } = await promiseExec(`"${helm}" status ${name} --output json --namespace ${namespace} --kubeconfig ${proxyKubeconfig}`, { - maxBuffer: 32 * 1024 * 1024 * 1024, // 32 MiB - }); - const release = JSON.parse(stdout); + const release = JSON.parse(await execHelm(args, { + maxBuffer: 32 * 1024 * 1024 * 1024, // 32 MiB + })); - release.resources = await getResources(name, namespace, cluster); + release.resources = await getResources(name, namespace, kubeconfigPath, kubectlPath); - return release; - } catch (error) { - throw error?.stderr || error; - } + return release; } -export async function deleteRelease(name: string, namespace: string, pathToKubeconfig: string) { - try { - const helm = await helmCli.binaryPath(); - const { stdout } = await promiseExec(`"${helm}" delete ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`); - - return stdout; - } catch (error) { - throw error?.stderr || error; - } +export async function deleteRelease(name: string, namespace: string, kubeconfigPath: string) { + return execHelm([ + "delete", + name, + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + ]); } interface GetValuesOptions { namespace: string; all?: boolean; - pathToKubeconfig: string; + kubeconfigPath: string; } -export async function getValues(name: string, { namespace, all = false, pathToKubeconfig }: GetValuesOptions) { - try { - const helm = await helmCli.binaryPath(); - const { stdout } = await promiseExec(`"${helm}" get values ${name} ${all ? "--all" : ""} --output yaml --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`); +export async function getValues(name: string, { namespace, all = false, kubeconfigPath }: GetValuesOptions) { + const args = [ + "get", + "values", + name, + ]; - return stdout; - } catch (error) { - throw error?.stderr || error; + if (all) { + args.push("--all"); } + + args.push( + "--output", "yaml", + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + ); + + return execHelm(args); } -export async function getHistory(name: string, namespace: string, pathToKubeconfig: string) { - try { - const helm = await helmCli.binaryPath(); - const { stdout } = await promiseExec(`"${helm}" history ${name} --output json --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`); - - return JSON.parse(stdout); - } catch (error) { - throw error?.stderr || error; - } +export async function getHistory(name: string, namespace: string, kubeconfigPath: string) { + return JSON.parse(await execHelm([ + "history", + name, + "--output", "json", + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + ])); } -export async function rollback(name: string, namespace: string, revision: number, pathToKubeconfig: string) { - try { - const helm = await helmCli.binaryPath(); - const { stdout } = await promiseExec(`"${helm}" rollback ${name} ${revision} --namespace ${namespace} --kubeconfig ${pathToKubeconfig}`); - - return stdout; - } catch (error) { - throw error?.stderr || error; - } +export async function rollback(name: string, namespace: string, revision: number, kubeconfigPath: string) { + return JSON.parse(await execHelm([ + "rollback", + name, + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + ])); } -async function getResources(name: string, namespace: string, cluster: Cluster) { - try { - const helm = await helmCli.binaryPath(); - const kubectl = await cluster.ensureKubectl(); - const kubectlPath = await kubectl.getPath(); - const pathToKubeconfig = await cluster.getProxyKubeconfigPath(); - const { stdout } = await promiseExec(`"${helm}" get manifest ${name} --namespace ${namespace} --kubeconfig ${pathToKubeconfig} | "${kubectlPath}" get -n ${namespace} --kubeconfig ${pathToKubeconfig} -f - -o=json`); +async function getResources(name: string, namespace: string, kubeconfigPath: string, kubectlPath: string) { + const helmArgs = [ + "get", + "manifest", + name, + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + ]; + const kubectlArgs = [ + "get", + "--namespace", namespace, + "--kubeconfig", kubeconfigPath, + "-f", "-", + "--output", "json", + ]; - return JSON.parse(stdout).items; + try { + const helmOutput = await execHelm(helmArgs); + + return new Promise((resolve, reject) => { + let stdout = ""; + let stderr = ""; + const kubectl = execFile(kubectlPath, kubectlArgs); + + kubectl + .on("exit", (code, signal) => { + if (typeof code === "number") { + if (code === 0) { + resolve(JSON.parse(stdout).items); + } else { + reject(stderr); + } + } else { + reject(new Error(`Kubectl exited with signal ${signal}`)); + } + }) + .on("error", reject); + + kubectl.stderr.on("data", output => stderr += output); + kubectl.stdout.on("data", output => stdout += output); + kubectl.stdin.write(helmOutput); + kubectl.stdin.end(); + }); } catch { return []; } diff --git a/src/main/helm/helm-repo-manager.ts b/src/main/helm/helm-repo-manager.ts index 1c36bef2be..f8edc12e00 100644 --- a/src/main/helm/helm-repo-manager.ts +++ b/src/main/helm/helm-repo-manager.ts @@ -20,13 +20,14 @@ */ import yaml from "js-yaml"; -import { readFile } from "fs-extra"; -import { promiseExec } from "../../common/utils/promise-exec"; +import { BaseEncodingOptions, readFile } from "fs-extra"; +import { promiseExecFile } from "../../common/utils/promise-exec"; import { helmCli } from "./helm-cli"; import { Singleton } from "../../common/utils/singleton"; import { customRequestPromise } from "../../common/request"; import orderBy from "lodash/orderBy"; import logger from "../logger"; +import type { ExecFileOptions } from "child_process"; export type HelmEnv = Record & { HELM_REPOSITORY_CACHE?: string; @@ -49,6 +50,18 @@ export interface HelmRepo { password?: string, } +async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFileOptions): Promise { + const helmCliPath = await helmCli.binaryPath(); + + try { + const { stdout } = await promiseExecFile(helmCliPath, args, options); + + return stdout; + } catch (error) { + throw error?.stderr || error; + } +} + export class HelmRepoManager extends Singleton { protected repos: HelmRepo[]; protected helmEnv: HelmEnv; @@ -77,11 +90,8 @@ export class HelmRepoManager extends Singleton { } protected static async parseHelmEnv() { - const helm = await helmCli.binaryPath(); - const { stdout } = await promiseExec(`"${helm}" env`).catch((error) => { - throw(error.stderr); - }); - const lines = stdout.split(/\r?\n/); // split by new line feed + const output = await execHelm(["env"]); + const lines = output.split(/\r?\n/); // split by new line feed const env: HelmEnv = {}; lines.forEach((line: string) => { @@ -135,57 +145,73 @@ export class HelmRepoManager extends Singleton { cacheFilePath: `${this.helmEnv.HELM_REPOSITORY_CACHE}/${repo.name}-index.yaml`, })); } catch (error) { - logger.error(`[HELM]: repositories listing error "${error}"`); + logger.error(`[HELM]: repositories listing error`, error); return []; } } public static async update() { - const helm = await helmCli.binaryPath(); - const { stdout } = await promiseExec(`"${helm}" repo update`).catch((error) => { - return { stdout: error.stdout }; - }); - - return stdout; + return execHelm([ + "repo", + "update", + ]); } public static async addRepo({ name, url }: HelmRepo) { logger.info(`[HELM]: adding repo "${name}" from ${url}`); - const helm = await helmCli.binaryPath(); - const { stdout } = await promiseExec(`"${helm}" repo add ${name} ${url}`).catch((error) => { - throw(error.stderr); - }); - return stdout; + return execHelm([ + "repo", + "add", + name, + url, + ]); } - public static async addCustomRepo(repoAttributes : HelmRepo) { - logger.info(`[HELM]: adding repo "${repoAttributes.name}" from ${repoAttributes.url}`); - const helm = await helmCli.binaryPath(); + public static async addCustomRepo({ name, url, insecureSkipTlsVerify, username, password, caFile, keyFile, certFile }: HelmRepo) { + logger.info(`[HELM]: adding repo ${name} from ${url}`); + const args = [ + "repo", + "add", + name, + url, + ]; - const insecureSkipTlsVerify = repoAttributes.insecureSkipTlsVerify ? " --insecure-skip-tls-verify" : ""; - const username = repoAttributes.username ? ` --username "${repoAttributes.username}"` : ""; - const password = repoAttributes.password ? ` --password "${repoAttributes.password}"` : ""; - const caFile = repoAttributes.caFile ? ` --ca-file "${repoAttributes.caFile}"` : ""; - const keyFile = repoAttributes.keyFile ? ` --key-file "${repoAttributes.keyFile}"` : ""; - const certFile = repoAttributes.certFile ? ` --cert-file "${repoAttributes.certFile}"` : ""; + if (insecureSkipTlsVerify) { + args.push("--insecure-skip-tls-verify"); + } - const addRepoCommand = `"${helm}" repo add ${repoAttributes.name} ${repoAttributes.url}${insecureSkipTlsVerify}${username}${password}${caFile}${keyFile}${certFile}`; - const { stdout } = await promiseExec(addRepoCommand).catch((error) => { - throw(error.stderr); - }); + if (username) { + args.push("--username", username); + } - return stdout; + if (password) { + args.push("--password", password); + } + + if (caFile) { + args.push("--ca-file", caFile); + } + + if (keyFile) { + args.push("--key-file", keyFile); + } + + if (certFile) { + args.push("--cert-file", certFile); + } + + return execHelm(args); } public static async removeRepo({ name, url }: HelmRepo): Promise { - logger.info(`[HELM]: removing repo "${name}" from ${url}`); - const helm = await helmCli.binaryPath(); - const { stdout } = await promiseExec(`"${helm}" repo remove ${name}`).catch((error) => { - throw(error.stderr); - }); + logger.info(`[HELM]: removing repo ${name} (${url})`); - return stdout; + return execHelm([ + "repo", + "remove", + name, + ]); } } diff --git a/src/main/helm/helm-service.ts b/src/main/helm/helm-service.ts index 2e86e36513..4d4637b3b1 100644 --- a/src/main/helm/helm-service.ts +++ b/src/main/helm/helm-service.ts @@ -65,13 +65,19 @@ class HelmService { public async listReleases(cluster: Cluster, namespace: string = null) { const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); + logger.debug("list releases"); + return listReleases(proxyKubeconfig, namespace); } public async getRelease(cluster: Cluster, releaseName: string, namespace: string) { + const kubeconfigPath = await cluster.getProxyKubeconfigPath(); + const kubectl = await cluster.ensureKubectl(); + const kubectlPath = await kubectl.getPath(); + logger.debug("Fetch release"); - return getRelease(releaseName, namespace, cluster); + return getRelease(releaseName, namespace, kubeconfigPath, kubectlPath); } public async getReleaseValues(releaseName: string, { cluster, namespace, all }: GetReleaseValuesArgs) { @@ -79,7 +85,7 @@ class HelmService { logger.debug("Fetch release values"); - return getValues(releaseName, { namespace, all, pathToKubeconfig }); + return getValues(releaseName, { namespace, all, kubeconfigPath: pathToKubeconfig }); } public async getReleaseHistory(cluster: Cluster, releaseName: string, namespace: string) { @@ -99,9 +105,13 @@ class HelmService { } public async updateRelease(cluster: Cluster, releaseName: string, namespace: string, data: { chart: string; values: {}; version: string }) { + const proxyKubeconfig = await cluster.getProxyKubeconfigPath(); + const kubectl = await cluster.ensureKubectl(); + const kubectlPath = await kubectl.getPath(); + logger.debug("Upgrade release"); - return upgradeRelease(releaseName, data.chart, data.values, namespace, data.version, cluster); + return upgradeRelease(releaseName, data.chart, data.values, namespace, data.version, proxyKubeconfig, kubectlPath); } public async rollback(cluster: Cluster, releaseName: string, namespace: string, revision: number) { diff --git a/src/main/kubectl.ts b/src/main/kubectl.ts index dbe4506082..2ac1831c41 100644 --- a/src/main/kubectl.ts +++ b/src/main/kubectl.ts @@ -21,7 +21,7 @@ import path from "path"; import fs from "fs"; -import { promiseExec } from "../common/utils/promise-exec"; +import { promiseExecFile } from "../common/utils/promise-exec"; import logger from "./logger"; import { ensureDir, pathExists } from "fs-extra"; import * as lockFile from "proper-lockfile"; @@ -199,7 +199,12 @@ export class Kubectl { if (exists) { try { - const { stdout } = await promiseExec(`"${path}" version --client=true -o json`); + const args = [ + "version", + "--client", "true", + "--output", "json", + ]; + const { stdout } = await promiseExecFile(path, args); const output = JSON.parse(stdout); if (!checkVersion) { diff --git a/src/renderer/components/+preferences/add-helm-repo-dialog.tsx b/src/renderer/components/+preferences/add-helm-repo-dialog.tsx index d6760fa561..608c33bad2 100644 --- a/src/renderer/components/+preferences/add-helm-repo-dialog.tsx +++ b/src/renderer/components/+preferences/add-helm-repo-dialog.tsx @@ -121,7 +121,7 @@ export class AddHelmRepoDialog extends React.Component {
this.setFilepath(fileType, v)} @@ -172,7 +172,7 @@ export class AddHelmRepoDialog extends React.Component { close={this.close} > - {this.addCustomRepo();}}> + this.addCustomRepo()}>
Date: Fri, 17 Dec 2021 11:01:34 -0500 Subject: [PATCH 06/12] Fix not building (#4588) --- src/renderer/components/+extensions/extensions.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/renderer/components/+extensions/extensions.tsx b/src/renderer/components/+extensions/extensions.tsx index 838635660d..0ba9deba65 100644 --- a/src/renderer/components/+extensions/extensions.tsx +++ b/src/renderer/components/+extensions/extensions.tsx @@ -68,10 +68,6 @@ class NonInjectedExtensions extends React.Component { makeObservable(this); } - get dependencies() { - return this.props.dependencies; - } - componentDidMount() { disposeOnUnmount(this, [ reaction(() => this.props.userExtensions.get().length, (curSize, prevSize) => { From 8f194b87c3535d38749bf3cd7f868f64bbc870c9 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 17 Dec 2021 11:53:00 -0500 Subject: [PATCH 07/12] Add getBaseRegistryUrl dep to attemptInstallByInfoInjectable (#4590) --- .../attempt-install-by-info.injectable.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/renderer/components/+extensions/attempt-install-by-info/attempt-install-by-info.injectable.ts b/src/renderer/components/+extensions/attempt-install-by-info/attempt-install-by-info.injectable.ts index 0db5c1677c..34fd491fe9 100644 --- a/src/renderer/components/+extensions/attempt-install-by-info/attempt-install-by-info.injectable.ts +++ b/src/renderer/components/+extensions/attempt-install-by-info/attempt-install-by-info.injectable.ts @@ -22,10 +22,12 @@ import type { Injectable } from "@ogre-tools/injectable"; import { lifecycleEnum } from "@ogre-tools/injectable"; import { attemptInstallByInfo, ExtensionInfo } from "./attempt-install-by-info"; import attemptInstallInjectable from "../attempt-install/attempt-install.injectable"; +import getBaseRegistryUrlInjectable from "../get-base-registry-url/get-base-registry-url.injectable"; const attemptInstallByInfoInjectable: Injectable<(extensionInfo: ExtensionInfo) => Promise, {}> = { getDependencies: di => ({ attemptInstall: di.inject(attemptInstallInjectable), + getBaseRegistryUrl: di.inject(getBaseRegistryUrlInjectable), }), instantiate: attemptInstallByInfo, From f8fa33ec8cde5dcc5b56f77262b6ccb233f13115 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Sun, 19 Dec 2021 23:18:01 -0500 Subject: [PATCH 08/12] Upgrade to latest version of @typescript-eslint/* pkgs (#4576) Signed-off-by: Sebastian Malton --- package.json | 4 +- yarn.lock | 117 +++++++++++++++++++++++++++------------------------ 2 files changed, 63 insertions(+), 58 deletions(-) diff --git a/package.json b/package.json index 52f521edb7..93af1a9bdf 100644 --- a/package.json +++ b/package.json @@ -324,8 +324,8 @@ "@types/webpack-dev-server": "^3.11.6", "@types/webpack-env": "^1.16.3", "@types/webpack-node-externals": "^1.7.1", - "@typescript-eslint/eslint-plugin": "^4.33.0", - "@typescript-eslint/parser": "^4.33.0", + "@typescript-eslint/eslint-plugin": "^5.7.0", + "@typescript-eslint/parser": "^5.7.0", "ansi_up": "^5.1.0", "chart.js": "^2.9.4", "circular-dependency-plugin": "^5.2.2", diff --git a/yarn.lock b/yarn.lock index 952789bf84..e791116113 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1558,7 +1558,7 @@ "@types/parse5" "*" "@types/tough-cookie" "*" -"@types/json-schema@^7.0.4": +"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.9": version "7.0.9" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== @@ -1568,11 +1568,6 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== -"@types/json-schema@^7.0.7": - version "7.0.8" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" - integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== - "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -2081,75 +2076,75 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" - integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== +"@typescript-eslint/eslint-plugin@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.7.0.tgz#12d54709f8ea1da99a01d8a992cd0474ad0f0aa9" + integrity sha512-8RTGBpNn5a9M628wBPrCbJ+v3YTEOE2qeZb7TDkGKTDXSj36KGRg92SpFFaR/0S3rSXQxM0Og/kV9EyadsYSBg== dependencies: - "@typescript-eslint/experimental-utils" "4.33.0" - "@typescript-eslint/scope-manager" "4.33.0" - debug "^4.3.1" + "@typescript-eslint/experimental-utils" "5.7.0" + "@typescript-eslint/scope-manager" "5.7.0" + debug "^4.3.2" functional-red-black-tree "^1.0.1" ignore "^5.1.8" - regexpp "^3.1.0" + regexpp "^3.2.0" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" - integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== +"@typescript-eslint/experimental-utils@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.7.0.tgz#2b1633e6613c3238036156f70c32634843ad034f" + integrity sha512-u57eZ5FbEpzN5kSjmVrSesovWslH2ZyNPnaXQMXWgH57d5+EVHEt76W75vVuI9qKZ5BMDKNfRN+pxcPEjQjb2A== dependencies: - "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.33.0" - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/typescript-estree" "4.33.0" + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.7.0" + "@typescript-eslint/types" "5.7.0" + "@typescript-eslint/typescript-estree" "5.7.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/parser@^4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" - integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== +"@typescript-eslint/parser@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.7.0.tgz#4dca6de463d86f02d252e681136a67888ea3b181" + integrity sha512-m/gWCCcS4jXw6vkrPQ1BjZ1vomP01PArgzvauBqzsoZ3urLbsRChexB8/YV8z9HwE3qlJM35FxfKZ1nfP/4x8g== dependencies: - "@typescript-eslint/scope-manager" "4.33.0" - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/typescript-estree" "4.33.0" - debug "^4.3.1" + "@typescript-eslint/scope-manager" "5.7.0" + "@typescript-eslint/types" "5.7.0" + "@typescript-eslint/typescript-estree" "5.7.0" + debug "^4.3.2" -"@typescript-eslint/scope-manager@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" - integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== +"@typescript-eslint/scope-manager@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz#70adf960e5a58994ad50438ba60d98ecadd79452" + integrity sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA== dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" + "@typescript-eslint/types" "5.7.0" + "@typescript-eslint/visitor-keys" "5.7.0" -"@typescript-eslint/types@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" - integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== +"@typescript-eslint/types@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.7.0.tgz#2d4cae0105ba7d08bffa69698197a762483ebcbe" + integrity sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA== -"@typescript-eslint/typescript-estree@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" - integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== +"@typescript-eslint/typescript-estree@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz#968fad899050ccce4f08a40cd5fabc0798525006" + integrity sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg== dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" - debug "^4.3.1" - globby "^11.0.3" - is-glob "^4.0.1" + "@typescript-eslint/types" "5.7.0" + "@typescript-eslint/visitor-keys" "5.7.0" + debug "^4.3.2" + globby "^11.0.4" + is-glob "^4.0.3" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" - integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== +"@typescript-eslint/visitor-keys@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz#e05164239eb7cb8aa9fa06c516ede480ce260178" + integrity sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg== dependencies: - "@typescript-eslint/types" "4.33.0" - eslint-visitor-keys "^2.0.0" + "@typescript-eslint/types" "5.7.0" + eslint-visitor-keys "^3.0.0" "@webassemblyjs/ast@1.9.0": version "1.9.0" @@ -5547,6 +5542,11 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== +eslint-visitor-keys@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz#eee4acea891814cda67a7d8812d9647dd0179af2" + integrity sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA== + eslint@^7.32.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" @@ -6594,7 +6594,7 @@ globalthis@^1.0.1: dependencies: define-properties "^1.1.3" -globby@^11.0.1, globby@^11.0.3: +globby@^11.0.1, globby@^11.0.4: version "11.0.4" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== @@ -11864,6 +11864,11 @@ regexpp@^3.1.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + registry-auth-token@^3.0.1: version "3.4.0" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e" From 43277d2af6ceac858cf8c805fa4e3abe6101c447 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Wed, 22 Dec 2021 16:11:06 +0200 Subject: [PATCH 09/12] Less noisy metrics-not-available error logging (#4602) Signed-off-by: Jari Kolehmainen --- src/main/routes/metrics-route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/routes/metrics-route.ts b/src/main/routes/metrics-route.ts index ec53c1a46b..c323c06be9 100644 --- a/src/main/routes/metrics-route.ts +++ b/src/main/routes/metrics-route.ts @@ -46,7 +46,7 @@ async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPa return await getMetrics(cluster, prometheusPath, { query, ...queryParams }); } catch (error) { if (lastAttempt || (error?.statusCode >= 400 && error?.statusCode < 500)) { - logger.error("[Metrics]: metrics not available", error); + logger.error("[Metrics]: metrics not available", error?.response ? error.response?.body : error); throw new Error("Metrics not available"); } From 9284741df51d7e68ca46c21d34277d4677f5f368 Mon Sep 17 00:00:00 2001 From: Juho Heikka Date: Thu, 23 Dec 2021 11:00:18 +0200 Subject: [PATCH 10/12] Show splash window until bundled extensions have been loaded (#4570) * Show splash window until bundled extensions have been loaded Signed-off-by: Juho Heikka * Remove unnecessary variable. Signed-off-by: Juho Heikka * Refactor autoInitExtensions Signed-off-by: Juho Heikka * Add timeout to waiting for bundled extensions to load Signed-off-by: Juho Heikka --- src/common/ipc/extension-loader.ipc.ts | 21 +++++++++++++++++++ src/common/ipc/index.ts | 1 + .../extension-loader/extension-loader.ts | 18 +++++++++++++--- src/main/window-manager.ts | 4 ++-- src/renderer/root-frame.tsx | 18 ++++++++++++++-- 5 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 src/common/ipc/extension-loader.ipc.ts diff --git a/src/common/ipc/extension-loader.ipc.ts b/src/common/ipc/extension-loader.ipc.ts new file mode 100644 index 0000000000..dae1c72316 --- /dev/null +++ b/src/common/ipc/extension-loader.ipc.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +export const BundledExtensionsLoaded = "extension-loader:bundled-extensions-loaded"; diff --git a/src/common/ipc/index.ts b/src/common/ipc/index.ts index ad1cf2a76c..774ce589e9 100644 --- a/src/common/ipc/index.ts +++ b/src/common/ipc/index.ts @@ -27,3 +27,4 @@ export * from "./update-available.ipc"; export * from "./cluster.ipc"; export * from "./type-enforced-ipc"; export * from "./hotbar"; +export * from "./extension-loader.ipc"; diff --git a/src/extensions/extension-loader/extension-loader.ts b/src/extensions/extension-loader/extension-loader.ts index b7d53402ed..af5f05c100 100644 --- a/src/extensions/extension-loader/extension-loader.ts +++ b/src/extensions/extension-loader/extension-loader.ts @@ -255,7 +255,8 @@ export class ExtensionLoader { loadOnClusterManagerRenderer() { logger.debug(`${logModule}: load on main renderer (cluster manager)`); - this.autoInitExtensions(async (extension: LensRendererExtension) => { + + return this.autoInitExtensions(async (extension: LensRendererExtension) => { const removeItems = [ registries.GlobalPageRegistry.getInstance().add(extension.globalPages, extension), registries.AppPreferenceRegistry.getInstance().add(extension.appPreferences), @@ -311,7 +312,9 @@ export class ExtensionLoader { } protected autoInitExtensions(register: (ext: LensExtension) => Promise) { - return reaction(() => this.toJSON(), installedExtensions => { + const loadingExtensions: { isBundled: boolean, loaded: Promise }[] = []; + + reaction(() => this.toJSON(), installedExtensions => { for (const [extId, extension] of installedExtensions) { const alreadyInit = this.instances.has(extId) || this.nonInstancesByName.has(extension.manifest.name); @@ -326,7 +329,14 @@ export class ExtensionLoader { const instance = new LensExtensionClass(extension); - instance.enable(register); + const loaded = instance.enable(register).catch((err) => { + logger.error(`${logModule}: failed to enable`, { ext: extension, err }); + }); + + loadingExtensions.push({ + isBundled: extension.isBundled, + loaded, + }); this.instances.set(extId, instance); } catch (err) { logger.error(`${logModule}: activation extension error`, { ext: extension, err }); @@ -338,6 +348,8 @@ export class ExtensionLoader { }, { fireImmediately: true, }); + + return loadingExtensions; } protected requireExtension(extension: InstalledExtension): LensExtensionConstructor | null { diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index 48704ba074..3987e29824 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -24,7 +24,7 @@ import { makeObservable, observable } from "mobx"; import { app, BrowserWindow, dialog, ipcMain, shell, webContents } from "electron"; import windowStateKeeper from "electron-window-state"; import { appEventBus } from "../common/event-bus"; -import { ipcMainOn } from "../common/ipc"; +import { BundledExtensionsLoaded, ipcMainOn } from "../common/ipc"; import { delay, iter, Singleton } from "../common/utils"; import { ClusterFrameInfo, clusterFrameMap } from "../common/cluster-frames"; import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; @@ -181,7 +181,7 @@ export class WindowManager extends Singleton { if (!this.mainWindow) { viewHasLoaded = new Promise(resolve => { - ipcMain.once(IpcRendererNavigationEvents.LOADED, () => resolve()); + ipcMain.once(BundledExtensionsLoaded, () => resolve()); }); await this.initMainWindow(showSplash); } diff --git a/src/renderer/root-frame.tsx b/src/renderer/root-frame.tsx index c403af4bed..33132570c0 100644 --- a/src/renderer/root-frame.tsx +++ b/src/renderer/root-frame.tsx @@ -29,7 +29,7 @@ import { ErrorBoundary } from "./components/error-boundary"; import { Notifications } from "./components/notifications"; import { ConfirmDialog } from "./components/confirm-dialog"; import type { ExtensionLoader } from "../extensions/extension-loader"; -import { broadcastMessage } from "../common/ipc"; +import { broadcastMessage, BundledExtensionsLoaded } from "../common/ipc"; import { CommandContainer } from "./components/command-palette/command-container"; import { registerIpcListeners } from "./ipc"; import { ipcRenderer } from "electron"; @@ -39,6 +39,7 @@ import logger from "../common/logger"; import { unmountComponentAtNode } from "react-dom"; import { ClusterFrameHandler } from "./components/cluster-manager/lens-views"; import type { LensProtocolRouterRenderer } from "./protocol-handler"; +import { delay } from "./utils"; injectSystemCAs(); @@ -54,8 +55,21 @@ export class RootFrame extends React.Component { lensProtocolRouterRendererInjectable: LensProtocolRouterRenderer, ) { catalogEntityRegistry.init(); - extensionLoader.loadOnClusterManagerRenderer(); + + try { + // maximum time to let bundled extensions finish loading + const timeout = delay(10000); + + const loadingExtensions = extensionLoader.loadOnClusterManagerRenderer(); + const loadingBundledExtensions = loadingExtensions.filter(e => e.isBundled).map(e => e.loaded); + const bundledExtensionsFinished = Promise.all(loadingBundledExtensions); + + await Promise.race([bundledExtensionsFinished, timeout]); + } finally { + ipcRenderer.send(BundledExtensionsLoaded); + } lensProtocolRouterRendererInjectable.init(); + bindProtocolAddRouteHandlers(); window.addEventListener("offline", () => broadcastMessage("network:offline")); From 71175a0ae1d573a2756774ee68c71bb2d0a0c729 Mon Sep 17 00:00:00 2001 From: Strokachuk Oleg Date: Thu, 23 Dec 2021 12:20:27 +0300 Subject: [PATCH 11/12] Show all helm release statuses. Fix lensapp#4586 (#4594) Signed-off-by: zifter --- src/main/helm/helm-release-manager.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/helm/helm-release-manager.ts b/src/main/helm/helm-release-manager.ts index 5a5f1e6ef2..25c8b67737 100644 --- a/src/main/helm/helm-release-manager.ts +++ b/src/main/helm/helm-release-manager.ts @@ -43,6 +43,7 @@ async function execHelm(args: string[], options?: BaseEncodingOptions & ExecFile export async function listReleases(pathToKubeconfig: string, namespace?: string): Promise[]> { const args = [ "ls", + "--all", "--output", "json", ]; From 2d279a6b99bf66fe35f1622916c90a0ea1d71684 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 23 Dec 2021 12:29:36 +0200 Subject: [PATCH 12/12] using both sass + modules + tailwind (optional) (#4604) using sass + css-modules + tailwind for styling, part of #3701 --- package.json | 9 +- postcss.config.js | 27 --- ...ter.module.css => add-cluster.module.scss} | 0 .../components/+add-cluster/add-cluster.tsx | 2 +- ...css => catalog-entity-details.module.scss} | 0 .../+catalog/catalog-entity-details.tsx | 2 +- .../+catalog/catalog-entity-item.tsx | 2 +- ...nu.module.css => catalog-menu.module.scss} | 0 .../components/+catalog/catalog-menu.tsx | 4 +- ...ee.module.css => catalog-tree.module.scss} | 0 ...catalog.module.css => catalog.module.scss} | 0 src/renderer/components/+catalog/catalog.tsx | 2 +- ....module.css => cluster-issues.module.scss} | 0 .../components/+cluster/cluster-issues.tsx | 2 +- ...module.css => cluster-metrics.module.scss} | 0 .../components/+cluster/cluster-metrics.tsx | 2 +- ...odule.css => cluster-overview.module.scss} | 0 .../components/+cluster/cluster-overview.tsx | 2 +- ...ule.css => cluster-pie-charts.module.scss} | 0 .../+cluster/cluster-pie-charts.tsx | 2 +- ...module.css => entity-settings.module.scss} | 0 .../+entity-settings/entity-settings.tsx | 2 +- ...install.module.css => install.module.scss} | 0 .../components/+extensions/install.tsx | 2 +- ...e.css => installed-extensions.module.scss} | 0 .../+extensions/installed-extensions.tsx | 2 +- .../{notice.module.css => notice.module.scss} | 0 .../components/+extensions/notice.tsx | 2 +- ...css => network-policy-details.module.scss} | 0 .../network-policy-details.tsx | 2 +- ...rts.module.css => helm-charts.module.scss} | 0 .../components/+preferences/helm-charts.tsx | 2 +- ....module.css => removable-item.module.scss} | 0 .../+preferences/removable-item.tsx | 2 +- src/renderer/components/app.scss | 9 +- .../{avatar.module.css => avatar.module.scss} | 0 src/renderer/components/avatar/avatar.tsx | 2 +- .../{badge.module.css => badge.module.scss} | 0 src/renderer/components/badge/badge.tsx | 2 +- ...-bar.module.css => bottom-bar.module.scss} | 0 .../components/cluster-manager/bottom-bar.tsx | 2 +- ....module.css => cluster-status.module.scss} | 0 .../cluster-manager/cluster-status.tsx | 2 +- ....css => delete-cluster-dialog.module.scss} | 0 .../delete-cluster-dialog.tsx | 2 +- ...el.module.css => editor-panel.module.scss} | 0 src/renderer/components/dock/editor-panel.tsx | 2 +- ...ule.css => hotbar-entity-icon.module.scss} | 0 .../components/hotbar/hotbar-entity-icon.tsx | 2 +- ...con.module.css => hotbar-icon.module.scss} | 0 .../components/hotbar/hotbar-icon.tsx | 2 +- ...dule.css => kubeconfig-dialog.module.scss} | 0 .../kubeconfig-dialog/kubeconfig-dialog.tsx | 2 +- ...out.module.css => main-layout.module.scss} | 0 .../components/layout/main-layout.tsx | 2 +- ...module.css => sidebar-cluster.module.scss} | 0 .../components/layout/sidebar-cluster.tsx | 2 +- ...sidebar.module.css => sidebar.module.scss} | 0 src/renderer/components/layout/sidebar.tsx | 2 +- .../{topbar.module.css => topbar.module.scss} | 0 src/renderer/components/layout/topbar.tsx | 2 +- .../{list.module.css => list.module.scss} | 0 src/renderer/components/list/list.tsx | 2 +- ...r.module.css => monaco-editor.module.scss} | 0 .../monaco-editor/monaco-editor.tsx | 2 +- ...ble.module.css => react-table.module.scss} | 0 src/renderer/components/table/react-table.tsx | 2 +- tailwind.config.js | 3 +- types/mocks.d.ts | 12 +- webpack.extensions.ts | 5 +- webpack.renderer.ts | 30 +-- yarn.lock | 212 +++++------------- 72 files changed, 121 insertions(+), 252 deletions(-) delete mode 100644 postcss.config.js rename src/renderer/components/+add-cluster/{add-cluster.module.css => add-cluster.module.scss} (100%) rename src/renderer/components/+catalog/{catalog-entity-details.module.css => catalog-entity-details.module.scss} (100%) rename src/renderer/components/+catalog/{catalog-menu.module.css => catalog-menu.module.scss} (100%) rename src/renderer/components/+catalog/{catalog-tree.module.css => catalog-tree.module.scss} (100%) rename src/renderer/components/+catalog/{catalog.module.css => catalog.module.scss} (100%) rename src/renderer/components/+cluster/{cluster-issues.module.css => cluster-issues.module.scss} (100%) rename src/renderer/components/+cluster/{cluster-metrics.module.css => cluster-metrics.module.scss} (100%) rename src/renderer/components/+cluster/{cluster-overview.module.css => cluster-overview.module.scss} (100%) rename src/renderer/components/+cluster/{cluster-pie-charts.module.css => cluster-pie-charts.module.scss} (100%) rename src/renderer/components/+entity-settings/{entity-settings.module.css => entity-settings.module.scss} (100%) rename src/renderer/components/+extensions/{install.module.css => install.module.scss} (100%) rename src/renderer/components/+extensions/{installed-extensions.module.css => installed-extensions.module.scss} (100%) rename src/renderer/components/+extensions/{notice.module.css => notice.module.scss} (100%) rename src/renderer/components/+network-policies/{network-policy-details.module.css => network-policy-details.module.scss} (100%) rename src/renderer/components/+preferences/{helm-charts.module.css => helm-charts.module.scss} (100%) rename src/renderer/components/+preferences/{removable-item.module.css => removable-item.module.scss} (100%) rename src/renderer/components/avatar/{avatar.module.css => avatar.module.scss} (100%) rename src/renderer/components/badge/{badge.module.css => badge.module.scss} (100%) rename src/renderer/components/cluster-manager/{bottom-bar.module.css => bottom-bar.module.scss} (100%) rename src/renderer/components/cluster-manager/{cluster-status.module.css => cluster-status.module.scss} (100%) rename src/renderer/components/delete-cluster-dialog/{delete-cluster-dialog.module.css => delete-cluster-dialog.module.scss} (100%) rename src/renderer/components/dock/{editor-panel.module.css => editor-panel.module.scss} (100%) rename src/renderer/components/hotbar/{hotbar-entity-icon.module.css => hotbar-entity-icon.module.scss} (100%) rename src/renderer/components/hotbar/{hotbar-icon.module.css => hotbar-icon.module.scss} (100%) rename src/renderer/components/kubeconfig-dialog/{kubeconfig-dialog.module.css => kubeconfig-dialog.module.scss} (100%) rename src/renderer/components/layout/{main-layout.module.css => main-layout.module.scss} (100%) rename src/renderer/components/layout/{sidebar-cluster.module.css => sidebar-cluster.module.scss} (100%) rename src/renderer/components/layout/{sidebar.module.css => sidebar.module.scss} (100%) rename src/renderer/components/layout/{topbar.module.css => topbar.module.scss} (100%) rename src/renderer/components/list/{list.module.css => list.module.scss} (100%) rename src/renderer/components/monaco-editor/{monaco-editor.module.css => monaco-editor.module.scss} (100%) rename src/renderer/components/table/{react-table.module.css => react-table.module.scss} (100%) diff --git a/package.json b/package.json index 93af1a9bdf..2a6ca287a4 100644 --- a/package.json +++ b/package.json @@ -361,8 +361,7 @@ "nodemon": "^2.0.14", "playwright": "^1.17.1", "postcss": "^8.4.5", - "postcss-loader": "4.3.0", - "postinstall-postinstall": "^2.1.0", + "postcss-loader": "^4.3.0", "progress-bar-webpack-plugin": "^2.1.0", "randomcolor": "^0.6.2", "raw-loader": "^4.0.2", @@ -373,11 +372,11 @@ "react-select-event": "^5.1.0", "react-table": "^7.7.0", "react-window": "^1.8.6", - "sass": "^1.44.0", - "sass-loader": "^8.0.2", + "sass": "^1.45.1", + "sass-loader": "^10.2.0", "sharp": "^0.29.3", "style-loader": "^2.0.0", - "tailwindcss": "^2.2.19", + "tailwindcss": "^3.0.7", "ts-jest": "26.5.6", "ts-loader": "^7.0.5", "ts-node": "^10.4.0", diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index 66746899cf..0000000000 --- a/postcss.config.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2021 OpenLens Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -module.exports = { - plugins: [ - require("tailwindcss/nesting"), - require("tailwindcss"), - ], -}; diff --git a/src/renderer/components/+add-cluster/add-cluster.module.css b/src/renderer/components/+add-cluster/add-cluster.module.scss similarity index 100% rename from src/renderer/components/+add-cluster/add-cluster.module.css rename to src/renderer/components/+add-cluster/add-cluster.module.scss diff --git a/src/renderer/components/+add-cluster/add-cluster.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx index f6b683f654..40257f6518 100644 --- a/src/renderer/components/+add-cluster/add-cluster.tsx +++ b/src/renderer/components/+add-cluster/add-cluster.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./add-cluster.module.css"; +import styles from "./add-cluster.module.scss"; import type { KubeConfig } from "@kubernetes/client-node"; import fse from "fs-extra"; diff --git a/src/renderer/components/+catalog/catalog-entity-details.module.css b/src/renderer/components/+catalog/catalog-entity-details.module.scss similarity index 100% rename from src/renderer/components/+catalog/catalog-entity-details.module.css rename to src/renderer/components/+catalog/catalog-entity-details.module.scss diff --git a/src/renderer/components/+catalog/catalog-entity-details.tsx b/src/renderer/components/+catalog/catalog-entity-details.tsx index b3c9fc73c2..4ac95833da 100644 --- a/src/renderer/components/+catalog/catalog-entity-details.tsx +++ b/src/renderer/components/+catalog/catalog-entity-details.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./catalog-entity-details.module.css"; +import styles from "./catalog-entity-details.module.scss"; import React, { Component } from "react"; import { observer } from "mobx-react"; import { Drawer, DrawerItem } from "../drawer"; diff --git a/src/renderer/components/+catalog/catalog-entity-item.tsx b/src/renderer/components/+catalog/catalog-entity-item.tsx index 9518131375..01fc46b95d 100644 --- a/src/renderer/components/+catalog/catalog-entity-item.tsx +++ b/src/renderer/components/+catalog/catalog-entity-item.tsx @@ -18,7 +18,7 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./catalog.module.css"; +import styles from "./catalog.module.scss"; import React from "react"; import { action, computed } from "mobx"; import { CatalogEntity } from "../../api/catalog-entity"; diff --git a/src/renderer/components/+catalog/catalog-menu.module.css b/src/renderer/components/+catalog/catalog-menu.module.scss similarity index 100% rename from src/renderer/components/+catalog/catalog-menu.module.css rename to src/renderer/components/+catalog/catalog-menu.module.scss diff --git a/src/renderer/components/+catalog/catalog-menu.tsx b/src/renderer/components/+catalog/catalog-menu.tsx index b2d013dfcd..c80fc1901c 100644 --- a/src/renderer/components/+catalog/catalog-menu.tsx +++ b/src/renderer/components/+catalog/catalog-menu.tsx @@ -19,8 +19,8 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import treeStyles from "./catalog-tree.module.css"; -import styles from "./catalog-menu.module.css"; +import treeStyles from "./catalog-tree.module.scss"; +import styles from "./catalog-menu.module.scss"; import React from "react"; import { TreeItem, TreeItemProps, TreeView } from "@material-ui/lab"; diff --git a/src/renderer/components/+catalog/catalog-tree.module.css b/src/renderer/components/+catalog/catalog-tree.module.scss similarity index 100% rename from src/renderer/components/+catalog/catalog-tree.module.css rename to src/renderer/components/+catalog/catalog-tree.module.scss diff --git a/src/renderer/components/+catalog/catalog.module.css b/src/renderer/components/+catalog/catalog.module.scss similarity index 100% rename from src/renderer/components/+catalog/catalog.module.css rename to src/renderer/components/+catalog/catalog.module.scss diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index bb39419ed3..8c9455d805 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./catalog.module.css"; +import styles from "./catalog.module.scss"; import React from "react"; import { disposeOnUnmount, observer } from "mobx-react"; diff --git a/src/renderer/components/+cluster/cluster-issues.module.css b/src/renderer/components/+cluster/cluster-issues.module.scss similarity index 100% rename from src/renderer/components/+cluster/cluster-issues.module.css rename to src/renderer/components/+cluster/cluster-issues.module.scss diff --git a/src/renderer/components/+cluster/cluster-issues.tsx b/src/renderer/components/+cluster/cluster-issues.tsx index 6165fde8bd..0537f98224 100644 --- a/src/renderer/components/+cluster/cluster-issues.tsx +++ b/src/renderer/components/+cluster/cluster-issues.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./cluster-issues.module.css"; +import styles from "./cluster-issues.module.scss"; import React from "react"; import { observer } from "mobx-react"; diff --git a/src/renderer/components/+cluster/cluster-metrics.module.css b/src/renderer/components/+cluster/cluster-metrics.module.scss similarity index 100% rename from src/renderer/components/+cluster/cluster-metrics.module.css rename to src/renderer/components/+cluster/cluster-metrics.module.scss diff --git a/src/renderer/components/+cluster/cluster-metrics.tsx b/src/renderer/components/+cluster/cluster-metrics.tsx index f4cc72e8d2..919a96e774 100644 --- a/src/renderer/components/+cluster/cluster-metrics.tsx +++ b/src/renderer/components/+cluster/cluster-metrics.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./cluster-metrics.module.css"; +import styles from "./cluster-metrics.module.scss"; import React from "react"; import { observer } from "mobx-react"; diff --git a/src/renderer/components/+cluster/cluster-overview.module.css b/src/renderer/components/+cluster/cluster-overview.module.scss similarity index 100% rename from src/renderer/components/+cluster/cluster-overview.module.css rename to src/renderer/components/+cluster/cluster-overview.module.scss diff --git a/src/renderer/components/+cluster/cluster-overview.tsx b/src/renderer/components/+cluster/cluster-overview.tsx index d2e3e3ee9a..064b716e7e 100644 --- a/src/renderer/components/+cluster/cluster-overview.tsx +++ b/src/renderer/components/+cluster/cluster-overview.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./cluster-overview.module.css"; +import styles from "./cluster-overview.module.scss"; import React from "react"; import { reaction } from "mobx"; diff --git a/src/renderer/components/+cluster/cluster-pie-charts.module.css b/src/renderer/components/+cluster/cluster-pie-charts.module.scss similarity index 100% rename from src/renderer/components/+cluster/cluster-pie-charts.module.css rename to src/renderer/components/+cluster/cluster-pie-charts.module.scss diff --git a/src/renderer/components/+cluster/cluster-pie-charts.tsx b/src/renderer/components/+cluster/cluster-pie-charts.tsx index 80df2c1a9c..c88202c410 100644 --- a/src/renderer/components/+cluster/cluster-pie-charts.tsx +++ b/src/renderer/components/+cluster/cluster-pie-charts.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./cluster-pie-charts.module.css"; +import styles from "./cluster-pie-charts.module.scss"; import React from "react"; import { observer } from "mobx-react"; diff --git a/src/renderer/components/+entity-settings/entity-settings.module.css b/src/renderer/components/+entity-settings/entity-settings.module.scss similarity index 100% rename from src/renderer/components/+entity-settings/entity-settings.module.css rename to src/renderer/components/+entity-settings/entity-settings.module.scss diff --git a/src/renderer/components/+entity-settings/entity-settings.tsx b/src/renderer/components/+entity-settings/entity-settings.tsx index 65a953a655..ee543490eb 100644 --- a/src/renderer/components/+entity-settings/entity-settings.tsx +++ b/src/renderer/components/+entity-settings/entity-settings.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./entity-settings.module.css"; +import styles from "./entity-settings.module.scss"; import React from "react"; import { observable, makeObservable } from "mobx"; diff --git a/src/renderer/components/+extensions/install.module.css b/src/renderer/components/+extensions/install.module.scss similarity index 100% rename from src/renderer/components/+extensions/install.module.css rename to src/renderer/components/+extensions/install.module.scss diff --git a/src/renderer/components/+extensions/install.tsx b/src/renderer/components/+extensions/install.tsx index 36ec5be2f9..2f81d7e950 100644 --- a/src/renderer/components/+extensions/install.tsx +++ b/src/renderer/components/+extensions/install.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./install.module.css"; +import styles from "./install.module.scss"; import React from "react"; import { prevDefault } from "../../utils"; import { Button } from "../button"; diff --git a/src/renderer/components/+extensions/installed-extensions.module.css b/src/renderer/components/+extensions/installed-extensions.module.scss similarity index 100% rename from src/renderer/components/+extensions/installed-extensions.module.css rename to src/renderer/components/+extensions/installed-extensions.module.scss diff --git a/src/renderer/components/+extensions/installed-extensions.tsx b/src/renderer/components/+extensions/installed-extensions.tsx index 14e490bed5..83c12b8603 100644 --- a/src/renderer/components/+extensions/installed-extensions.tsx +++ b/src/renderer/components/+extensions/installed-extensions.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./installed-extensions.module.css"; +import styles from "./installed-extensions.module.scss"; import React, { useMemo } from "react"; import { ExtensionDiscovery, InstalledExtension } from "../../../extensions/extension-discovery"; import { Icon } from "../icon"; diff --git a/src/renderer/components/+extensions/notice.module.css b/src/renderer/components/+extensions/notice.module.scss similarity index 100% rename from src/renderer/components/+extensions/notice.module.css rename to src/renderer/components/+extensions/notice.module.scss diff --git a/src/renderer/components/+extensions/notice.tsx b/src/renderer/components/+extensions/notice.tsx index 511388a09b..e72fa55eef 100644 --- a/src/renderer/components/+extensions/notice.tsx +++ b/src/renderer/components/+extensions/notice.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./notice.module.css"; +import styles from "./notice.module.scss"; import React, { DOMAttributes } from "react"; import { cssNames } from "../../utils"; diff --git a/src/renderer/components/+network-policies/network-policy-details.module.css b/src/renderer/components/+network-policies/network-policy-details.module.scss similarity index 100% rename from src/renderer/components/+network-policies/network-policy-details.module.css rename to src/renderer/components/+network-policies/network-policy-details.module.scss diff --git a/src/renderer/components/+network-policies/network-policy-details.tsx b/src/renderer/components/+network-policies/network-policy-details.tsx index ff870bc2cc..21ca1f1191 100644 --- a/src/renderer/components/+network-policies/network-policy-details.tsx +++ b/src/renderer/components/+network-policies/network-policy-details.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./network-policy-details.module.css"; +import styles from "./network-policy-details.module.scss"; import React from "react"; import { DrawerItem, DrawerTitle } from "../drawer"; diff --git a/src/renderer/components/+preferences/helm-charts.module.css b/src/renderer/components/+preferences/helm-charts.module.scss similarity index 100% rename from src/renderer/components/+preferences/helm-charts.module.css rename to src/renderer/components/+preferences/helm-charts.module.scss diff --git a/src/renderer/components/+preferences/helm-charts.tsx b/src/renderer/components/+preferences/helm-charts.tsx index 5119e3d86c..028b3bc763 100644 --- a/src/renderer/components/+preferences/helm-charts.tsx +++ b/src/renderer/components/+preferences/helm-charts.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./helm-charts.module.css"; +import styles from "./helm-charts.module.scss"; import React from "react"; import { action, computed, observable, makeObservable } from "mobx"; diff --git a/src/renderer/components/+preferences/removable-item.module.css b/src/renderer/components/+preferences/removable-item.module.scss similarity index 100% rename from src/renderer/components/+preferences/removable-item.module.css rename to src/renderer/components/+preferences/removable-item.module.scss diff --git a/src/renderer/components/+preferences/removable-item.tsx b/src/renderer/components/+preferences/removable-item.tsx index 131974f4d1..aeb68c1d33 100644 --- a/src/renderer/components/+preferences/removable-item.tsx +++ b/src/renderer/components/+preferences/removable-item.tsx @@ -18,7 +18,7 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./removable-item.module.css"; +import styles from "./removable-item.module.scss"; import React, { DOMAttributes } from "react"; diff --git a/src/renderer/components/app.scss b/src/renderer/components/app.scss index feba4d8666..0482dd0dd8 100755 --- a/src/renderer/components/app.scss +++ b/src/renderer/components/app.scss @@ -19,9 +19,12 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -@import "tailwindcss/utilities"; -@import "~flex.box"; -@import "fonts"; +@tailwind base; +@tailwind components; +@tailwind utilities; + +@import "~flex.box"; // todo: replace with tailwind's flexbox classes +@import "./fonts"; @import "../themes/theme-vars"; :root { diff --git a/src/renderer/components/avatar/avatar.module.css b/src/renderer/components/avatar/avatar.module.scss similarity index 100% rename from src/renderer/components/avatar/avatar.module.css rename to src/renderer/components/avatar/avatar.module.scss diff --git a/src/renderer/components/avatar/avatar.tsx b/src/renderer/components/avatar/avatar.tsx index 605652e99a..597c5c36f7 100644 --- a/src/renderer/components/avatar/avatar.tsx +++ b/src/renderer/components/avatar/avatar.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./avatar.module.css"; +import styles from "./avatar.module.scss"; import React, { HTMLAttributes, ImgHTMLAttributes } from "react"; import randomColor from "randomcolor"; diff --git a/src/renderer/components/badge/badge.module.css b/src/renderer/components/badge/badge.module.scss similarity index 100% rename from src/renderer/components/badge/badge.module.css rename to src/renderer/components/badge/badge.module.scss diff --git a/src/renderer/components/badge/badge.tsx b/src/renderer/components/badge/badge.tsx index fe722c3032..05588db58a 100644 --- a/src/renderer/components/badge/badge.tsx +++ b/src/renderer/components/badge/badge.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./badge.module.css"; +import styles from "./badge.module.scss"; import React from "react"; import { computed, makeObservable, observable } from "mobx"; diff --git a/src/renderer/components/cluster-manager/bottom-bar.module.css b/src/renderer/components/cluster-manager/bottom-bar.module.scss similarity index 100% rename from src/renderer/components/cluster-manager/bottom-bar.module.css rename to src/renderer/components/cluster-manager/bottom-bar.module.scss diff --git a/src/renderer/components/cluster-manager/bottom-bar.tsx b/src/renderer/components/cluster-manager/bottom-bar.tsx index 27b5a10f3f..3213bcc0cc 100644 --- a/src/renderer/components/cluster-manager/bottom-bar.tsx +++ b/src/renderer/components/cluster-manager/bottom-bar.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./bottom-bar.module.css"; +import styles from "./bottom-bar.module.scss"; import React from "react"; import { observer } from "mobx-react"; diff --git a/src/renderer/components/cluster-manager/cluster-status.module.css b/src/renderer/components/cluster-manager/cluster-status.module.scss similarity index 100% rename from src/renderer/components/cluster-manager/cluster-status.module.css rename to src/renderer/components/cluster-manager/cluster-status.module.scss diff --git a/src/renderer/components/cluster-manager/cluster-status.tsx b/src/renderer/components/cluster-manager/cluster-status.tsx index 79df15c412..5b7e73510c 100644 --- a/src/renderer/components/cluster-manager/cluster-status.tsx +++ b/src/renderer/components/cluster-manager/cluster-status.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./cluster-status.module.css"; +import styles from "./cluster-status.module.scss"; import { computed, observable, makeObservable } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; diff --git a/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.module.css b/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.module.scss similarity index 100% rename from src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.module.css rename to src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.module.scss diff --git a/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.tsx b/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.tsx index c677a8995c..1a2fbb0506 100644 --- a/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.tsx +++ b/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.tsx @@ -18,7 +18,7 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./delete-cluster-dialog.module.css"; +import styles from "./delete-cluster-dialog.module.scss"; import { computed, makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; diff --git a/src/renderer/components/dock/editor-panel.module.css b/src/renderer/components/dock/editor-panel.module.scss similarity index 100% rename from src/renderer/components/dock/editor-panel.module.css rename to src/renderer/components/dock/editor-panel.module.scss diff --git a/src/renderer/components/dock/editor-panel.tsx b/src/renderer/components/dock/editor-panel.tsx index dcf3197b2f..df9cc15e10 100644 --- a/src/renderer/components/dock/editor-panel.tsx +++ b/src/renderer/components/dock/editor-panel.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./editor-panel.module.css"; +import styles from "./editor-panel.module.scss"; import throttle from "lodash/throttle"; import React from "react"; import { makeObservable, observable, reaction } from "mobx"; diff --git a/src/renderer/components/hotbar/hotbar-entity-icon.module.css b/src/renderer/components/hotbar/hotbar-entity-icon.module.scss similarity index 100% rename from src/renderer/components/hotbar/hotbar-entity-icon.module.css rename to src/renderer/components/hotbar/hotbar-entity-icon.module.scss diff --git a/src/renderer/components/hotbar/hotbar-entity-icon.tsx b/src/renderer/components/hotbar/hotbar-entity-icon.tsx index e889306c41..56826a747b 100644 --- a/src/renderer/components/hotbar/hotbar-entity-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-entity-icon.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./hotbar-entity-icon.module.css"; +import styles from "./hotbar-entity-icon.module.scss"; import React, { HTMLAttributes } from "react"; import { makeObservable, observable } from "mobx"; diff --git a/src/renderer/components/hotbar/hotbar-icon.module.css b/src/renderer/components/hotbar/hotbar-icon.module.scss similarity index 100% rename from src/renderer/components/hotbar/hotbar-icon.module.css rename to src/renderer/components/hotbar/hotbar-icon.module.scss diff --git a/src/renderer/components/hotbar/hotbar-icon.tsx b/src/renderer/components/hotbar/hotbar-icon.tsx index 0cd746b72f..6e2eead049 100644 --- a/src/renderer/components/hotbar/hotbar-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-icon.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./hotbar-icon.module.css"; +import styles from "./hotbar-icon.module.scss"; import React, { useState } from "react"; diff --git a/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.module.css b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.module.scss similarity index 100% rename from src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.module.css rename to src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.module.scss diff --git a/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx index 02a240043a..8ea0c420f8 100644 --- a/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx +++ b/src/renderer/components/kubeconfig-dialog/kubeconfig-dialog.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./kubeconfig-dialog.module.css"; +import styles from "./kubeconfig-dialog.module.scss"; import React from "react"; import { makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; diff --git a/src/renderer/components/layout/main-layout.module.css b/src/renderer/components/layout/main-layout.module.scss similarity index 100% rename from src/renderer/components/layout/main-layout.module.css rename to src/renderer/components/layout/main-layout.module.scss diff --git a/src/renderer/components/layout/main-layout.tsx b/src/renderer/components/layout/main-layout.tsx index e8316bfd3c..2c409d9ca2 100755 --- a/src/renderer/components/layout/main-layout.tsx +++ b/src/renderer/components/layout/main-layout.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./main-layout.module.css"; +import styles from "./main-layout.module.scss"; import React from "react"; import { observer } from "mobx-react"; diff --git a/src/renderer/components/layout/sidebar-cluster.module.css b/src/renderer/components/layout/sidebar-cluster.module.scss similarity index 100% rename from src/renderer/components/layout/sidebar-cluster.module.css rename to src/renderer/components/layout/sidebar-cluster.module.scss diff --git a/src/renderer/components/layout/sidebar-cluster.tsx b/src/renderer/components/layout/sidebar-cluster.tsx index a4479f1a24..83a337a590 100644 --- a/src/renderer/components/layout/sidebar-cluster.tsx +++ b/src/renderer/components/layout/sidebar-cluster.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./sidebar-cluster.module.css"; +import styles from "./sidebar-cluster.module.scss"; import { observable } from "mobx"; import React, { useState } from "react"; import { HotbarStore } from "../../../common/hotbar-store"; diff --git a/src/renderer/components/layout/sidebar.module.css b/src/renderer/components/layout/sidebar.module.scss similarity index 100% rename from src/renderer/components/layout/sidebar.module.css rename to src/renderer/components/layout/sidebar.module.scss diff --git a/src/renderer/components/layout/sidebar.tsx b/src/renderer/components/layout/sidebar.tsx index bd4c4ec661..58d388242c 100644 --- a/src/renderer/components/layout/sidebar.tsx +++ b/src/renderer/components/layout/sidebar.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./sidebar.module.css"; +import styles from "./sidebar.module.scss"; import type { TabLayoutRoute } from "./tab-layout"; import React from "react"; diff --git a/src/renderer/components/layout/topbar.module.css b/src/renderer/components/layout/topbar.module.scss similarity index 100% rename from src/renderer/components/layout/topbar.module.css rename to src/renderer/components/layout/topbar.module.scss diff --git a/src/renderer/components/layout/topbar.tsx b/src/renderer/components/layout/topbar.tsx index af47fd2548..4705c6993c 100644 --- a/src/renderer/components/layout/topbar.tsx +++ b/src/renderer/components/layout/topbar.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./topbar.module.css"; +import styles from "./topbar.module.scss"; import React, { useEffect, useMemo, useRef } from "react"; import { observer } from "mobx-react"; import { TopBarRegistry } from "../../../extensions/registries"; diff --git a/src/renderer/components/list/list.module.css b/src/renderer/components/list/list.module.scss similarity index 100% rename from src/renderer/components/list/list.module.css rename to src/renderer/components/list/list.module.scss diff --git a/src/renderer/components/list/list.tsx b/src/renderer/components/list/list.tsx index 0cff4c7e2e..38ce0f3a37 100644 --- a/src/renderer/components/list/list.tsx +++ b/src/renderer/components/list/list.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./list.module.css"; +import styles from "./list.module.scss"; import React, { useState } from "react"; import { SearchInput } from "../input"; diff --git a/src/renderer/components/monaco-editor/monaco-editor.module.css b/src/renderer/components/monaco-editor/monaco-editor.module.scss similarity index 100% rename from src/renderer/components/monaco-editor/monaco-editor.module.css rename to src/renderer/components/monaco-editor/monaco-editor.module.scss diff --git a/src/renderer/components/monaco-editor/monaco-editor.tsx b/src/renderer/components/monaco-editor/monaco-editor.tsx index e01ce37479..96301e8a7f 100644 --- a/src/renderer/components/monaco-editor/monaco-editor.tsx +++ b/src/renderer/components/monaco-editor/monaco-editor.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./monaco-editor.module.css"; +import styles from "./monaco-editor.module.scss"; import React from "react"; import { observer } from "mobx-react"; import { action, computed, makeObservable, observable, reaction } from "mobx"; diff --git a/src/renderer/components/table/react-table.module.css b/src/renderer/components/table/react-table.module.scss similarity index 100% rename from src/renderer/components/table/react-table.module.css rename to src/renderer/components/table/react-table.module.scss diff --git a/src/renderer/components/table/react-table.tsx b/src/renderer/components/table/react-table.tsx index 27b19f50dd..de001c2a9c 100644 --- a/src/renderer/components/table/react-table.tsx +++ b/src/renderer/components/table/react-table.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./react-table.module.css"; +import styles from "./react-table.module.scss"; import React from "react"; import { useCallback, useMemo } from "react"; import { useFlexLayout, useSortBy, useTable, UseTableOptions } from "react-table"; diff --git a/tailwind.config.js b/tailwind.config.js index 040f2dce23..1d2f2c9506 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -20,8 +20,7 @@ */ module.exports = { - mode: "jit", - purge: ["src/**/*.tsx"], + content: ["src/**/*.tsx"], darkMode: "class", theme: { fontFamily: { diff --git a/types/mocks.d.ts b/types/mocks.d.ts index 57e2812dcb..8b26cb3a03 100644 --- a/types/mocks.d.ts +++ b/types/mocks.d.ts @@ -29,6 +29,14 @@ declare const __static: string; // Support import for custom module extensions // https://www.typescriptlang.org/docs/handbook/modules.html#wildcard-module-declarations +declare module "*.module.scss" { + const classes: { [key: string]: string }; + export default classes; +} +declare module "*.module.css" { + const classes: { [key: string]: string }; + export default classes; +} declare module "*.scss" { const content: string; export = content; @@ -37,7 +45,3 @@ declare module "*.ttf" { const content: string; export = content; } -declare module "*.module.css" { - const classes: { [key: string]: string }; - export default classes; -} diff --git a/webpack.extensions.ts b/webpack.extensions.ts index 7b7e876fdc..c8d058401e 100644 --- a/webpack.extensions.ts +++ b/webpack.extensions.ts @@ -89,14 +89,13 @@ export default function generateExtensionTypes(): webpack.Configuration { { test: /\.s?css$/, use: [ - // creates `style` nodes from JS strings "style-loader", - // translates CSS into CommonJS "css-loader", + "postcss-loader", { loader: "sass-loader", options: { - prependData: `@import "${path.basename(sassCommonVars)}";`, + additionalData: `@import "${path.basename(sassCommonVars)}";`, sassOptions: { includePaths: [ path.dirname(sassCommonVars), diff --git a/webpack.renderer.ts b/webpack.renderer.ts index 5ade45b6d3..4e07d42ffb 100755 --- a/webpack.renderer.ts +++ b/webpack.renderer.ts @@ -119,21 +119,30 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura { loader: "css-loader", options: { + sourceMap: isDevelopment, modules: { - auto: true, - mode: "local", + auto: /\.module\./i, // https://github.com/webpack-contrib/css-loader#auto + mode: "local", // :local(.selector) by default localIdentName: "[name]__[local]--[hash:base64:5]", }, }, }, { loader: "postcss-loader", + options: { + sourceMap: isDevelopment, + postcssOptions: { + plugins: [ + "tailwindcss", + ], + }, + }, }, { loader: "sass-loader", options: { sourceMap: isDevelopment, - prependData: `@import "${path.basename(sassCommonVars)}";`, + additionalData: `@import "${path.basename(sassCommonVars)}";`, sassOptions: { includePaths: [ path.dirname(sassCommonVars), @@ -158,21 +167,6 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura globalAPI: isDevelopment, }), - // todo: fix remain warnings about circular dependencies - // new CircularDependencyPlugin({ - // cwd: __dirname, - // exclude: /node_modules/, - // allowAsyncCycles: true, - // failOnError: false, - // }), - - // todo: check if this actually works in mode=production files - // new webpack.DllReferencePlugin({ - // context: process.cwd(), - // manifest: manifestPath, - // sourceType: libraryTarget, - // }), - new HtmlWebpackPlugin({ filename: `${appName}.html`, template: htmlTemplate, diff --git a/yarn.lock b/yarn.lock index e791116113..43abd52c52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3356,7 +3356,7 @@ bytes@3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= -bytes@3.1.0, bytes@^3.0.0: +bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== @@ -3747,15 +3747,6 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" @@ -3915,11 +3906,6 @@ commander@^5.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== -commander@^6.0.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" - integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== - commander@^8.2.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -4283,11 +4269,6 @@ css-box-model@^1.2.0: dependencies: tiny-invariant "^1.0.6" -css-color-names@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" - integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= - css-loader@^5.2.7: version "5.2.7" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae" @@ -4329,11 +4310,6 @@ css-selector-tokenizer@^0.7.0: cssesc "^3.0.0" fastparse "^1.1.2" -css-unit-converter@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.2.tgz#4c77f5a1954e6dbff60695ecb214e3270436ab21" - integrity sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA== - css-vendor@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-2.0.8.tgz#e47f91d3bd3117d49180a3c935e62e3d9f7f449d" @@ -6478,7 +6454,7 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob-parent@^6.0.1: +glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== @@ -6820,11 +6796,6 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -hex-color-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" - integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== - history@^4.10.1, history@^4.9.0: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" @@ -6882,16 +6853,6 @@ hpack.js@^2.1.6: readable-stream "^2.0.1" wbuf "^1.1.0" -hsl-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" - integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= - -hsla-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" - integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= - html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -6922,11 +6883,6 @@ html-minifier-terser@^5.0.1: relateurl "^0.2.7" terser "^4.6.3" -html-tags@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" - integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== - html-webpack-plugin@^4.5.2: version "4.5.2" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz#76fc83fa1a0f12dd5f7da0404a54e2699666bc12" @@ -7476,18 +7432,6 @@ is-cidr@^3.0.0: dependencies: cidr-regex "^2.0.10" -is-color-stop@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" - integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= - dependencies: - css-color-names "^0.0.4" - hex-color-regex "^1.1.0" - hsl-regex "^1.0.0" - hsla-regex "^1.0.0" - rgb-regex "^1.0.1" - rgba-regex "^1.0.0" - is-core-module@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" @@ -8686,9 +8630,9 @@ kleur@^3.0.3: integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== klona@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" - integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== + version "2.0.5" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" + integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== kuler@^2.0.0: version "2.0.0" @@ -9059,11 +9003,6 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.topath@^4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009" - integrity sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak= - lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" @@ -9583,11 +9522,6 @@ mock-fs@^5.1.2: resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-5.1.2.tgz#6fa486e06d00f8793a8d2228de980eff93ce6db7" integrity sha512-YkjQkdLulFrz0vD4BfNQdQRVmgycXTV7ykuHMlyv+C8WCHazpkiQRDthwa02kSyo8wKnY9wRptHfQLgmf0eR+A== -modern-normalize@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.1.0.tgz#da8e80140d9221426bd4f725c6e11283d34f90b7" - integrity sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA== - moment-timezone@^0.5.34: version "0.5.34" resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c" @@ -9727,6 +9661,11 @@ neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -9757,13 +9696,6 @@ node-addon-api@^4.2.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.2.0.tgz#117cbb5a959dff0992e1c586ae0393573e4d2a87" integrity sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q== -node-emoji@^1.11.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" - integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== - dependencies: - lodash "^4.17.21" - node-fetch-npm@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz#6507d0e17a9ec0be3bec516958a497cec54bf5a4" @@ -11018,7 +10950,7 @@ postcss-load-config@^3.0.1, postcss-load-config@^3.1.0: lilconfig "^2.0.3" yaml "^1.10.2" -postcss-loader@4.3.0: +postcss-loader@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.3.0.tgz#2c4de9657cd4f07af5ab42bd60a673004da1b8cc" integrity sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q== @@ -11072,6 +11004,14 @@ postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-selector-parser@^6.0.7: + version "6.0.7" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.7.tgz#48404830a635113a71fd79397de8209ed05a66fc" + integrity sha512-U+b/Deoi4I/UmE6KOVPpnhS7I7AYdKbhGcat+qTQ27gycvaACvNEw11ba6RrkwVmDVRW7sigWgLj4/KbbJjeDA== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-value-parser@^3.3.0: version "3.3.1" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" @@ -11082,6 +11022,11 @@ postcss-value-parser@^4.1.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== +postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + postcss@^6.0.14, postcss@^6.0.2: version "6.0.23" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" @@ -11091,7 +11036,7 @@ postcss@^6.0.14, postcss@^6.0.2: source-map "^0.6.1" supports-color "^5.4.0" -postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.15, postcss@^8.3.0, postcss@^8.4.5: +postcss@^8.1.6, postcss@^8.2.15, postcss@^8.3.0, postcss@^8.4.5: version "8.4.5" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95" integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== @@ -11100,11 +11045,6 @@ postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.15, postcss@^8.3.0, postcss@^8.4.5: picocolors "^1.0.0" source-map-js "^1.0.1" -postinstall-postinstall@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" - integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ== - prebuild-install@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.0.tgz#3c5ce3902f1cb9d6de5ae94ca53575e4af0c1574" @@ -11172,11 +11112,6 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-hrtime@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" - integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -11365,16 +11300,6 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" -purgecss@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-4.0.3.tgz#8147b429f9c09db719e05d64908ea8b672913742" - integrity sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw== - dependencies: - commander "^6.0.0" - glob "^7.0.0" - postcss "^8.2.1" - postcss-selector-parser "^6.0.2" - qrcode-terminal@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz#bb5b699ef7f9f0505092a3748be4464fe71b5819" @@ -11814,14 +11739,6 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -reduce-css-calc@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz#7ef8761a28d614980dc0c982f772c93f7a99de03" - integrity sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg== - dependencies: - css-unit-converter "^1.1.1" - postcss-value-parser "^3.3.0" - redux@^4.0.0, redux@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" @@ -12113,16 +12030,6 @@ rfc6902@^4.0.2: resolved "https://registry.yarnpkg.com/rfc6902/-/rfc6902-4.0.2.tgz#ce99d3562b9e3287d403462e6bcc81eead8fcea0" integrity sha512-MJOC4iDSv3Qn5/QvhPbrNoRongti6moXSShcRmtbNqOk0WPxlviEdMV4bb9PaULhSxLUXzWd4AjAMKQ3j3y54w== -rgb-regex@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" - integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= - -rgba-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" - integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= - rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -12225,18 +12132,18 @@ sanitize-filename@^1.6.2, sanitize-filename@^1.6.3: dependencies: truncate-utf8-bytes "^1.0.0" -sass-loader@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-8.0.2.tgz#debecd8c3ce243c76454f2e8290482150380090d" - integrity sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ== +sass-loader@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.2.0.tgz#3d64c1590f911013b3fa48a0b22a83d5e1494716" + integrity sha512-kUceLzC1gIHz0zNJPpqRsJyisWatGYNFRmv2CKZK2/ngMJgLqxTbXwe/hJ85luyvZkgqU3VlJ33UVF2T/0g6mw== dependencies: - clone-deep "^4.0.1" - loader-utils "^1.2.3" - neo-async "^2.6.1" - schema-utils "^2.6.1" - semver "^6.3.0" + klona "^2.0.4" + loader-utils "^2.0.0" + neo-async "^2.6.2" + schema-utils "^3.0.0" + semver "^7.3.2" -sass@^1.32.13, sass@^1.44.0: +sass@^1.32.13: version "1.44.0" resolved "https://registry.yarnpkg.com/sass/-/sass-1.44.0.tgz#619aa0a2275c097f9af5e6b8fe8a95e3056430fb" integrity sha512-0hLREbHFXGQqls/K8X+koeP+ogFRPF4ZqetVB19b7Cst9Er8cOR0rc6RU7MaI4W1JmUShd1BPgPoeqmmgMMYFw== @@ -12244,6 +12151,15 @@ sass@^1.32.13, sass@^1.44.0: chokidar ">=3.0.0 <4.0.0" immutable "^4.0.0" +sass@^1.45.1: + version "1.45.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.45.1.tgz#fa03951f924d1ba5762949567eaf660e608a1ab0" + integrity sha512-pwPRiq29UR0o4X3fiQyCtrESldXvUQAAE0QmcJTpsI4kuHHcLzZ54M1oNBVIXybQv8QF2zfkpFcTxp8ta97dUA== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -12282,7 +12198,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.6.1, schema-utils@^2.6.5: +schema-utils@^2.6.5: version "2.7.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== @@ -12454,13 +12370,6 @@ sha@^3.0.0: dependencies: graceful-fs "^4.1.2" -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - sharp@^0.29.3: version "0.29.3" resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.29.3.tgz#0da183d626094c974516a48fab9b3e4ba92eb5c2" @@ -12704,7 +12613,7 @@ source-list-map@^2.0.0, source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^1.0.1: +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf" integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA== @@ -13259,41 +13168,30 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" -tailwindcss@^2.2.19: - version "2.2.19" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.19.tgz#540e464832cd462bb9649c1484b0a38315c2653c" - integrity sha512-6Ui7JSVtXadtTUo2NtkBBacobzWiQYVjYW0ZnKaP9S1ZCKQ0w7KVNz+YSDI/j7O7KCMHbOkz94ZMQhbT9pOqjw== +tailwindcss@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.7.tgz#15936881f042a7eb8d6f2b6a454bac9f51181bbd" + integrity sha512-rZdKNHtC64jcQncLoWOuCzj4lQDTAgLtgK3WmQS88tTdpHh9OwLqULTQxI3tw9AMJsqSpCKlmcjW/8CSnni6zQ== dependencies: arg "^5.0.1" - bytes "^3.0.0" chalk "^4.1.2" chokidar "^3.5.2" - color "^4.0.1" + color-name "^1.1.4" cosmiconfig "^7.0.1" detective "^5.2.0" didyoumean "^1.2.2" dlv "^1.1.3" fast-glob "^3.2.7" - fs-extra "^10.0.0" - glob-parent "^6.0.1" - html-tags "^3.1.0" - is-color-stop "^1.1.0" - is-glob "^4.0.1" - lodash "^4.17.21" - lodash.topath "^4.5.2" - modern-normalize "^1.1.0" - node-emoji "^1.11.0" + glob-parent "^6.0.2" + is-glob "^4.0.3" normalize-path "^3.0.0" object-hash "^2.2.0" postcss-js "^3.0.3" postcss-load-config "^3.1.0" postcss-nested "5.0.6" - postcss-selector-parser "^6.0.6" - postcss-value-parser "^4.1.0" - pretty-hrtime "^1.0.3" - purgecss "^4.0.3" + postcss-selector-parser "^6.0.7" + postcss-value-parser "^4.2.0" quick-lru "^5.1.1" - reduce-css-calc "^2.1.8" resolve "^1.20.0" tmp "^0.2.1"