Turn on strict mode in tsconfig.json, some helpful lints, and required cleanup where strictness necessitates it (#5195)

* Turn on strict mode in tsconfig.json

- Add route, clusterRoute, and payloadValidatedClusterRoute helper
  functions to improve types with backend routes

- Turn on the following new lints:
  - react/jsx-first-prop-new-line
  - react/jsx-wrap-multilines
  - react/jsx-one-expression-per-line
  - react/jsx-max-props-per-line
  - react/jsx-indent
  - react/jsx-indent-props

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix build

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Replace KubeObject scope strings with enum

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Revert package.json version changes

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* revert move hostedCluster(Id)

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* change some type param names to be not single letters

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* remove copy-extension-themes

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* add new make clean action

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix build to not use webpack for generating types

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix kube-object-menu.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix select.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix catalog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* revert move fileNameMigration to index

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix ref logic error

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix log-resource-selector.test.tsx tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix dock-store.test.ts test by overriding createStorage to not touch file system

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix cluster.test.ts tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix kube=api.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fixed hotbar-store.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix kubeconfig-manager.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix cluster-role-bindings/__tests__/dialog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix role-bindings/__tests__/dialog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix pods.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix delete-cluster-dialog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix daemonset.store.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix replicaset.store.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix statefulsets/dialog/dialog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix replicasets/scale-dialog/dialog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix deployments.store.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix deployments/scale/dialog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix cronjob.store.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix stateful-set.api.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix deployment.api.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix api-manager.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix statefulset.store.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix job.store.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix pods.store.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix scroll-spy.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix hotbar-remove-command.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix catalog-entity-registry.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix welcome.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix verify-that-all-routes-have-route-component.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix pod-tolerations.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* better fix for previous 3 fixes, plus also select.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix kube-object-menu.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix app-paths.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix dock-tabs.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix isReactNode typing

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix sub-title.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix drawer.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix list-layout.tsx and header.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix error-boundary.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix upgrade-chart/store.ts and dock-tab.store.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix install-chart/store.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix edit-resource/store.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix create-resource/store.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix namespace-select.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix namespace-select-filter.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix crd-list.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix wrong types for extensions

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix circular dependency

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix circular dependency on catalogCategoryRegistry

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix api-kube

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix type errors, most <Select /> errors

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fixing more type errors

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* some more fixing type errors

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* convert all KubeApis to injectable with legacy global backups

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* factor out into a common file all the exports

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* convert all KubeObjectStores to injectable with legacy global backups

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix lint

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* remove unused legacy KubeApi globals

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix bad previous commit

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* more crash fixing

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* try and fix behavioural tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix sidebar-and-tab-navigation-for-core.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix sidebar-and-tab-navigation-for-extensions.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix navigation-using-application-menu.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix catalog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Make ThemeStore non-singleton and fix navigation-to-terminal-preferences.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* extensions.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix catalog-entity-registry.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix navigation-using-application-menu.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix log-resource-selector.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix dock-tabs.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix delete-cluster-dialog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix navigation-to-kubernetes-preferences.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix navigation-to-editor-preferences.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix navigation-to-proxy-preferences.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix navigation-using-application-menu.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix navigation-to-application-preferences.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix dock-store.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix select.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix role-bindings/__tests__/dialog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix hotbar-remove-command.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix cluster-role-bindings/__tests__/dialog.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix navigation-to-extension-specific-preferences.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix navigation-to-telemetry-preferences.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix closing-preferences.test.tsx

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix navigation-to-editor-preferences.test.ts

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix navigation-to-proxy-preferences.test.ts

- Fix other type errors too

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* final tweaks

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add more tsconfig files, fix bug in <Catalog>

- Make all of history, navigation injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix type errors

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Convert all of kube-details-params/ and navigate/ to injectable

- This fixes a runtime error that was encountered during testing

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix runtime errors on renderer

- remove all static uses of `createPageParam` (and then removed the
  legacy global)
- Made LensRendererExtension and LensMainExtension just used
  dependencies and not the getLegacyDi
- Fixed circular dep in extension-loader

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Move registerStore calls to after injectMany

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* replace all the rest of the legacy uses of apiManager

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix stack overflow and cycles in DI

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix NamespaceSelectFilter not opening

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix WizardStep and AddNamespaceDialog

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix KubeApi's not being registered

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* cleanup WindowManager

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Proper fix for Wizard, fix NamespaceStore.subscribe

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Rewrite withTooltip to be more type correct

- Fixes mobx related "too many recursive actions" error

- Change all the uses of withTooltips to be functional components

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add e2e test to cover kube api registration

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* cleanup internal-commands

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* remove cast in <Animate>

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix command-palette e2e test

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix type error after rebase

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix test name

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix lint

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix code to help CodeQL scanner

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* update intree extension lock files

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix build-extensions picking wrong @types/react

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix tests from rebase

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix type error

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Make KubeconfigSyncManager more injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix crash in test mode for Dialog

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* make Select snapshots deterministic

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix new type error

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix kube-object.store.test.ts typing

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix merge build issues

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix snapshots after merge

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix lint after merge

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* reexport BaseKubeJsonApiObjectMetadata

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix typo in terminalSpawningPool

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* remove duplicate license header

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix typo to waitUntilDefined

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* remove iter use from getLegacyGlobalDiForExtensionApi

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* remove complex createStorage override

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* override logger with mocks only when needed for tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* remove specialized overrideStore flags for getDiForUnitTesting

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* remove unnecessary | undefined types from the exactOptionalFieldTypes experiment

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* use more descriptive name for local test mocks

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* remove unnecessary addition to 'make clean' target

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* remove oddity of KubeObjectStore.getById(undefined) being allowed

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* rename KubeObject.getDescriptor in favour of name without fundemental JS meaning

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Simplify legacyRegisterApi when working in behaviour unit tests

- Don't emit within main environment as there should be no auto
  registering there

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* change confusing variable name in ReactiveDuration

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* make visitor pattern more explicit for Entity contextMenuOpen

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* toggleDetails -> toggleKubeDetailsPane is more specific

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* remove outdated comment

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix bug where LensExtension dependencies are not set

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Fix tests from the revert of react 18

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix more tests from merge

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix typings with new is-compatible-extension tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* more type fixing

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Revert in-tree extension versions

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Improve name of guarding injectable for stores and apis

- New name better implies that it is just a guard state and does not do
  anything

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Add helper for <Select>.isMulti for storing in a Set<Value>

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* fix is-compatible-extension.test.ts types

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton authored and GitHub committed 2022-05-16 07:17:57 -04:00
1 parent 381d77c633
commit dfcb7c3330
1062 files changed
+26512 -28689

No files matched your search

+25 -1
View File
@@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { reduce } from "../iter";
import { join, nth, reduce } from "../iter";
describe("iter", () => {
describe("reduce", () => {
@@ -15,4 +15,28 @@ describe("iter", () => {
expect(reduce([], (acc: number[], current: number) => [acc[0] + current], [])).toEqual([]);
});
});
describe("join", () => {
it("should not prefix the output by the seperator", () => {
expect(join(["a", "b", "c"].values(), " ")).toBe("a b c");
});
it("should return empty string if iterator is empty", () => {
expect(join([].values(), " ")).toBe("");
});
it("should return just first entry if iterator is of size 1", () => {
expect(join(["d"].values(), " ")).toBe("d");
});
});
describe("nth", () => {
it("should return undefined past the end of the iterator", () => {
expect(nth(["a"], 123)).toBeUndefined();
});
it("should by 0-indexing the index", () => {
expect(nth(["a", "b"], 0)).toBe("a");
});
});
});
+3 -2
View File
@@ -6,11 +6,12 @@
import type { Options } from "auto-bind";
import autoBindClass from "auto-bind";
import autoBindReactClass from "auto-bind/react";
import React from "react";
// Automatically bind methods to their class instance
export function autoBind<T extends object>(obj: T, opts?: Options): T {
if ("componentWillUnmount" in obj) {
return autoBindReactClass(obj as any, opts);
if (obj instanceof React.Component) {
return autoBindReactClass(obj, opts);
}
return autoBindClass(obj, opts);
+7 -1
View File
@@ -4,6 +4,12 @@
*/
import { compile } from "path-to-regexp";
import type { RouteProps } from "react-router";
import { isDefined } from "./type-narrowing";
export interface UrlRouteProps extends RouteProps {
path: string;
}
export interface URLParams<P extends object = {}, Q extends object = {}> {
params?: P;
@@ -21,7 +27,7 @@ export function buildURL<P extends object = {}, Q extends object = {}>(path: str
fragment && `#${fragment}`,
];
return parts.filter(Boolean).join("");
return parts.filter(isDefined).join("");
}
export function buildURLPositional<P extends object = {}, Q extends object = {}>(path: string) {
+16 -12
View File
@@ -4,22 +4,26 @@
*/
// Convert object's keys to camelCase format
import { camelCase, isPlainObject } from "lodash";
import { camelCase } from "lodash";
import type { SingleOrMany } from "./types";
import { isObject } from "./type-narrowing";
export function toCamelCase(obj: Record<string, any>): any {
export function toCamelCase<T extends Record<string, unknown>[]>(obj: T): T;
export function toCamelCase<T extends Record<string, unknown>>(obj: T): T;
export function toCamelCase(obj: SingleOrMany<Record<string, unknown> | unknown>): SingleOrMany<Record<string, unknown> | unknown> {
if (Array.isArray(obj)) {
return obj.map(toCamelCase);
}
else if (isPlainObject(obj)) {
return Object.keys(obj).reduce((result, key) => {
const value = obj[key];
result[camelCase(key)] = typeof value === "object" ? toCamelCase(value) : value;
if (isObject(obj)) {
return Object.fromEntries(
Object.entries(obj)
.map(([key, value]) => {
return [camelCase(key), toCamelCase(value)];
}),
);
}
return result;
}, {} as any);
}
else {
return obj;
}
return obj;
}
@@ -25,24 +25,3 @@ export function getClusterIdFromHost(host: string): ClusterId | undefined {
export function getClusterFrameUrl(clusterId: ClusterId) {
return `//${clusterId}.${location.host}`;
}
/**
* Get the result of `getClusterIdFromHost` from the current `location.host`
*/
export function getHostedClusterId(): ClusterId | undefined {
// catch being called in main
if (typeof location === "undefined") {
return undefined;
}
return getClusterIdFromHost(location.host);
}
/**
* Returns true only if code is running within a cluster iframe context
*/
export function isClusterPageContext(): boolean {
if (typeof window === "undefined") return false;
return !!getClusterIdFromHost(window.location.host);
}
+28 -6
View File
@@ -4,6 +4,8 @@
*/
import { runInAction } from "mobx";
import { inspect } from "util";
import { isDefined } from "./type-narrowing";
/**
* Get the value behind `key`. If it was not present, first insert `value`
@@ -17,7 +19,17 @@ export function getOrInsert<K, V>(map: Map<K, V>, key: K, value: V): V {
map.set(key, value);
}
return map.get(key);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return map.get(key)!;
}
/**
* Updates map and returns the value that was just inserted
*/
export function put<K, V>(map: Map<K, V>, key: K, value: V): V {
map.set(key, value);
return value;
}
/**
@@ -45,7 +57,8 @@ export function getOrInsertWith<K, V>(map: Map<K, V>, key: K, builder: () => V):
map.set(key, builder());
}
return map.get(key);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return map.get(key)!;
}
/**
@@ -56,7 +69,8 @@ export async function getOrInsertWithAsync<K, V>(map: Map<K, V>, key: K, asyncBu
map.set(key, await asyncBuilder());
}
return map.get(key);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return map.get(key)!;
}
/**
@@ -67,7 +81,7 @@ export async function getOrInsertWithAsync<K, V>(map: Map<K, V>, key: K, asyncBu
*/
export function strictSet<K, V>(map: Map<K, V>, key: K, val: V): typeof map {
if (map.has(key)) {
throw new TypeError("Duplicate key in map");
throw new TypeError(`Map already contains key: ${inspect(key)}`);
}
return map.set(key, val);
@@ -80,10 +94,11 @@ export function strictSet<K, V>(map: Map<K, V>, key: K, val: V): typeof map {
*/
export function strictGet<K, V>(map: Map<K, V>, key: K): V {
if (!map.has(key)) {
throw new TypeError("key not in map");
throw new TypeError(`Map does not contains key: ${inspect(key)}`);
}
return map.get(key);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return map.get(key)!;
}
/**
@@ -99,3 +114,10 @@ export function toggle<K>(set: Set<K>, key: K): void {
}
});
}
/**
* A helper function to also check for defined-ness
*/
export function includes<T>(src: T[], value: T | null | undefined): boolean {
return isDefined(value) && src.includes(value);
}
+1 -1
View File
@@ -11,7 +11,7 @@ interface Extendable<T> {
export type ExtendableDisposer = Disposer & Extendable<Disposer>;
export function disposer(...args: Disposer[]): ExtendableDisposer {
export function disposer(...args: (Disposer | undefined | null)[]): ExtendableDisposer {
const res = () => {
args.forEach(dispose => dispose?.());
args.length = 0;
+4 -2
View File
@@ -4,6 +4,8 @@
*/
import request from "request";
import type { JsonValue } from "type-fest";
import { parse } from "./json";
export interface DownloadFileOptions {
url: string;
@@ -41,11 +43,11 @@ export function downloadFile({ url, timeout, gzip = true }: DownloadFileOptions)
};
}
export function downloadJson(args: DownloadFileOptions): DownloadFileTicket<any> {
export function downloadJson(args: DownloadFileOptions): DownloadFileTicket<JsonValue> {
const { promise, ...rest } = downloadFile(args);
return {
promise: promise.then(res => JSON.parse(res.toString())),
promise: promise.then(res => parse(res.toString())),
...rest,
};
}
+6 -2
View File
@@ -14,7 +14,6 @@ export * from "./abort-controller";
export * from "./app-version";
export * from "./autobind";
export * from "./camelCase";
export * from "./cloneJson";
export * from "./cluster-id-url-parsing";
export * from "./collection-functions";
export * from "./convertCpu";
@@ -28,7 +27,6 @@ export * from "./formatDuration";
export * from "./getRandId";
export * from "./hash-set";
export * from "./n-fircate";
export * from "./objects";
export * from "./openBrowser";
export * from "./paths";
export * from "./promise-exec";
@@ -40,7 +38,9 @@ export * from "./splitArray";
export * from "./tar";
export * from "./toJS";
export * from "./type-narrowing";
export * from "./types";
export * from "./wait-for-path";
export * from "./wait";
export type { Tuple } from "./tuple";
@@ -48,10 +48,14 @@ import * as iter from "./iter";
import * as array from "./array";
import * as tuple from "./tuple";
import * as base64 from "./base64";
import * as object from "./objects";
import * as json from "./json";
export {
iter,
array,
tuple,
base64,
object,
json,
};
@@ -12,14 +12,14 @@ export type IsAllowedResource = (resource: KubeResource) => boolean;
const isAllowedResourceInjectable = getInjectable({
id: "is-allowed-resource",
instantiate: (di, resourceName: KubeResource) => {
instantiate: (di, resourceName: string) => {
const allowedResources = di.inject(allowedResourcesInjectable);
return computed(() => allowedResources.get().has(resourceName));
},
lifecycle: lifecycleEnum.keyedSingleton({
getInstanceKey: (di, resource: KubeResource) => resource,
getInstanceKey: (di, resource: string) => resource,
}),
});
+46 -16
View File
@@ -5,14 +5,31 @@
export type Falsey = false | 0 | "" | null | undefined;
interface Iterator<T> {
filter(fn: (val: T) => unknown): Iterator<T>;
filterMap<U>(fn: (val: T) => Falsey | U): Iterator<U>;
find(fn: (val: T) => unknown): T | undefined;
collect<U>(fn: (values: Iterable<T>) => U): U;
map<U>(fn: (val: T) => U): Iterator<U>;
join(sep?: string): string;
}
export function pipeline<T>(src: IterableIterator<T>): Iterator<T> {
return {
filter: (fn) => pipeline(filter(src, fn)),
filterMap: (fn) => pipeline(filterMap(src, fn)),
map: (fn) => pipeline(map(src, fn)),
find: (fn) => find(src, fn),
join: (sep) => join(src, sep),
collect: (fn) => fn(src),
};
}
/**
* Create a new type safe empty Iterable
* @returns An `Iterable` that yields 0 items
*/
// eslint-disable-next-line require-yield
export function* newEmpty<T>(): IterableIterator<T> {
return;
}
export function* newEmpty<T>(): IterableIterator<T> {}
/**
* Creates a new `Iterable` that yields at most n items from src.
@@ -167,25 +184,38 @@ export function reduce<T, R = T>(src: Iterable<T>, reducer: (acc: R, cur: T) =>
* @param connector The string value to intersperse between the yielded values
* @returns The concatenated entries of `src` interspersed with copies of `connector`
*/
export function join(src: Iterable<string>, connector = ","): string {
return reduce(src, (acc, cur) => `${acc}${connector}${cur}`, "");
export function join(src: IterableIterator<unknown>, connector = ","): string {
const iterSrc = src[Symbol.iterator]();
const first = iterSrc.next();
if (first.done === true) {
return "";
}
return reduce(iterSrc, (acc, cur) => `${acc}${connector}${cur}`, `${first.value}`);
}
/**
* Returns the next value after iterating over the iterable `index` times.
*
* For example: `nth(["a", "b"], 0)` will return `"a"`
* For example: `nth(["a", "b"], 1)` will return `"b"`
* For example: `nth(["a", "b"], 2)` will return `undefined`
* Iterate `n` times and then return the next value.
* @param src The value to iterate over
* @param n The zero-index value for the item to return to.
*/
export function nth<T>(src: Iterable<T>, index: number): T | undefined {
const iteree = src[Symbol.iterator]();
export function nth<T>(src: Iterable<T>, n: number): T | undefined {
const iterator = src[Symbol.iterator]();
while (index-- > 0) {
iteree.next();
while (n --> 0) {
iterator.next();
}
return iteree.next().value;
return iterator.next().value;
}
/**
* A convenience function to get the first item of an iterator
* @param src The value to iterate over
*/
export function first<T>(src: Iterable<T>): T | undefined {
return nth(src, 0);
}
/**
@@ -3,8 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
// Clone json-serializable object
import type { JsonValue } from "type-fest";
export function cloneJsonObject<T = object>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
export function parse(input: string): JsonValue {
return JSON.parse(input);
}
+2 -1
View File
@@ -23,7 +23,8 @@ export function lazyInitialized<T>(builder: () => T): LazyInitialized<T> {
return {
get() {
if (called) {
return value;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return value!;
}
value = builder();
+16 -3
View File
@@ -8,9 +8,22 @@
* be a specific subset
*/
export function fromEntries<T, Key extends string>(entries: Iterable<readonly [Key, T]>): Record<Key, T> {
return Object.fromEntries(entries) as { [k in Key]: T };
return Object.fromEntries(entries) as Record<Key, T>;
}
export function entries<T extends Record<string, any>>(obj: T): [keyof T, T[keyof T]][] {
return Object.entries(obj);
export function keys<K extends keyof any>(obj: Partial<Record<K, any>>): K[];
export function keys<K extends keyof any>(obj: Record<K, any>): K[] {
return Object.keys(obj) as K[];
}
export function entries<K extends string | number | symbol, V>(obj: Partial<Record<K, V>> | null | undefined): [K, V][];
export function entries<K extends string | number | symbol, V>(obj: Record<K, V> | null | undefined): [K, V][];
export function entries<K extends string | number | symbol, V>(obj: Record<K, V> | null | undefined): [K, V][] {
if (obj && typeof obj == "object") {
return Object.entries(obj) as never;
}
return [] as never;
}
+1 -1
View File
@@ -24,7 +24,7 @@ export class ReadableWebToNodeStream<T extends TypedArray> extends Readable {
* https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader
*/
private reader: ReadableStreamReader<T>;
private pendingRead: Promise<ReadableStreamDefaultReadResult<T>>;
private pendingRead?: Promise<ReadableStreamDefaultReadResult<T>>;
/**
*
+2
View File
@@ -51,6 +51,8 @@ export class Singleton {
* Default: `true`
* @returns An instance of the child class
*/
static getInstance<T, R extends any[]>(this: StaticThis<T, R>, strict?: true): T;
static getInstance<T, R extends any[]>(this: StaticThis<T, R>, strict: false): T | undefined;
static getInstance<T, R extends any[]>(this: StaticThis<T, R>, strict = true): T | undefined {
if (!Singleton.instances.has(this) && strict) {
throw new TypeError(`instance of ${this.name} is not created`);
+4 -4
View File
@@ -51,7 +51,7 @@ export function sortCompare<T>(left: T, right: T): Ordering {
interface ChartVersion {
version: string;
__version?: SemVer;
__version?: SemVer | null;
}
export function sortCompareChartVersions(left: ChartVersion, right: ChartVersion): Ordering {
@@ -74,13 +74,13 @@ export function sortCompareChartVersions(left: ChartVersion, right: ChartVersion
export function sortCharts(charts: RawHelmChart[]) {
interface ExtendedHelmChart extends RawHelmChart {
__version: SemVer;
__version?: SemVer | null;
}
const chartsWithVersion = Array.from(
iter.map(
charts,
(chart => {
chart => {
const __version = coerce(chart.version, { includePrerelease: true, loose: true });
if (!__version) {
@@ -90,7 +90,7 @@ export function sortCharts(charts: RawHelmChart[]) {
(chart as ExtendedHelmChart).__version = __version;
return chart as ExtendedHelmChart;
}),
},
),
);
+18 -6
View File
@@ -8,14 +8,26 @@
import type { ExtractOptions, FileStat } from "tar";
import tar from "tar";
import path from "path";
import { parse } from "./json";
import type { JsonValue } from "type-fest";
export interface ReadFileFromTarOpts {
export type ReadFileFromTarOpts<ParseJson extends boolean> = {
tarPath: string;
filePath: string;
parseJson?: boolean;
}
} & (
ParseJson extends true
? {
parseJson: true;
}
: {
parseJson?: false;
}
);
export function readFileFromTar<R = Buffer>({ tarPath, filePath, parseJson }: ReadFileFromTarOpts): Promise<R> {
export function readFileFromTar(opts: ReadFileFromTarOpts<false>): Promise<Buffer>;
export function readFileFromTar(opts: ReadFileFromTarOpts<true>): Promise<JsonValue>;
export function readFileFromTar<ParseJson extends boolean>({ tarPath, filePath, parseJson = false }: ReadFileFromTarOpts<ParseJson>): Promise<JsonValue | Buffer> {
return new Promise((resolve, reject) => {
const fileChunks: Buffer[] = [];
@@ -32,7 +44,7 @@ export function readFileFromTar<R = Buffer>({ tarPath, filePath, parseJson }: Re
});
entry.once("end", () => {
const data = Buffer.concat(fileChunks);
const result = parseJson ? JSON.parse(data.toString("utf8")) : data;
const result = parseJson ? parse(data.toString("utf8")) : data;
resolve(result);
});
@@ -51,7 +63,7 @@ export async function listTarEntries(filePath: string): Promise<string[]> {
await tar.list({
file: filePath,
onentry: (entry: FileStat) => {
entries.push(path.normalize(entry.path as any as string));
entries.push(path.normalize(entry.path as unknown as string));
},
});
+3
View File
@@ -24,6 +24,9 @@ type TupleOfImpl<T, N extends number, R extends unknown[]> = R["length"] extends
* @yields A tuple of the next element from each of the sources
* @returns The tuple of all the sources as soon as at least one of the sources is exausted
*/
export function zip<T>(src1: T[]): Iterator<[T], Tuple<T[], 1>>;
export function zip<T>(src1: T[], src2: T[]): Iterator<[T, T], Tuple<T[], 2>>;
export function* zip<T, N extends number>(...sources: Tuple<T[], N>): Iterator<Tuple<T, N>, Tuple<T[], N>> {
const maxSafeLength = Math.min(...sources.map(source => source.length));
+111 -1
View File
@@ -3,6 +3,9 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ExecException, ExecFileException } from "child_process";
import type { IncomingMessage } from "http";
/**
* Narrows `val` to include the property `key` (if true is returned)
* @param val The object to be tested
@@ -38,7 +41,7 @@ export function hasTypedProperty<S extends object, K extends PropertyKey, V>(val
* @param key The key to test if it is present on the object (must be a literal for tsc to do any meaningful typing)
* @param isValid a function to check if the field (when present) is valid
*/
export function hasOptionalProperty<S extends object, K extends PropertyKey, V>(val: S, key: K, isValid: (value: unknown) => value is V): val is (S & { [key in K]?: V }) {
export function hasOptionalTypedProperty<S extends object, K extends PropertyKey, V>(val: S, key: K, isValid: (value: unknown) => value is V): val is (S & { [key in K]?: V }) {
if (hasOwnProperty(val, key)) {
return typeof val[key] === "undefined" || isValid(val[key]);
}
@@ -73,6 +76,21 @@ export function isString(val: unknown): val is string {
return typeof val === "string";
}
/**
* checks if val is of type Buffer
* @param val the value to be checked
*/export function isBuffer(val: unknown): val is Buffer {
return val instanceof Buffer;
}
/**
* checks if val is of type number
* @param val the value to be checked
*/
export function isNumber(val: unknown): val is number {
return typeof val === "number";
}
/**
* checks if val is of type boolean
* @param val the value to be checked
@@ -89,6 +107,20 @@ export function isObject(val: unknown): val is object {
return typeof val === "object" && val !== null;
}
/**
* checks if `val` is defined, useful for filtering out undefined values in a strict manner
*/
export function isDefined<T>(val: T | undefined | null): val is T {
return val != null;
}
/**
* Checks if the value in the second position is non-nullable
*/
export function hasDefinedTupleValue<K, V>(pair: [K, V | undefined | null]): pair is [K, V] {
return pair[1] != null;
}
/**
* Creates a new predicate function (with the same predicate) from `fn`. Such
* that it can be called with just the value to be tested.
@@ -100,3 +132,81 @@ export function isObject(val: unknown): val is object {
export function bindPredicate<FnArgs extends any[], T>(fn: (arg1: unknown, ...args: FnArgs) => arg1 is T, ...boundArgs: FnArgs): (arg1: unknown) => arg1 is T {
return (arg1: unknown): arg1 is T => fn(arg1, ...boundArgs);
}
export function hasDefiniteField<Field extends keyof T, T>(field: Field): (val: T) => val is T & { [f in Field]-?: NonNullable<T[Field]> } {
return (val): val is T & { [f in Field]-?: NonNullable<T[Field]> } => val[field] != null;
}
export function isErrnoException(error: unknown): error is NodeJS.ErrnoException {
return isObject(error)
&& hasOptionalTypedProperty(error, "code", isString)
&& hasOptionalTypedProperty(error, "path", isString)
&& hasOptionalTypedProperty(error, "syscall", isString)
&& hasOptionalTypedProperty(error, "errno", isNumber)
&& error instanceof Error;
}
export function isExecException(error: unknown): error is ExecException {
return isObject(error)
&& hasOptionalTypedProperty(error, "cmd", isString)
&& hasOptionalTypedProperty(error, "killed", isBoolean)
&& hasOptionalTypedProperty(error, "signal", isString)
&& hasOptionalTypedProperty(error, "code", isNumber)
&& error instanceof Error;
}
export function isExecFileException(error: unknown): error is ExecFileException {
return isExecException(error) && isErrnoException(error);
}
export type OutputFormat = "string" | "buffer";
export type ComputeOutputFormat<Format> = Format extends "string"
? string
: Format extends "buffer"
? Buffer
: string | Buffer;
export interface ChildProcessExecpetion<Format> extends ExecFileException {
stderr: ComputeOutputFormat<Format>;
stdout: ComputeOutputFormat<Format>;
}
const isStringOrBuffer = (val: unknown): val is string | Buffer => isString(val) || isBuffer(val);
export function isChildProcessError(error: unknown, format?: OutputFormat): error is ChildProcessExecpetion<typeof format> {
if (!isExecFileException(error)) {
return false;
}
if (format === "string") {
return hasTypedProperty(error, "stderr", isString)
&& hasTypedProperty(error, "stdout", isString);
} else if (format === "buffer") {
return hasTypedProperty(error, "stderr", isBuffer)
&& hasTypedProperty(error, "stdout", isBuffer);
} else {
return hasTypedProperty(error, "stderr", isStringOrBuffer)
&& hasTypedProperty(error, "stdout", isStringOrBuffer);
}
}
export interface RequestLikeError extends Error {
statusCode?: number;
failed?: boolean;
timedOut?: boolean;
error?: string;
response?: IncomingMessage & { body?: any };
}
/**
* A type guard for checking if the error is similar in shape to a request package error
*/
export function isRequestError(error: unknown): error is RequestLikeError {
return isObject(error)
&& hasOptionalTypedProperty(error, "statusCode", isNumber)
&& hasOptionalTypedProperty(error, "failed", isBoolean)
&& hasOptionalTypedProperty(error, "timedOut", isBoolean)
&& hasOptionalTypedProperty(error, "error", isString)
&& hasOptionalTypedProperty(error, "response", isObject)
&& error instanceof Error;
}
+31
View File
@@ -0,0 +1,31 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { SetRequired } from "type-fest";
export type RemoveUndefinedFromValues<K> = {
[P in keyof K]: NonNullable<K[P]>;
};
/**
* This type helps define which fields of some type will always be defined
*/
export type Defaulted<Params, DefaultParams extends keyof Params> = RemoveUndefinedFromValues<Required<Pick<Params, DefaultParams>>> & Omit<Params, DefaultParams>;
export type OptionVarient<Key, Base, RequiredKey extends keyof Base> = {
type: Key;
} & Pick<Base, RequiredKey> & {
[OtherKey in Exclude<keyof Base, RequiredKey>]?: undefined;
};
export type SingleOrMany<T> = T | T[];
export type IfEquals<T, U, Y=unknown, N=never> =
(<G>() => G extends T ? 1 : 2) extends
(<G>() => G extends U ? 1 : 2) ? Y : N;
export type MaybeSetRequired<BaseType, Keys extends keyof BaseType, Query> = Query extends true
? SetRequired<BaseType, Keys>
: BaseType;
+57
View File
@@ -0,0 +1,57 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { IComputedValue } from "mobx";
import { runInAction, when } from "mobx";
import type { Disposer } from "./disposer";
export async function waitUntilDefined<T>(getter: (() => T | null | undefined) | IComputedValue<T | null | undefined>, opts?: { timeout?: number }): Promise<T> {
return new Promise<T>((resolve, reject) => {
let res: T | null | undefined;
when(
() => {
res = typeof getter === "function"
? getter()
: getter.get();
if (res != null) {
resolve(res);
return true;
}
return false;
},
() => {},
{
onError: reject,
...opts,
},
);
});
}
export function onceDefined<T>(getter: () => T | null | undefined, action: (val: T) => void): Disposer {
let res: T | null | undefined;
return when(
() => {
res = getter();
if (res != null) {
const r = res;
runInAction(() => {
action(r);
});
return true;
}
return false;
},
() => {},
);
}