diff --git a/eslint.config.mjs b/eslint.config.mjs index 89710f8..b0025d7 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,6 +12,7 @@ import eslint from "@eslint/js"; * npm i globals@15.3.0 --save-dev */ import globals from "globals"; +import jest from "eslint-plugin-jest"; /** * Plugins @@ -118,10 +119,13 @@ export default [ } }, { - name: "ts", - rules: { - - } + name: "jest", + languageOptions: { + globals: { + ...jest.environments.globals.globals + } + }, + files: ["**/*.spec.ts"] }, // ESLint config file ONLY! { diff --git a/package-lock.json b/package-lock.json index 824a99e..fd949cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "@types/node": "^22.0.0", "@types/prompts": "^2.4.9", "eslint": ">=9.3.0", + "eslint-plugin-jest": "^28.8.0", "globals": ">=15.9.0", "jest": "^29.7.0", "jest-when": "^3.6.0", @@ -3206,6 +3207,31 @@ "url": "https://eslint.org/donate" } }, + "node_modules/eslint-plugin-jest": { + "version": "28.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.0.tgz", + "integrity": "sha512-Tubj1hooFxCl52G4qQu0edzV/+EZzPUeN8p2NnW5uu4fbDs+Yo7+qDVDc4/oG3FbCqEBmu/OC3LSsyiU22oghw==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "engines": { + "node": "^16.10.0 || ^18.12.0 || >=20.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", diff --git a/package.json b/package.json index 4c02adf..b157652 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "@types/node": "^22.0.0", "@types/prompts": "^2.4.9", "eslint": ">=9.3.0", + "eslint-plugin-jest": "^28.8.0", "globals": ">=15.9.0", "jest": "^29.7.0", "jest-when": "^3.6.0", diff --git a/spec/core/OAuthClient.spec.ts b/spec/core/OAuthClient.spec.ts index 2b563c1..005d915 100644 --- a/spec/core/OAuthClient.spec.ts +++ b/spec/core/OAuthClient.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable camelcase, no-magic-numbers */ import { OAuthClient, OAuthCodeExpired, OAuthRefreshTokenExpired, OAuthUsernamePasswordIncorrect } from "../../src/core/OAuthClient"; import { InstanceConfig, InstanceOAuthTokenData, Profile } from "../../src/core/ProfileManager"; import { Request, Response } from "../../src/core/Request"; @@ -18,7 +19,6 @@ describe("OAuthClientSpec", () => { refresh_token: "bbb", scope: "", token_type: "Bearer", - // seconds expires_in: 60 } } @@ -39,54 +39,51 @@ describe("OAuthClientSpec", () => { const token: InstanceOAuthTokenData = { clientID: "clientID", clientSecret: "clientSecret", - // current time -1 hour lastRetrieved: Date.now() - (60 * 60 * 1000), token: { access_token: "aaa", refresh_token: "bbb", scope: "", token_type: "Bearer", - // seconds expires_in: 60 } }; expect(OAuthClient.isTokenExpired(token)).toBe(true); }); - it("should be valid", () => { + it("should be valid", () => { const tokenValid: InstanceOAuthTokenData = { clientID: "clientID", clientSecret: "clientSecret", - // current time - 10 sec + // Current time - 10 sec lastRetrieved: Date.now() - 10000, token: { access_token: "aaa", refresh_token: "bbb", scope: "", token_type: "Bearer", - // seconds expires_in: 60 } }; - expect(OAuthClient.isTokenExpired(tokenValid)).toBe(false);; + expect(OAuthClient.isTokenExpired(tokenValid)).toBe(false); }); }); describe("request token by username", () => { - it("should resolve on 200", async () => { - let response = Response.empty(JSON.stringify(config.auth.token!)); + it("should resolve on 200", async() => { + const response = Response.empty(JSON.stringify(config.auth.token)); jest.spyOn(response, "isOK").mockReturnValue(true); jest.spyOn(Request, "execute").mockResolvedValue(response); const client = new OAuthClient(); - await expect(client.requestTokenByUsername(profile, "admin", "admin")).resolves.toStrictEqual(config.auth.token!); + await expect(client.requestTokenByUsername(profile, "admin", "admin")).resolves.toStrictEqual(config.auth.token); }); - it("should reject on 401", async () => { - let response = Response.empty(JSON.stringify({})); - jest.spyOn(response, "isEmpty").mockReturnValue(false); - jest.spyOn(response, "isUnauthorized").mockReturnValue(true); + it("should reject on 401", async() => { + const response = Response.empty(JSON.stringify({})); + jest.spyOn(response, "isEmpty").mockReturnValue(false); + jest.spyOn(response, "isUnauthorized").mockReturnValue(true); jest.spyOn(Request, "execute").mockRejectedValue(response); const client = new OAuthClient(); @@ -96,18 +93,18 @@ describe("OAuthClientSpec", () => { describe("request token by code", () => { const code = "1234"; - it("should resolve on 200", async () => { - let response = Response.empty(JSON.stringify(config.auth.token!)); - jest.spyOn(response, "isOK").mockReturnValue(true); + it("should resolve on 200", async() => { + const response = Response.empty(JSON.stringify(config.auth.token)); + jest.spyOn(response, "isOK").mockReturnValue(true); jest.spyOn(Request, "execute").mockResolvedValue(response); const client = new OAuthClient(); - await expect(client.requestTokenByCode(profile, code)).resolves.toStrictEqual(config.auth.token!); + await expect(client.requestTokenByCode(profile, code)).resolves.toStrictEqual(config.auth.token); }); - it("should reject on 401", async () => { - let response = Response.empty("{}"); - jest.spyOn(response, "isEmpty").mockReturnValue(false); + it("should reject on 401", async() => { + const response = Response.empty("{}"); + jest.spyOn(response, "isEmpty").mockReturnValue(false); jest.spyOn(response, "isUnauthorized").mockReturnValue(true); jest.spyOn(Request, "execute").mockRejectedValue(response); @@ -117,17 +114,17 @@ describe("OAuthClientSpec", () => { }); describe("refresh token", () => { - it("should resolve on 200", async () => { - let response = Response.empty(JSON.stringify(config.auth.token!)); + it("should resolve on 200", async() => { + const response = Response.empty(JSON.stringify(config.auth.token)); jest.spyOn(response, "isOK").mockReturnValue(true); jest.spyOn(Request, "execute").mockResolvedValue(response); const client = new OAuthClient(); - await expect(client.refreshToken(profile)).resolves.toStrictEqual(config.auth.token!); + await expect(client.refreshToken(profile)).resolves.toStrictEqual(config.auth.token); }); - it("should reject on 401", async () => { - let response = Response.empty("{}"); - jest.spyOn(response, "isEmpty").mockReturnValue(false); + it("should reject on 401", async() => { + const response = Response.empty("{}"); + jest.spyOn(response, "isEmpty").mockReturnValue(false); jest.spyOn(response, "isUnauthorized").mockReturnValue(true); jest.spyOn(Request, "execute").mockRejectedValue(response); const client = new OAuthClient(); @@ -136,7 +133,7 @@ describe("OAuthClientSpec", () => { }); it("should extend headers with authentication", async() => { - const token: SNOAuthTokenData = config.auth.token!; + const token: SNOAuthTokenData = config.auth.token as SNOAuthTokenData; const options: RequestOptions = { method: "GET" }; @@ -145,6 +142,6 @@ describe("OAuthClientSpec", () => { await client.handleAuthentication(profile, options); expect(options.headers).not.toBeUndefined(); - expect(options.headers!.authorization).toBe(`${token.token_type} ${token.access_token}`); + expect(options.headers?.authorization).toBe(`${token.token_type} ${token.access_token}`); }); }); \ No newline at end of file diff --git a/spec/core/ProfileManager.spec.ts b/spec/core/ProfileManager.spec.ts index 1273abd..a30c1f7 100644 --- a/spec/core/ProfileManager.spec.ts +++ b/spec/core/ProfileManager.spec.ts @@ -37,13 +37,13 @@ describe("ProfileManagerSpec", () => { dev: 0, gid: 0, ino: 0, - isBlockDevice: function(){return false}, - isCharacterDevice: function(){return false}, - isDirectory: function(){return true}, - isFIFO: function(){return false}, - isFile: function(){return false}, - isSocket: function(){return false}, - isSymbolicLink: function(){return false}, + isBlockDevice: () => false, + isCharacterDevice: () => false, + isDirectory: () => true, + isFIFO: () => false, + isFile: () => false, + isSocket: () => false, + isSymbolicLink: () => false, mode: 0, mtime: new Date(), mtimeMs: Date.now(), @@ -60,10 +60,9 @@ describe("ProfileManagerSpec", () => { when(jest.spyOn(fs, "readdirSync")) .defaultImplementation(jesthelpers.defaultWhenImplementationThrow) - // @ts-ignore + // @ts-expect-error wrong signature picked up .calledWith(profilesHomePath).mockReturnValue(["dev1", "dev2"]); - // @ts-ignore // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars jest.spyOn(fs, "statSync").mockImplementation((path) => { return dirStats; @@ -194,7 +193,6 @@ describe("ProfileManagerSpec", () => { expect(rmSpy).toHaveBeenCalledTimes(1); }); - }); // describe("Profile", () => { diff --git a/spec/core/RESTClient.spec.ts b/spec/core/RESTClient.spec.ts index 942615c..8ba5eef 100644 --- a/spec/core/RESTClient.spec.ts +++ b/spec/core/RESTClient.spec.ts @@ -1,11 +1,13 @@ +/* eslint-disable camelcase */ import { resetAllWhenMocks, when } from "jest-when"; import { jesthelpers } from "../helpers.js"; import { URLSearchParams } from "url"; import { OAuthClient } from "../../src/core/OAuthClient.js"; import { InstanceConfig, Profile, TableConfig } from "../../src/core/ProfileManager.js"; import { Request, Response } from "../../src/core/Request.js"; -import { RESTClient, JSONRESTResponse, TableAPI, TableFieldData, TableParentData } from "../../src/core/RESTClient.js"; -import { SNTable, SNUpdateSetData, SNUpdateXMLData } from "../../src/core/sn.js"; +import * as restclient from "../../src/core/RESTClient.js"; +import { RESTClient, JSONRESTResponse, TableFieldData, TableParentData } from "../../src/core/RESTClient.js"; +import { SNUpdateSetData, SNUpdateXMLData } from "../../src/core/sn.js"; describe("RESTClientSpec", () => { const config: InstanceConfig = { @@ -21,7 +23,6 @@ describe("RESTClientSpec", () => { refresh_token: "bbb", scope: "", token_type: "Bearer", - // seconds expires_in: 60 } } @@ -29,7 +30,8 @@ describe("RESTClientSpec", () => { const profile: Profile = new Profile(config); const oauthClient: OAuthClient = new OAuthClient(); - const _makeRESTResponse = function(data?: T | Array): JSONRESTResponse { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const _makeRESTResponse = function(data?: T | T[]): JSONRESTResponse { const response: JSONRESTResponse = { result: [] }; @@ -41,7 +43,7 @@ describe("RESTClientSpec", () => { response.result.push(data); } return response; - } + }; beforeEach(() => { resetAllWhenMocks(); @@ -63,12 +65,12 @@ describe("RESTClientSpec", () => { const url = { origin: config.baseUrl, - pathname: TableAPI.USER_PREFERENCE_PATH, + pathname: restclient.PATH_API_TABLE_USER_PREFERENCE, search: expect.stringContaining(new URLSearchParams("sysparm_query=name=@hrax/now-eslint/table_config^userISEMPTY^ORuserDYNAMIC90d1921e5f510100a9ad2572f2b477fe^ORDERBYDESCuser").toString()) }; it("should succeed if exists", async() => { - const requestExecuteSpy = jest.spyOn(Request, "execute"); + const requestExecuteSpy = jest.spyOn(Request, "execute"); const response = Response.empty(JSON.stringify(_makeRESTResponse(tablePref))); jest.spyOn(response, "isEmpty").mockReturnValue(false); @@ -94,12 +96,12 @@ describe("RESTClientSpec", () => { .calledWith(expect.objectContaining(url), expect.anything(), undefined).mockResolvedValue(response); const client = new RESTClient(oauthClient); - await expect(client.loadTableConfigurationPreference(profile)).rejects.toEqual(RESTClient.NO_TABLE_CONFIG_PREF); + await expect(client.loadTableConfigurationPreference(profile)).rejects.toEqual(restclient.NO_TABLE_CONFIG_PREF); }); }); describe("setting up table config", () => { - const tpData: Array = [ + const tpData: TableParentData[] = [ { name: "sys_script_include", "super_class.name": "" @@ -117,18 +119,20 @@ describe("RESTClientSpec", () => { "super_class.name": "task" } ]; - const tfData: Array = [{ - name: "sys_script_include", - element: "script" - }, - { - name: "sys_script", - element: "condition" - }, - { - name: "sys_script_client", - element: "script" - }]; + const tfData: TableFieldData[] = [ + { + name: "sys_script_include", + element: "script" + }, + { + name: "sys_script", + element: "condition" + }, + { + name: "sys_script_client", + element: "script" + } + ]; const data: TableConfig = { tables: { "sys_script_include": { @@ -160,19 +164,19 @@ describe("RESTClientSpec", () => { } } } - } + }; const tpUrl = { origin: config.baseUrl, - pathname: TableAPI.DB_OBJECT_PATH + pathname: restclient.PATH_API_TABLE_DB_OBJECT }; const tfUrl = { origin: config.baseUrl, - pathname: TableAPI.DICTIONARY_PATH + pathname: restclient.PATH_API_TABLE_DICTIONARY }; const upUrl = { origin: config.baseUrl, - pathname: TableAPI.USER_PREFERENCE_PATH + pathname: restclient.PATH_API_TABLE_USER_PREFERENCE }; it("should load, prepare and save the config", async() => { const tpResponse = Response.empty(JSON.stringify(_makeRESTResponse(tpData))); @@ -186,11 +190,11 @@ describe("RESTClientSpec", () => { const requestExecuteSpy = jest.spyOn(Request, "execute"); when(requestExecuteSpy) .defaultImplementation(jesthelpers.defaultWhenImplementationThrow) - // pull table-parent + // Pull table-parent .calledWith(expect.objectContaining(tpUrl), expect.anything(), undefined).mockResolvedValue(tpResponse) - // pull table-field + // Pull table-field .calledWith(expect.objectContaining(tfUrl), expect.anything(), undefined).mockResolvedValue(tfResponse) - // push preference + // Push preference .calledWith(expect.objectContaining(upUrl), expect.objectContaining({ "method": "POST" }), expect.anything()).mockResolvedValue(tfResponse); @@ -198,12 +202,13 @@ describe("RESTClientSpec", () => { await expect(client.setupTableConfiguration(profile)).resolves.toStrictEqual(data); // 2 pulls + 1 push + // eslint-disable-next-line no-magic-numbers expect(requestExecuteSpy).toHaveBeenCalledTimes(3); }); }); describe("loading update set changes", () => { - const responseBody: Array = [ + const responseBody: SNUpdateXMLData[] = [ { action: "INSERT_OR_UPDATE", application: "global", @@ -238,10 +243,10 @@ describe("RESTClientSpec", () => { } ]; - it("should load by update set ids", async() => { + it("should load by update set ids", async() => { const url = { origin: profile.getBaseUrl(), - pathname: TableAPI.UPDATE_XML_PATH, + pathname: restclient.PATH_API_TABLE_UPDATE_XML, search: expect.stringContaining(encodeURIComponent("update_setIN1,2,3")) }; @@ -255,27 +260,27 @@ describe("RESTClientSpec", () => { .expectCalledWith(expect.objectContaining(url), expect.anything(), undefined).mockResolvedValue(response); const client = new RESTClient(oauthClient); - await expect(client.loadUpdateXMLByUpdateSetIds(profile, "1","2","3")).resolves.toStrictEqual(responseBody); + await expect(client.loadUpdateXMLByUpdateSetIds(profile, "1", "2", "3")).resolves.toStrictEqual(responseBody); }); it("should load by update set query", async() => { - const setResponseBody: Array = [ + const setResponseBody: SNUpdateSetData[] = [ { sys_id: "1" }, { sys_id: "2" } - ] + ]; const setURL = { origin: profile.getBaseUrl(), - pathname: TableAPI.UPDATE_SET_PATH, + pathname: restclient.PATH_API_TABLE_UPDATE_SET, search: expect.stringContaining(encodeURIComponent("sys_idIN1,2")) }; const xmlURL = { origin: profile.getBaseUrl(), - pathname: TableAPI.UPDATE_XML_PATH, + pathname: restclient.PATH_API_TABLE_UPDATE_XML, search: expect.stringContaining(encodeURIComponent("update_setIN1,2")) }; @@ -297,5 +302,4 @@ describe("RESTClientSpec", () => { await expect(client.loadUpdateXMLByUpdateSetQuery(profile, "sys_idIN1,2")).resolves.toStrictEqual(responseBody); }); }); - }); \ No newline at end of file diff --git a/spec/core/Request.spec.ts b/spec/core/Request.spec.ts index 4038295..f64b9f7 100644 --- a/spec/core/Request.spec.ts +++ b/spec/core/Request.spec.ts @@ -6,7 +6,7 @@ describe("RequestSpec", () => { const url = new URL("http://example.com"); const options: RequestOptions = { method: "GET" - } + }; await expect(Request.execute(url, options)).rejects.toStrictEqual(Response.empty("URL protocol must be https!")); }); @@ -14,7 +14,7 @@ describe("RequestSpec", () => { const url = new URL("https://example.com"); const options: RequestOptions = { method: "GET" - } + }; const response = await Request.execute(url, options); expect(response.isEmpty()).toBe(false); diff --git a/spec/linter/Linter.spec.ts b/spec/linter/Linter.spec.ts index f8b8df0..1a921ff 100644 --- a/spec/linter/Linter.spec.ts +++ b/spec/linter/Linter.spec.ts @@ -1,5 +1,5 @@ import fs from "fs"; -import { UpdateXMLScan } from "../../src/linter/UpdateXMLScan.js" +import { UpdateXMLScan } from "../../src/linter/UpdateXMLScan.js"; import { Linter } from "../../src/linter/Linter.js"; import { Profile } from "../../src/core/ProfileManager.js"; diff --git a/src/cli/helpers.ts b/src/cli/helpers.ts index 38f5a64..4771a39 100644 --- a/src/cli/helpers.ts +++ b/src/cli/helpers.ts @@ -44,8 +44,7 @@ export function debug(options: any): void { }; export function forceOption(): Option { - return new Option("--force") - .default(false); + return new Option("--force"); } // eslint-disable-next-line @typescript-eslint/no-explicit-any export function isForce(options: any): boolean { diff --git a/src/cli/now-eslint-profile.ts b/src/cli/now-eslint-profile.ts deleted file mode 100644 index aa83780..0000000 --- a/src/cli/now-eslint-profile.ts +++ /dev/null @@ -1,19 +0,0 @@ -import commander from "commander"; -import * as helpers from "./helpers.js"; - -try { - // Program setup; program is never meant to be run directly only as a subcommand - const program = new commander.Command("profile") - .description("Command to manage now-eslint profiles") - .configureOutput({outputError: helpers.outputError}) - .executableDir("subcommands") - .showHelpAfterError(); - - program.command("create", {isDefault: true}); - program.command("view"); - program.command("purge"); - - program.parseAsync(process.argv); -} catch (err) { - helpers.outputError(`${err}`); -}; \ No newline at end of file diff --git a/src/cli/now-eslint-report.ts b/src/cli/now-eslint-report.ts deleted file mode 100644 index ce5123e..0000000 --- a/src/cli/now-eslint-report.ts +++ /dev/null @@ -1,26 +0,0 @@ -import commander from "commander"; -import * as helpers from "./helpers.js"; - -try { - const program = new commander.Command("report") - .description("Command to generate update set eslint report from Service Now instance") - .configureOutput({outputError: helpers.outputError}) - .argument("", `name of the profile; ${helpers.PROFILE_HELP}`, helpers.validateProfileName) - .option("-t, --title ", "title of the report (in quotes if multiword)") - .option("-f, --file-name ", "file name of the report without an extension", helpers.validateFileName) - .option("-q, --query ", "update set query to perform report on (in quotes if multiword)") - /* - * .option("--json", "generate report as JSON rather than PDF report") - * .option("--with-json", "generate JSON for the PDF report; ignored if option --json is used") - * .option("--from-json ", "generate PDF report from provided JSON file; ignores all options") - */ - .addOption(helpers.debugOption()) - .showHelpAfterError() - .action(async(name, options) => { - // TODO: - }); - - program.parseAsync(process.argv); -} catch (err) { - helpers.outputError(`${err}`); -}; \ No newline at end of file diff --git a/src/cli/now-eslint.ts b/src/cli/now-eslint.ts index b4b12c0..5dbd56d 100644 --- a/src/cli/now-eslint.ts +++ b/src/cli/now-eslint.ts @@ -1,18 +1,26 @@ #!/usr/bin/env node +import dotenv from "dotenv"; import { Command } from "commander"; import { outputError } from "./helpers.js"; import { PACKAGE_VERSION } from "../core/Package.js"; +import { profileCommand } from "./subcommands/now-eslint-profile.js"; +import { reportCommand } from "./subcommands/now-eslint-report.js"; +// Initialize dotenv +try { + dotenv.config(); +// eslint-disable-next-line no-empty, @typescript-eslint/no-unused-vars +} catch (err) {} + try { const program = new Command() .name("now-eslint") .description("CLI to ESLint Service Now update sets") .version(PACKAGE_VERSION, "-v, --version", "current version") - .executableDir("./") - .configureOutput({outputError: outputError}); - - program.command("profile"); - program.command("report", {isDefault: true}); + .showHelpAfterError() + .configureOutput({outputError: outputError}) + .addCommand(reportCommand, {isDefault: true}) + .addCommand(profileCommand); program.parseAsync(process.argv); } catch (err) { diff --git a/src/cli/subcommands/now-eslint-profile-create.ts b/src/cli/subcommands/now-eslint-profile-create.ts index cbfdcb4..f8449e6 100644 --- a/src/cli/subcommands/now-eslint-profile-create.ts +++ b/src/cli/subcommands/now-eslint-profile-create.ts @@ -1,27 +1,15 @@ -import dotenv from "dotenv"; import commander from "commander"; - import * as helpers from "../helpers.js"; -// Initialize dotenv -try { - dotenv.config(); -// eslint-disable-next-line no-empty, @typescript-eslint/no-unused-vars -} catch (err) {} +const createCommand = new commander.Command("create") + .description("create new profile for the ServiceNow instance") + .argument("", `name of the profile; ${helpers.PROFILE_HELP}`, helpers.validateProfileName) + .option("--proxy", "set up proxy connection configuration") + .addOption(helpers.forceOption()) + .addOption(helpers.debugOption()); -try { - const program = new commander.Command("create") - .description("create new profile for the ServiceNow instance (default)") - .argument("", `name of the profile; ${helpers.PROFILE_HELP}`, helpers.validateProfileName) - .option("-d, --domain ", `the URL to the ServiceNow instance; ${helpers.DOMAIN_HELP}`, helpers.validateDomain) - .option("--proxy", "proxy connection configuration") - .addOption(helpers.forceOption()) - .addOption(helpers.debugOption()) - .action(async function(name, options) { - // TODO:! - }); +createCommand.action(async function(name, options) { + // TODO:! +}); - program.parseAsync(process.argv); -} catch (err) { - helpers.outputError(`${err}`); -}; \ No newline at end of file +export { createCommand }; \ No newline at end of file diff --git a/src/cli/subcommands/now-eslint-profile-purge.ts b/src/cli/subcommands/now-eslint-profile-purge.ts index f4b2a8d..cfa03f7 100644 --- a/src/cli/subcommands/now-eslint-profile-purge.ts +++ b/src/cli/subcommands/now-eslint-profile-purge.ts @@ -1,32 +1,22 @@ -import dotenv from "dotenv"; import commander from "commander"; import * as helpers from "../helpers.js"; -import * as ProfileManager from "../../core/ProfileManager.js"; - -// Initialize dotenv -try { - dotenv.config(); -// eslint-disable-next-line no-empty, @typescript-eslint/no-unused-vars -} catch (err) {} - -const program = new commander.Command("purge") - .addOption(helpers.debugOption()); const PURGE_CONFIRM = "PURGE"; -program +const purgeCommand = new commander.Command("purge") .description("purge single existing ServiceNow instance profile") .argument("", "name of the profile to set up (lowecase/uppercase letters, numbers, underscore and dash)", helpers.validateProfileName) + .addOption(helpers.debugOption()) .addOption(helpers.forceOption()) .action(async function(name, options) { // TODO: }); -program.command("all") +purgeCommand.command("all") .description("purge all existing ServiceNow instance profiles") .action(async function(options) { // TODO: }); -program.parseAsync(process.argv); \ No newline at end of file +export {purgeCommand}; \ No newline at end of file diff --git a/src/cli/subcommands/now-eslint-profile-view.ts b/src/cli/subcommands/now-eslint-profile-view.ts index 8c60667..a2da804 100644 --- a/src/cli/subcommands/now-eslint-profile-view.ts +++ b/src/cli/subcommands/now-eslint-profile-view.ts @@ -2,13 +2,10 @@ import commander from "commander"; import * as helpers from "../helpers.js"; -const program = new commander.Command("view"); - -program +const viewCommand = new commander.Command("view") .argument("", "name of the profile to set up (lowecase/uppercase letters, numbers, underscore and dash)", helpers.validateProfileName) .option("-t, --test-connection", "test connection to the instance") .action(async function(name, options) { // TODO: }); - -program.parseAsync(process.argv); \ No newline at end of file +export {viewCommand}; \ No newline at end of file diff --git a/src/cli/subcommands/now-eslint-profile.ts b/src/cli/subcommands/now-eslint-profile.ts new file mode 100644 index 0000000..d42931e --- /dev/null +++ b/src/cli/subcommands/now-eslint-profile.ts @@ -0,0 +1,13 @@ +import commander from "commander"; +import { createCommand } from "./now-eslint-profile-create.js"; +import { viewCommand } from "./now-eslint-profile-view.js"; +import { purgeCommand } from "./now-eslint-profile-purge.js"; + +// Program setup; program is never meant to be run directly only as a subcommand +const profileCommand = new commander.Command("profile") + .description("Command to manage now-eslint profiles") + .addCommand(createCommand, {isDefault: true}) + .addCommand(viewCommand) + .addCommand(purgeCommand); + +export { profileCommand }; \ No newline at end of file diff --git a/src/cli/subcommands/now-eslint-report.ts b/src/cli/subcommands/now-eslint-report.ts new file mode 100644 index 0000000..7081a9d --- /dev/null +++ b/src/cli/subcommands/now-eslint-report.ts @@ -0,0 +1,22 @@ +import commander from "commander"; +import * as helpers from "../helpers.js"; + +const reportCommand = new commander.Command("report") + .description("Command to generate update set eslint report from Service Now instance") + .configureOutput({outputError: helpers.outputError}) + .argument("", `name of the profile; ${helpers.PROFILE_HELP}`, helpers.validateProfileName) + .option("-t, --title ", "title of the report (in quotes if multiword)") + .option("-f, --file-name ", "file name of the report without an extension", helpers.validateFileName) + .option("-q, --query ", "update set query to perform report on (in quotes if multiword)") + /* + * .option("--json", "generate report as JSON rather than PDF report") + * .option("--with-json", "generate JSON for the PDF report; ignored if option --json is used") + * .option("--from-json ", "generate PDF report from provided JSON file; ignores all options") + */ + .addOption(helpers.debugOption()) + .showHelpAfterError() + .action(async(name, options) => { + // TODO: + }); + +export {reportCommand}; \ No newline at end of file