1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Merge branch 'master' into run-tests-on-win

This commit is contained in:
Lauri Nevala 2020-12-02 14:00:09 +02:00
commit 352dfd9b25
19 changed files with 285 additions and 165 deletions

View File

@ -4,6 +4,7 @@ module.exports = {
ignorePatterns: [
"**/node_modules/**/*",
"**/dist/**/*",
"**/static/**/*",
],
settings: {
react: {

View File

@ -1,5 +1,5 @@
{
"name": "extension-example",
"name": "example-extension",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,

View File

@ -1594,6 +1594,7 @@ msgstr "Names"
#: src/renderer/components/dock/upgrade-chart.tsx:98
#: src/renderer/components/item-object-list/page-filters-select.tsx:57
#: src/renderer/components/kube-object/kube-object-meta.tsx:23
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:144
msgid "Namespace"
msgstr "Namespace"
@ -2003,6 +2004,10 @@ msgstr "Read-only Root Filesystem"
msgid "Readiness"
msgstr "Readiness"
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:145
msgid "Ready"
msgstr "Ready"
#: src/renderer/components/+events/event-details.tsx:32
#: src/renderer/components/+workloads-pods/pod-details-container.tsx:25
msgid "Reason"

View File

@ -1585,6 +1585,7 @@ msgstr ""
#: src/renderer/components/dock/upgrade-chart.tsx:98
#: src/renderer/components/item-object-list/page-filters-select.tsx:57
#: src/renderer/components/kube-object/kube-object-meta.tsx:23
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:144
msgid "Namespace"
msgstr ""
@ -1986,6 +1987,10 @@ msgstr ""
msgid "Readiness"
msgstr ""
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:145
msgid "Ready"
msgstr ""
#: src/renderer/components/+events/event-details.tsx:32
#: src/renderer/components/+workloads-pods/pod-details-container.tsx:25
msgid "Reason"

View File

@ -1595,6 +1595,7 @@ msgstr ""
#: src/renderer/components/dock/upgrade-chart.tsx:98
#: src/renderer/components/item-object-list/page-filters-select.tsx:57
#: src/renderer/components/kube-object/kube-object-meta.tsx:23
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:144
msgid "Namespace"
msgstr "Namespace"
@ -2004,6 +2005,10 @@ msgstr ""
msgid "Readiness"
msgstr "Готовность"
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:145
msgid "Ready"
msgstr "Готовы"
#: src/renderer/components/+events/event-details.tsx:32
#: src/renderer/components/+workloads-pods/pod-details-container.tsx:25
msgid "Reason"

View File

@ -37,7 +37,7 @@
"download:kubectl": "yarn run ts-node build/download_kubectl.ts",
"download:helm": "yarn run ts-node build/download_helm.ts",
"build:tray-icons": "yarn run ts-node build/build_tray_icon.ts",
"lint": "yarn run eslint $@ --ext js,ts,tsx --max-warnings=0 src/ integration/ __mocks__/ build/ extensions/",
"lint": "yarn run eslint $@ --ext js,ts,tsx --max-warnings=0 .",
"lint:fix": "yarn run lint --fix",
"mkdocs-serve-local": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -it -p 8000:8000 -v ${PWD}:/docs mkdocs-serve-local:latest",
"verify-docs": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -v ${PWD}:/docs mkdocs-serve-local:latest build --strict",

View File

@ -9,7 +9,7 @@ jest.mock(
() => ({
ipcRenderer: {
invoke: jest.fn(async (channel: string) => {
if (channel === "extensions:loaded") {
if (channel === "extensions:main") {
return [
[
manifestPath,
@ -44,7 +44,7 @@ jest.mock(
}),
on: jest.fn(
(channel: string, listener: (event: any, ...args: any[]) => void) => {
if (channel === "extensions:loaded") {
if (channel === "extensions:main") {
// First initialize with extensions 1 and 2
// and then broadcast event to remove extensioin 2 and add extension number 3
setTimeout(() => {

View File

@ -1,5 +1,6 @@
import { app, ipcRenderer, remote } from "electron";
import { EventEmitter } from "events";
import { isEqual } from "lodash";
import { action, computed, observable, reaction, toJS, when } from "mobx";
import path from "path";
import { getHostedCluster } from "../common/cluster-store";
@ -25,7 +26,12 @@ const logModule = "[EXTENSIONS-LOADER]";
export class ExtensionLoader {
protected extensions = observable.map<LensExtensionId, InstalledExtension>();
protected instances = observable.map<LensExtensionId, LensExtension>();
protected readonly requestExtensionsChannel = "extensions:loaded";
// IPC channel to broadcast changes to extensions from main
protected static readonly extensionsMainChannel = "extensions:main";
// IPC channel to broadcast changes to extensions from renderer
protected static readonly extensionsRendererChannel = "extensions:renderer";
// emits event "remove" of type LensExtension when the extension is removed
private events = new EventEmitter();
@ -95,28 +101,27 @@ export class ExtensionLoader {
this.loadOnMain();
this.broadcastExtensions();
reaction(() => this.extensions.toJS(), () => {
reaction(() => this.toJSON(), () => {
this.broadcastExtensions();
});
handleRequest(this.requestExtensionsChannel, () => {
handleRequest(ExtensionLoader.extensionsMainChannel, () => {
return Array.from(this.toJSON());
});
subscribeToBroadcast(ExtensionLoader.extensionsRendererChannel, (_event, extensions: [LensExtensionId, InstalledExtension][]) => {
this.syncExtensions(extensions);
});
}
protected async initRenderer() {
const extensionListHandler = (extensions: [LensExtensionId, InstalledExtension][]) => {
this.isLoaded = true;
this.syncExtensions(extensions);
const receivedExtensionIds = extensions.map(([lensExtensionId]) => lensExtensionId);
// Add new extensions
extensions.forEach(([extId, ext]) => {
if (!this.extensions.has(extId)) {
this.extensions.set(extId, ext);
}
});
// Remove deleted extensions
// Remove deleted extensions in renderer side only
this.extensions.forEach((_, lensExtensionId) => {
if (!receivedExtensionIds.includes(lensExtensionId)) {
this.removeExtension(lensExtensionId);
@ -124,14 +129,26 @@ export class ExtensionLoader {
});
};
requestMain(this.requestExtensionsChannel).then(extensionListHandler);
subscribeToBroadcast(this.requestExtensionsChannel, (event, extensions: [LensExtensionId, InstalledExtension][]) => {
reaction(() => this.toJSON(), () => {
this.broadcastExtensions(false);
});
requestMain(ExtensionLoader.extensionsMainChannel).then(extensionListHandler);
subscribeToBroadcast(ExtensionLoader.extensionsMainChannel, (_event, extensions: [LensExtensionId, InstalledExtension][]) => {
extensionListHandler(extensions);
});
}
syncExtensions(extensions: [LensExtensionId, InstalledExtension][]) {
extensions.forEach(([lensExtensionId, extension]) => {
if (!isEqual(this.extensions.get(lensExtensionId), extension)) {
this.extensions.set(lensExtensionId, extension);
}
});
}
loadOnMain() {
logger.info(`${logModule}: load on main`);
logger.debug(`${logModule}: load on main`);
this.autoInitExtensions(async (extension: LensMainExtension) => {
// Each .add returns a function to remove the item
const removeItems = [
@ -151,7 +168,7 @@ export class ExtensionLoader {
}
loadOnClusterManagerRenderer() {
logger.info(`${logModule}: load on main renderer (cluster manager)`);
logger.debug(`${logModule}: load on main renderer (cluster manager)`);
this.autoInitExtensions(async (extension: LensRendererExtension) => {
const removeItems = [
registries.globalPageRegistry.add(extension.globalPages, extension),
@ -174,7 +191,7 @@ export class ExtensionLoader {
}
loadOnClusterRenderer() {
logger.info(`${logModule}: load on cluster renderer (dashboard)`);
logger.debug(`${logModule}: load on cluster renderer (dashboard)`);
const cluster = getHostedCluster();
this.autoInitExtensions(async (extension: LensRendererExtension) => {
@ -204,26 +221,26 @@ export class ExtensionLoader {
protected autoInitExtensions(register: (ext: LensExtension) => Promise<Function[]>) {
return reaction(() => this.toJSON(), installedExtensions => {
for (const [extId, ext] of installedExtensions) {
for (const [extId, extension] of installedExtensions) {
const alreadyInit = this.instances.has(extId);
if (ext.isEnabled && !alreadyInit) {
if (extension.isEnabled && !alreadyInit) {
try {
const LensExtensionClass = this.requireExtension(ext);
const LensExtensionClass = this.requireExtension(extension);
if (!LensExtensionClass) {
continue;
}
const instance = new LensExtensionClass(ext);
const instance = new LensExtensionClass(extension);
instance.whenEnabled(() => register(instance));
instance.enable();
this.instances.set(extId, instance);
} catch (err) {
logger.error(`${logModule}: activation extension error`, { ext, err });
logger.error(`${logModule}: activation extension error`, { ext: extension, err });
}
} else if (!ext.isEnabled && alreadyInit) {
} else if (!extension.isEnabled && alreadyInit) {
this.removeInstance(extId);
}
}
@ -262,8 +279,8 @@ export class ExtensionLoader {
});
}
broadcastExtensions() {
broadcastMessage(this.requestExtensionsChannel, Array.from(this.toJSON()));
broadcastExtensions(main = true) {
broadcastMessage(main ? ExtensionLoader.extensionsMainChannel : ExtensionLoader.extensionsRendererChannel, Array.from(this.toJSON()));
}
}

View File

@ -45,17 +45,6 @@ export class ExtensionsStore extends BaseStore<LensExtensionsStoreModel> {
await extensionLoader.whenLoaded;
await this.whenLoaded;
// apply state on changes from store
reaction(() => this.state.toJS(), extensionsState => {
extensionsState.forEach((state, extId) => {
const ext = extensionLoader.getExtension(extId);
if (ext && !ext.isBundled) {
ext.isEnabled = state.enabled;
}
});
});
// save state on change `extension.isEnabled`
reaction(() => this.getState(extensionLoader), extensionsState => {
this.state.merge(extensionsState);
@ -65,7 +54,9 @@ export class ExtensionsStore extends BaseStore<LensExtensionsStoreModel> {
isEnabled(extId: LensExtensionId) {
const state = this.state.get(extId);
return state && state.enabled; // by default false
// By default false, so that copied extensions are disabled by default.
// If user installs the extension from the UI, the Extensions component will specifically enable it.
return Boolean(state?.enabled);
}
@action

View File

@ -44,7 +44,7 @@ export function getExtensionPageUrl<P extends object>({ extensionId, pageId = ""
const extensionBaseUrl = compile(`/extension/:name`)({
name: sanitizeExtensionName(extensionId), // compile only with extension-id first and define base path
});
const extPageRoutePath = path.join(extensionBaseUrl, pageId); // page-id might contain route :param-s, so don't compile yet
const extPageRoutePath = path.posix.join(extensionBaseUrl, pageId);
if (params) {
return compile(extPageRoutePath)(params); // might throw error when required params not passed

View File

@ -62,6 +62,9 @@ export class Extensions extends React.Component {
@observable search = "";
@observable installPath = "";
// True if the preliminary install steps have started, but unpackExtension has not started yet
@observable startingInstall = false;
/**
* Extensions that were removed from extensions but are still in "uninstalling" state
*/
@ -91,11 +94,20 @@ export class Extensions extends React.Component {
});
this.addedInstalling.forEach(({ id, displayName }) => {
const extension = this.extensions.find(extension => extension.id === id);
if (!extension) {
throw new Error("Extension not found");
}
Notifications.ok(
<p>Extension <b>{displayName}</b> successfully installed!</p>
);
this.extensionState.delete(id);
this.installPath = "";
// Enable installed extensions by default.
extension.isEnabled = true;
});
})
);
@ -152,6 +164,8 @@ export class Extensions extends React.Component {
const { installPath } = this;
if (!installPath) return;
this.startingInstall = true;
const fileName = path.basename(installPath);
try {
@ -161,13 +175,14 @@ export class Extensions extends React.Component {
const { promise: filePromise } = downloadFile({ url: installPath, timeout: 60000 /*1m*/ });
const data = await filePromise;
this.requestInstall({ fileName, data });
await this.requestInstall({ fileName, data });
}
// otherwise installing from system path
else if (InputValidators.isPath.validate(installPath)) {
this.requestInstall({ fileName, filePath: installPath });
await this.requestInstall({ fileName, filePath: installPath });
}
} catch (error) {
this.startingInstall = false;
Notifications.error(
<p>Installation has failed: <b>{String(error)}</b></p>
);
@ -186,11 +201,11 @@ export class Extensions extends React.Component {
};
async preloadExtensions(requests: InstallRequest[], { showError = true } = {}) {
const preloadedRequests = requests.filter(req => req.data);
const preloadedRequests = requests.filter(request => request.data);
await Promise.all(
requests
.filter(req => !req.data && req.filePath)
.filter(request => !request.data && request.filePath)
.map(async request => {
try {
const data = await fse.readFile(request.filePath);
@ -247,11 +262,11 @@ export class Extensions extends React.Component {
// copy files to temp
await fse.ensureDir(this.getExtensionPackageTemp());
requests.forEach(req => {
const tempFile = this.getExtensionPackageTemp(req.fileName);
for (const request of requests) {
const tempFile = this.getExtensionPackageTemp(request.fileName);
fse.writeFileSync(tempFile, req.data);
});
await fse.writeFile(tempFile, request.data);
}
// validate packages
await Promise.all(
@ -289,15 +304,24 @@ export class Extensions extends React.Component {
const preloadedRequests = await this.preloadExtensions(requests);
const validatedRequests = await this.createTempFilesAndValidate(preloadedRequests);
validatedRequests.forEach(install => {
// If there are no requests for installing, reset startingInstall state
if (validatedRequests.length === 0) {
this.startingInstall = false;
}
for (const install of validatedRequests) {
const { name, version, description } = install.manifest;
const extensionFolder = this.getExtensionDestFolder(name);
const folderExists = fse.existsSync(extensionFolder);
const folderExists = await fse.pathExists(extensionFolder);
if (!folderExists) {
// auto-install extension if not yet exists
this.unpackExtension(install);
} else {
// If we show the confirmation dialog, we stop the install spinner until user clicks ok
// and the install continues
this.startingInstall = false;
// otherwise confirmation required (re-install / update)
const removeNotification = Notifications.info(
<div className="InstallingExtensionNotification flex gaps align-center">
@ -315,21 +339,23 @@ export class Extensions extends React.Component {
</div>
);
}
});
}
}
async unpackExtension({ fileName, tempFile, manifest: { name, version } }: InstallRequestValidated) {
const displayName = extensionDisplayName(name, version);
const extensionFolder = this.getExtensionDestFolder(name);
const unpackingTempFolder = path.join(path.dirname(tempFile), `${path.basename(tempFile)}-unpacked`);
const extensionId = path.join(extensionDiscovery.nodeModulesPath, name, "package.json");
logger.info(`Unpacking extension ${displayName}`, { fileName, tempFile });
this.extensionState.set(extensionId, {
state: "installing",
displayName
});
this.startingInstall = false;
const extensionFolder = this.getExtensionDestFolder(name);
const unpackingTempFolder = path.join(path.dirname(tempFile), `${path.basename(tempFile)}-unpacked`);
logger.info(`Unpacking extension ${displayName}`, { fileName, tempFile });
try {
// extract to temp folder first
@ -455,7 +481,7 @@ export class Extensions extends React.Component {
* True if at least one extension is in installing state
*/
@computed get isInstalling() {
return [...this.extensionState.values()].some(extension => extension.state === "installing");
return this.startingInstall || [...this.extensionState.values()].some(extension => extension.state === "installing");
}
render() {

View File

@ -115,6 +115,7 @@ export class PodDetailsList extends React.Component<Props> {
<TableCell className="name">{pod.getName()}</TableCell>
<TableCell className="warning"><KubeObjectStatusIcon key="icon" object={pod}/></TableCell>
<TableCell className="namespace">{pod.getNs()}</TableCell>
<TableCell className="ready">{pod.getRunningContainers().length}/{pod.getContainers().length}</TableCell>
<TableCell className="cpu">{this.renderCpuUsage(`cpu-${pod.getId()}`, metrics.cpu)}</TableCell>
<TableCell className="memory">{this.renderMemoryUsage(`memory-${pod.getId()}`, metrics.memory)}</TableCell>
<TableCell className={cssNames("status", kebabCase(pod.getStatusMessage()))}>{pod.getStatusMessage()}</TableCell>
@ -148,7 +149,8 @@ export class PodDetailsList extends React.Component<Props> {
<TableHead>
<TableCell className="name" sortBy={sortBy.name}><Trans>Name</Trans></TableCell>
<TableCell className="warning"/>
<TableCell className="namespace" sortBy={sortBy.namespace}>Namespace</TableCell>
<TableCell className="namespace" sortBy={sortBy.namespace}><Trans>Namespace</Trans></TableCell>
<TableCell className="ready"><Trans>Ready</Trans></TableCell>
<TableCell className="cpu" sortBy={sortBy.cpu}><Trans>CPU</Trans></TableCell>
<TableCell className="memory" sortBy={sortBy.memory}><Trans>Memory</Trans></TableCell>
<TableCell className="status"><Trans>Status</Trans></TableCell>

View File

@ -0,0 +1,52 @@
import React from "react";
import { render } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import { BottomBar } from "./bottom-bar";
jest.mock("../../../extensions/registries");
import { statusBarRegistry } from "../../../extensions/registries";
describe("<BottomBar />", () => {
it("renders w/o errors", () => {
const { container } = render(<BottomBar />);
expect(container).toBeInstanceOf(HTMLElement);
});
// some defensive testing
it("renders w/o errors when .getItems() returns edge cases", async () => {
statusBarRegistry.getItems = jest.fn().mockImplementationOnce(() => undefined);
expect(() => render(<BottomBar />)).not.toThrow();
statusBarRegistry.getItems = jest.fn().mockImplementationOnce(() => null);
expect(() => render(<BottomBar />)).not.toThrow();
statusBarRegistry.getItems = jest.fn().mockImplementationOnce(() => []);
expect(() => render(<BottomBar />)).not.toThrow();
statusBarRegistry.getItems = jest.fn().mockImplementationOnce(() => { return {};});
expect(() => render(<BottomBar />)).not.toThrow();
});
it("renders items [{item: React.ReactNode}] (4.0.0-rc.1)", async () => {
const testId = "testId";
const text = "heee";
statusBarRegistry.getItems = jest.fn().mockImplementationOnce(() => [
{ item: <span data-testid={testId} >{text}</span> }
]);
const { getByTestId } = render(<BottomBar />);
expect(await getByTestId(testId)).toHaveTextContent(text);
});
it("renders items [{item: () => React.ReactNode}] (4.0.0-rc.1+)", async () => {
const testId = "testId";
const text = "heee";
statusBarRegistry.getItems = jest.fn().mockImplementationOnce(() => [
{ item: () => <span data-testid={testId} >{text}</span> }
]);
const { getByTestId } = render(<BottomBar />);
expect(await getByTestId(testId)).toHaveTextContent(text);
});
});

View File

@ -11,6 +11,8 @@ import { statusBarRegistry } from "../../../extensions/registries";
export class BottomBar extends React.Component {
render() {
const { currentWorkspace } = workspaceStore;
// in case .getItems() returns undefined
const items = statusBarRegistry.getItems() ?? [];
return (
<div className="BottomBar flex gaps">
@ -22,10 +24,17 @@ export class BottomBar extends React.Component {
htmlFor="current-workspace"
/>
<div className="extensions box grow flex gaps justify-flex-end">
{statusBarRegistry.getItems().map(({ item }, index) => {
{Array.isArray(items) && items.map(({ item }, index) => {
if (!item) return;
return <div className="flex align-center gaps item" key={index}>{item}</div>;
return (
<div
className="flex align-center gaps item"
key={index}
>
{typeof item === "function" ? item() : item}
</div>
);
})}
</div>
</div>

2
types/dom.d.ts vendored
View File

@ -1,4 +1,4 @@
export {}
export {};
declare global {
interface Element {

10
types/font-face.d.ts vendored
View File

@ -1,6 +1,6 @@
// https://www.w3.org/TR/css-font-loading/
// https://developer.mozilla.org/en-US/docs/Web/API/FontFace
export {}
export {};
declare global {
const FontFace: FontFace;
@ -10,11 +10,11 @@ declare global {
}
type CSSOMString = string;
type FontFaceLoadStatus = 'unloaded' | 'loading' | 'loaded' | 'error';
type FontFaceSetStatus = 'loading' | 'loaded';
type FontFaceLoadStatus = "unloaded" | "loading" | "loaded" | "error";
type FontFaceSetStatus = "loading" | "loaded";
interface FontFace extends FontFaceDescriptors {
new(family: string, source: string | ArrayBuffer, descriptors?: FontFaceDescriptors): FontFace;
class FontFace implements FontFaceDescriptors {
constructor(family: string, source: string | ArrayBuffer, descriptors?: FontFaceDescriptors);
readonly status: FontFaceLoadStatus;
readonly loaded: Promise<FontFace>;
variationSettings: CSSOMString;

View File

@ -1,87 +1,90 @@
import path from 'path';
import path from "path";
import webpack from "webpack";
import { sassCommonVars } from "./src/common/vars";
export default function (): webpack.Configuration {
const entry = "./src/extensions/extension-api.ts"
const outDir = "./src/extensions/npm/extensions/dist";
return {
// Compile for Electron for renderer process
// see <https://webpack.js.org/configuration/target/>
target: "electron-renderer",
entry,
output: {
filename: 'extension-api.js',
// need to be an absolute path
path: path.resolve(__dirname, `${outDir}/src/extensions`),
// can be use in commonjs environments
// e.g. require('@k8slens/extensions')
libraryTarget: "commonjs"
const entry = "./src/extensions/extension-api.ts";
const outDir = "./src/extensions/npm/extensions/dist";
return {
// Compile for Electron for renderer process
// see <https://webpack.js.org/configuration/target/>
target: "electron-renderer",
entry,
// this is the default mode, so we should make it explicit to silence the warning
mode: "production",
output: {
filename: "extension-api.js",
// need to be an absolute path
path: path.resolve(__dirname, `${outDir}/src/extensions`),
// can be use in commonjs environments
// e.g. require('@k8slens/extensions')
libraryTarget: "commonjs"
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
options: {
// !! ts-loader will use tsconfig.json at folder root
// !! changes in tsconfig.json may have side effects
// !! on '@k8slens/extensions' module
compilerOptions: {
declaration: true, // output .d.ts
sourceMap: false, // to override sourceMap: true in tsconfig.json
outDir // where the .d.ts should be located
}
}
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
// !! ts-loader will use tsconfig.json at folder root
// !! changes in tsconfig.json may have side effects
// !! on '@k8slens/extensions' module
compilerOptions: {
declaration: true, // output .d.ts
sourceMap: false, // to override sourceMap: true in tsconfig.json
outDir // where the .d.ts should be located
}
}
// for src/renderer/components/fonts/roboto-mono-nerd.ttf
// in src/renderer/components/dock/terminal.ts 95:25-65
{
test: /\.(ttf|eot|woff2?)$/,
use: {
loader: "url-loader",
options: {
name: "fonts/[name].[ext]"
}
}
},
// for import scss files
{
test: /\.s?css$/,
use: [
// creates `style` nodes from JS strings
"style-loader",
// translates CSS into CommonJS
"css-loader",
{
loader: "sass-loader",
options: {
prependData: `@import "${path.basename(sassCommonVars)}";`,
sassOptions: {
includePaths: [
path.dirname(sassCommonVars)
]
},
// for src/renderer/components/fonts/roboto-mono-nerd.ttf
// in src/renderer/components/dock/terminal.ts 95:25-65
{
test: /\.(ttf|eot|woff2?)$/,
use: {
loader: "url-loader",
options: {
name: "fonts/[name].[ext]"
}
}
},
// for import scss files
{
test: /\.s?css$/,
use: [
// creates `style` nodes from JS strings
"style-loader",
// translates CSS into CommonJS
"css-loader",
{
loader: "sass-loader",
options: {
prependData: `@import "${path.basename(sassCommonVars)}";`,
sassOptions: {
includePaths: [
path.dirname(sassCommonVars)
]
},
}
},
]
}
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js']
},
plugins: [
// In ts-loader's README they said to output a built .d.ts file,
// you can set "declaration": true in tsconfig.extensions.json,
// and use the DeclarationBundlerPlugin in your webpack config... but
// !! the DeclarationBundlerPlugin doesn't work anymore, author archived it.
// https://www.npmjs.com/package/declaration-bundler-webpack-plugin
// new DeclarationBundlerPlugin({
// moduleName: '@k8slens/extensions',
// out: 'extension-api.d.ts',
// })
]
};
}
},
]
}
]
},
resolve: {
extensions: [".ts", ".tsx", ".js"]
},
plugins: [
// In ts-loader's README they said to output a built .d.ts file,
// you can set "declaration": true in tsconfig.extensions.json,
// and use the DeclarationBundlerPlugin in your webpack config... but
// !! the DeclarationBundlerPlugin doesn't work anymore, author archived it.
// https://www.npmjs.com/package/declaration-bundler-webpack-plugin
// new DeclarationBundlerPlugin({
// moduleName: '@k8slens/extensions',
// out: 'extension-api.d.ts',
// })
]
};
}

View File

@ -1,12 +1,14 @@
import path from "path";
import webpack from "webpack";
import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin"
import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin";
import { isDevelopment, isProduction, mainDir, buildDir } from "./src/common/vars";
import nodeExternals from "webpack-node-externals";
import ProgressBarPlugin from "progress-bar-webpack-plugin";
import * as vars from "./src/common/vars";
export default function (): webpack.Configuration {
console.info('WEBPACK:main', require("./src/common/vars"))
console.info("WEBPACK:main", vars);
return {
context: __dirname,
target: "electron-main",
@ -21,7 +23,7 @@ export default function (): webpack.Configuration {
path: buildDir,
},
resolve: {
extensions: ['.json', '.js', '.ts']
extensions: [".json", ".js", ".ts"]
},
externals: [
nodeExternals()
@ -48,5 +50,5 @@ export default function (): webpack.Configuration {
new ProgressBarPlugin(),
new ForkTsCheckerPlugin(),
].filter(Boolean)
}
};
}

View File

@ -4,18 +4,20 @@ import webpack from "webpack";
import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import TerserPlugin from "terser-webpack-plugin";
import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin"
import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin";
import ProgressBarPlugin from "progress-bar-webpack-plugin";
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'
import ReactRefreshWebpackPlugin from "@pmmmwh/react-refresh-webpack-plugin";
import * as vars from "./src/common/vars";
export default [
webpackLensRenderer
]
];
export function webpackLensRenderer({ showVars = true } = {}): webpack.Configuration {
if (showVars) {
console.info('WEBPACK:renderer', require("./src/common/vars"));
console.info("WEBPACK:renderer", vars);
}
return {
context: __dirname,
target: "electron-renderer",
@ -27,7 +29,7 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
hot: true,
// to avoid cors errors when requests is from iframes
disableHostCheck: true,
headers: { 'Access-Control-Allow-Origin': '*' },
headers: { "Access-Control-Allow-Origin": "*" },
},
name: "lens-app",
mode: isProduction ? "production" : "development",
@ -39,10 +41,10 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
libraryTarget: "global",
library: "",
globalObject: "this",
publicPath: publicPath,
publicPath,
path: buildDir,
filename: '[name].js',
chunkFilename: 'chunks/[name].js',
filename: "[name].js",
chunkFilename: "chunks/[name].js",
},
stats: {
warningsFilter: [
@ -51,8 +53,8 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
},
resolve: {
extensions: [
'.js', '.jsx', '.json',
'.ts', '.tsx',
".js", ".jsx", ".json",
".ts", ".tsx",
]
},
optimization: {
@ -91,7 +93,7 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
}],
],
plugins: [
isDevelopment && require.resolve('react-refresh/babel'),
isDevelopment && require.resolve("react-refresh/babel"),
].filter(Boolean),
}
},
@ -190,5 +192,5 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
isDevelopment && new ReactRefreshWebpackPlugin(),
].filter(Boolean),
}
};
}