diff --git a/package-lock.json b/package-lock.json index 8b0d8a33a2..331640c585 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36595,6 +36595,9 @@ "webpack": "^5.81.0", "webpack-cli": "^4.10.0" }, + "bin": { + "lens-webpack-build": "bin/webpack-build" + }, "devDependencies": { "@async-fn/jest": "^1.6.4", "@k8slens/test-utils": "^1.0.0-alpha.1", diff --git a/packages/infrastructure/webpack/bin/webpack-build b/packages/infrastructure/webpack/bin/webpack-build new file mode 100755 index 0000000000..30c4f8a78d --- /dev/null +++ b/packages/infrastructure/webpack/bin/webpack-build @@ -0,0 +1,4 @@ +#!/usr/bin/env node +const { doWebpackBuild } = require("../dist/index"); + +doWebpackBuild(); diff --git a/packages/infrastructure/webpack/do-webpack-build.ts b/packages/infrastructure/webpack/do-webpack-build.ts new file mode 100644 index 0000000000..1e2e91e3b4 --- /dev/null +++ b/packages/infrastructure/webpack/do-webpack-build.ts @@ -0,0 +1,10 @@ +import { getDi } from "./src/scripts/get-di"; +import { doWebpackBuildInjectable } from "./src/scripts/do-webpack-build"; + +export const doWebpackBuild = () => { + const di = getDi(); + + const doWebpackBuild = di.inject(doWebpackBuildInjectable); + + doWebpackBuild(); +}; diff --git a/packages/infrastructure/webpack/index.ts b/packages/infrastructure/webpack/index.ts index c7dffc231e..72b6800d02 100644 --- a/packages/infrastructure/webpack/index.ts +++ b/packages/infrastructure/webpack/index.ts @@ -1,3 +1,4 @@ export { configForNode } from "./src/node-config"; export { configForReact } from "./src/react-config"; export { getMultiExportConfig } from "./src/get-multi-export-config"; +export { doWebpackBuild } from "./do-webpack-build"; diff --git a/packages/infrastructure/webpack/package.json b/packages/infrastructure/webpack/package.json index 016ffd3b56..3844dded35 100644 --- a/packages/infrastructure/webpack/package.json +++ b/packages/infrastructure/webpack/package.json @@ -48,5 +48,8 @@ "@k8slens/typescript": "^6.5.0-alpha.2", "ts-node": "^10.9.1", "webpack-node-externals": "^3.0.0" + }, + "bin": { + "lens-webpack-build": "bin/webpack-build" } } diff --git a/packages/infrastructure/webpack/src/scripts/do-webpack-build.test.ts b/packages/infrastructure/webpack/src/scripts/do-webpack-build.test.ts new file mode 100644 index 0000000000..738a25283d --- /dev/null +++ b/packages/infrastructure/webpack/src/scripts/do-webpack-build.test.ts @@ -0,0 +1,89 @@ +import { getDi } from "./get-di"; +import { Exec, execInjectable } from "./exec.injectable"; +import asyncFn, { AsyncFnMock } from "@async-fn/jest"; +import { DoWebpackBuild, doWebpackBuildInjectable } from "./do-webpack-build"; +import { getPromiseStatus } from "@k8slens/test-utils"; +import { LogSuccess, logSuccessInjectable } from "./log-success.injectable"; + +describe("do-webpack-build", () => { + let execMock: AsyncFnMock; + let doWebpackBuild: DoWebpackBuild; + let logSuccessMock: AsyncFnMock; + + beforeEach(() => { + const di = getDi(); + + execMock = asyncFn(); + di.override(execInjectable, () => execMock); + logSuccessMock = asyncFn(); + di.override(logSuccessInjectable, () => logSuccessMock); + + doWebpackBuild = di.inject(doWebpackBuildInjectable); + }); + + describe("when called", () => { + let actualPromise: Promise; + + beforeEach(() => { + actualPromise = doWebpackBuild(); + }); + + it("calls webpack", () => { + expect(execMock).toHaveBeenCalledWith("webpack"); + }); + + it("does not resolve yet", async () => { + const promiseStatus = await getPromiseStatus(actualPromise); + + expect(promiseStatus.fulfilled).toBe(false); + }); + + describe("when webpack resolves with stdout", () => { + beforeEach(async () => { + await execMock.resolve({ stdout: "some-stdout", stderr: "" }); + }); + + it("logs the stdout", () => { + expect(logSuccessMock).toHaveBeenCalledWith("some-stdout"); + }); + + it("script finishes", async () => { + const promiseStatus = await getPromiseStatus(actualPromise); + + expect(promiseStatus.fulfilled).toBe(true); + }); + }); + + describe("when webpack resolves with stderr", () => { + beforeEach(() => { + execMock.resolve({ stdout: "", stderr: "some-stderr" }); + }); + + it("does not log success", () => { + actualPromise.catch(() => {}); + + expect(logSuccessMock).not.toHaveBeenCalled(); + }); + + it("throws", () => { + return expect(actualPromise).rejects.toThrow("some-stderr"); + }); + }); + + describe("when webpack rejects", () => { + beforeEach(() => { + execMock.reject(new Error("some-error")); + }); + + it("does not log success", () => { + actualPromise.catch(() => {}); + + expect(logSuccessMock).not.toHaveBeenCalled(); + }); + + it("throws", () => { + return expect(actualPromise).rejects.toThrow("some-error"); + }); + }); + }); +}); diff --git a/packages/infrastructure/webpack/src/scripts/do-webpack-build.ts b/packages/infrastructure/webpack/src/scripts/do-webpack-build.ts new file mode 100644 index 0000000000..8a9b566480 --- /dev/null +++ b/packages/infrastructure/webpack/src/scripts/do-webpack-build.ts @@ -0,0 +1,24 @@ +import { getInjectable } from "@ogre-tools/injectable"; +import { execInjectable } from "./exec.injectable"; +import { logSuccessInjectable } from "./log-success.injectable"; + +export type DoWebpackBuild = () => Promise; + +export const doWebpackBuildInjectable = getInjectable({ + id: "do-webpack-build", + + instantiate: (di) => { + const exec = di.inject(execInjectable); + const logSuccess = di.inject(logSuccessInjectable); + + return async () => { + const { stdout, stderr } = await exec("webpack"); + + if (stderr) { + throw new Error(stderr); + } + + logSuccess(stdout); + }; + }, +}); diff --git a/packages/infrastructure/webpack/src/scripts/exec.injectable.ts b/packages/infrastructure/webpack/src/scripts/exec.injectable.ts new file mode 100644 index 0000000000..893520fcc0 --- /dev/null +++ b/packages/infrastructure/webpack/src/scripts/exec.injectable.ts @@ -0,0 +1,12 @@ +import { getInjectable } from "@ogre-tools/injectable"; +import { exec } from "child_process"; +import { promisify } from "util"; + +const promisifiedExec = promisify(exec); + +export type Exec = typeof promisifiedExec; + +export const execInjectable = getInjectable({ + id: "exec", + instantiate: () => promisifiedExec, +}); diff --git a/packages/infrastructure/webpack/src/scripts/get-di.ts b/packages/infrastructure/webpack/src/scripts/get-di.ts new file mode 100644 index 0000000000..28774e026f --- /dev/null +++ b/packages/infrastructure/webpack/src/scripts/get-di.ts @@ -0,0 +1,12 @@ +import { createContainer } from "@ogre-tools/injectable"; +import { execInjectable } from "./exec.injectable"; +import { doWebpackBuildInjectable } from "./do-webpack-build"; +import { logSuccessInjectable } from "./log-success.injectable"; + +export const getDi = () => { + const di = createContainer("do-webpack-build"); + + di.register(execInjectable, doWebpackBuildInjectable, logSuccessInjectable); + + return di; +}; diff --git a/packages/infrastructure/webpack/src/scripts/log-success.injectable.ts b/packages/infrastructure/webpack/src/scripts/log-success.injectable.ts new file mode 100644 index 0000000000..5a0d260ceb --- /dev/null +++ b/packages/infrastructure/webpack/src/scripts/log-success.injectable.ts @@ -0,0 +1,8 @@ +import { getInjectable } from "@ogre-tools/injectable"; + +export type LogSuccess = typeof console.log; + +export const logSuccessInjectable = getInjectable({ + id: "log-success", + instantiate: (di): LogSuccess => console.log, +});