diff --git a/README.md b/README.md index 743e953..67e7c73 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ $ npm install -g chs-dev $ chs-dev COMMAND running command... $ chs-dev (--version) -chs-dev/1.3.0 darwin-arm64 node-v20.10.0 +chs-dev/1.3.0 darwin-arm64 node-v20.12.2 $ chs-dev --help [COMMAND] USAGE $ chs-dev COMMAND @@ -506,11 +506,14 @@ Rebuilds and restarts the supplied service running in development mode to load i ``` USAGE - $ chs-dev reload SERVICE + $ chs-dev reload SERVICE [-f] ARGUMENTS SERVICE Name of the service +FLAGS + -f, --force Forcefully reload service and force a rebuild + DESCRIPTION Rebuilds and restarts the supplied service running in development mode to load in any changes to source code ``` diff --git a/src/commands/reload.ts b/src/commands/reload.ts index 8723805..98caa3b 100644 --- a/src/commands/reload.ts +++ b/src/commands/reload.ts @@ -1,10 +1,11 @@ -import { Args, Command, Config } from "@oclif/core"; +import { Args, Command, Config, Flags } from "@oclif/core"; import { Inventory } from "../state/inventory.js"; import { join } from "path"; import fsExtra from "fs-extra"; import { DependencyCache } from "../run/dependency-cache.js"; import ChsDevConfig from "../model/Config.js"; import loadConfig from "../helpers/config-loader.js"; +import { existsSync, unlinkSync } from "fs"; export default class Reload extends Command { @@ -18,6 +19,18 @@ export default class Reload extends Command { }) }; + static flags = { + force: Flags.boolean({ + required: false, + allowNo: false, + default: false, + description: "Forcefully reload service and force a rebuild", + name: "force", + char: "f", + aliases: ["force"] + }) + }; + private readonly inventory: Inventory; private readonly dependencyCache: DependencyCache; private readonly chsDevConfig: ChsDevConfig; @@ -32,9 +45,19 @@ export default class Reload extends Command { } async run (): Promise { - const { args } = await this.parse(Reload); + const { args, flags } = await this.parse(Reload); const serviceName: string = args.service; + const codeHashFile = join( + this.chsDevConfig.projectPath, + "local", + serviceName, + "out/.code.hash" + ); + + if (flags.force && existsSync(codeHashFile)) { + unlinkSync(codeHashFile); + } if (this.isServiceValid(serviceName)) { const touchFile = join(this.chsDevConfig.projectPath, "local", serviceName, ".touch"); diff --git a/test/commands/reload.spec.ts b/test/commands/reload.spec.ts index 54a34bc..132ac46 100644 --- a/test/commands/reload.spec.ts +++ b/test/commands/reload.spec.ts @@ -3,6 +3,7 @@ import { Service } from "../../src/model/Service"; import { join } from "path"; import { Config } from "@oclif/core"; import Reload from "../../src/commands/reload"; +import fs from "fs"; const ensureFileMock = jest.fn(); const utimesMock = jest.fn(); @@ -69,6 +70,8 @@ describe("reload spec", () => { const parseMock = jest.fn(); const logMock = jest.fn(); const errorMock = jest.fn(); + const unlinkSyncSpy = jest.spyOn(fs, "unlinkSync"); + const existsSyncSpy = jest.spyOn(fs, "existsSync"); const testDateTime = new Date(2023, 1, 1, 0, 0, 0); @@ -93,6 +96,9 @@ describe("reload spec", () => { reload.log = logMock; // @ts-expect-error reload.error = errorMock; + + unlinkSyncSpy.mockImplementation((_) => {}); + existsSyncSpy.mockReturnValue(true); }); it("should not reload an invalid service", async () => { @@ -100,6 +106,9 @@ describe("reload spec", () => { parseMock.mockResolvedValue({ args: { service: "not-found" + }, + flags: { + force: false } }); @@ -115,6 +124,9 @@ describe("reload spec", () => { parseMock.mockResolvedValue({ args: { service: "service-one" + }, + flags: { + force: false } }); @@ -134,6 +146,9 @@ describe("reload spec", () => { parseMock.mockResolvedValue({ args: { service: "service-one" + }, + flags: { + force: false } }); @@ -143,4 +158,73 @@ describe("reload spec", () => { expect(utimesMock).toHaveBeenCalledWith(join(projectDir, "local/service-one/.touch"), testDateTime, testDateTime); }); + it("does not remove code hash when force flag not supplied", async () => { + // @ts-expect-error + ensureFileMock.mockResolvedValue(undefined); + + // @ts-expect-error + utimesMock.mockResolvedValue(undefined); + + // @ts-expect-error + parseMock.mockResolvedValue({ + args: { + service: "service-one" + }, + flags: { + force: false + } + }); + + await reload.run(); + + expect(unlinkSyncSpy).not.toHaveBeenCalled(); + }); + + it("removes code hash when force flag supplied", async () => { + // @ts-expect-error + ensureFileMock.mockResolvedValue(undefined); + + // @ts-expect-error + utimesMock.mockResolvedValue(undefined); + + // @ts-expect-error + parseMock.mockResolvedValue({ + args: { + service: "service-one" + }, + flags: { + force: true + } + }); + + await reload.run(); + + expect(unlinkSyncSpy).toHaveBeenCalledWith( + join(projectDir, "local/service-one/out/.code.hash") + ); + }); + + it("does not remove code hash when force flag supplied and code hash does not exist", async () => { + existsSyncSpy.mockReturnValue(false); + + // @ts-expect-error + ensureFileMock.mockResolvedValue(undefined); + + // @ts-expect-error + utimesMock.mockResolvedValue(undefined); + + // @ts-expect-error + parseMock.mockResolvedValue({ + args: { + service: "service-one" + }, + flags: { + force: true + } + }); + + await reload.run(); + + expect(unlinkSyncSpy).not.toHaveBeenCalled(); + }); });