Skip to content

Commit

Permalink
refactor: split cli command/register to fix and make testing easier
Browse files Browse the repository at this point in the history
  • Loading branch information
sinedied committed Jan 9, 2023
1 parent b711262 commit 8b9e061
Show file tree
Hide file tree
Showing 21 changed files with 339 additions and 328 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import mockFs from "mock-fs";
import { build } from "./build";
import { DEFAULT_CONFIG } from "../../config";
import { convertToNativePaths } from "../../jest.helpers.";
import { DEFAULT_CONFIG } from "../../../config";
import { convertToNativePaths } from "../../../jest.helpers.";

jest.mock("child_process", () => ({
execSync: jest.fn(),
Expand Down
37 changes: 2 additions & 35 deletions src/cli/commands/build.ts → src/cli/commands/build/build.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,15 @@
import path from "path";
import chalk from "chalk";
import { Command } from "commander";
import { DEFAULT_CONFIG } from "../../config";
import { detectProjectFolders, generateConfiguration } from "../../core/frameworks";
import { detectProjectFolders, generateConfiguration } from "../../../core/frameworks";
import {
configureOptions,
findUpPackageJsonDir,
isUserOption,
isUserOrConfigOption,
logger,
matchLoadedConfigName,
pathExists,
readWorkflowFile,
runCommand,
swaCliConfigFilename,
} from "../../core/utils";

export default function registerCommand(program: Command) {
program
.command("build [configName|appLocation]")
.usage("[configName|appLocation] [options]")
.description("build your project")
.option("-a, --app-location <path>", "the folder containing the source code of the front-end application", DEFAULT_CONFIG.appLocation)
.option("-i, --api-location <path>", "the folder containing the source code of the API application", DEFAULT_CONFIG.apiLocation)
.option("-O, --output-location <path>", "the folder containing the built source of the front-end application", DEFAULT_CONFIG.outputLocation)
.option("-A, --app-build-command <command>", "the command used to build your app", DEFAULT_CONFIG.appBuildCommand)
.option("-I, --api-build-command <command>", "the command used to build your api", DEFAULT_CONFIG.apiBuildCommand)
.option("--auto", "automatically detect how to build your app and api", false)
.action(async (positionalArg: string | undefined, _options: SWACLIConfig, command: Command) => {
positionalArg = positionalArg?.trim();
const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "build");
if (positionalArg && !matchLoadedConfigName(positionalArg)) {
if (isUserOption("appLocation")) {
logger.error(`swa build <appLocation> cannot be when with --app-location option is also set.`);
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
}

// If it's not the config name, then it's the app location
options.appLocation = positionalArg;
}

await build(options);
});
}
} from "../../../core/utils";

export async function build(options: SWACLIConfig) {
const workflowConfig = readWorkflowFile();
Expand Down
2 changes: 2 additions & 0 deletions src/cli/commands/build/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./build";
export { default as registerBuild } from "./register";
32 changes: 32 additions & 0 deletions src/cli/commands/build/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Command } from "commander";
import { DEFAULT_CONFIG } from "../../../config";
import { configureOptions, isUserOption, logger, matchLoadedConfigName } from "../../../core/utils";
import { build } from "./build";

export default function registerCommand(program: Command) {
program
.command("build [configName|appLocation]")
.usage("[configName|appLocation] [options]")
.description("build your project")
.option("-a, --app-location <path>", "the folder containing the source code of the front-end application", DEFAULT_CONFIG.appLocation)
.option("-i, --api-location <path>", "the folder containing the source code of the API application", DEFAULT_CONFIG.apiLocation)
.option("-O, --output-location <path>", "the folder containing the built source of the front-end application", DEFAULT_CONFIG.outputLocation)
.option("-A, --app-build-command <command>", "the command used to build your app", DEFAULT_CONFIG.appBuildCommand)
.option("-I, --api-build-command <command>", "the command used to build your api", DEFAULT_CONFIG.apiBuildCommand)
.option("--auto", "automatically detect how to build your app and api", false)
.action(async (positionalArg: string | undefined, _options: SWACLIConfig, command: Command) => {
positionalArg = positionalArg?.trim();
const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "build");
if (positionalArg && !matchLoadedConfigName(positionalArg)) {
if (isUserOption("appLocation")) {
logger.error(`swa build <appLocation> cannot be when with --app-location option is also set.`);
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
}

// If it's not the config name, then it's the app location
options.appLocation = positionalArg;
}

await build(options);
});
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import child_process from "child_process";
import mockFs from "mock-fs";
import path from "path";
import { logger } from "../../core";
import * as accountModule from "../../core/account";
import * as deployClientModule from "../../core/deploy-client";
import { logger } from "../../../core";
import * as accountModule from "../../../core/account";
import * as deployClientModule from "../../../core/deploy-client";
import { deploy } from "./deploy";
import * as loginModule from "./login";
import * as loginModule from "../login/login";

const pkg = require(path.join(__dirname, "..", "..", "..", "package.json"));
const pkg = require(path.join(__dirname, "..", "..", "..", "..", "package.json"));

jest.mock("ora", () => {
return jest.fn();
});

jest.mock("../../core/utils/logger", () => {
jest.mock("../../../core/utils/logger", () => {
return {
logger: {
error: jest.fn(),
Expand Down
74 changes: 6 additions & 68 deletions src/cli/commands/deploy.ts → src/cli/commands/deploy/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,84 +1,22 @@
import chalk from "chalk";
import { spawn } from "child_process";
import { Command } from "commander";
import fs from "fs";
import ora, { Ora } from "ora";
import path from "path";
import { DEFAULT_CONFIG } from "../../config";
import {
configureOptions,
findSWAConfigFile,
getCurrentSwaCliConfigFromFile,
isUserOption,
logger,
logGiHubIssueMessageAndExit,
matchLoadedConfigName,
readWorkflowFile,
updateSwaCliConfigFile,
} from "../../core";
import { chooseOrCreateProjectDetails, getStaticSiteDeployment } from "../../core/account";
import { cleanUp, getDeployClientPath } from "../../core/deploy-client";
import { swaCLIEnv } from "../../core/env";
import { addSharedLoginOptionsToCommand, login } from "./login";

const packageInfo = require(path.join(__dirname, "..", "..", "..", "package.json"));

export default function registerCommand(program: Command) {
const deployCommand = program
.command("deploy [configName|outputLocation]")
.usage("[configName|outputLocation] [options]")
.description("deploy the current project to Azure Static Web Apps")
.option("-a, --app-location <path>", "the folder containing the source code of the front-end application", DEFAULT_CONFIG.appLocation)
.option("-i, --api-location <path>", "the folder containing the source code of the API application", DEFAULT_CONFIG.apiLocation)
.option("-O, --output-location <path>", "the folder containing the built source of the front-end application", DEFAULT_CONFIG.outputLocation)
.option(
"-w, --swa-config-location <swaConfigLocation>",
"the directory where the staticwebapp.config.json file is located",
DEFAULT_CONFIG.swaConfigLocation
)
.option("-d, --deployment-token <secret>", "the secret token used to authenticate with the Static Web Apps")
.option("-dr, --dry-run", "simulate a deploy process without actually running it", DEFAULT_CONFIG.dryRun)
.option("-pt, --print-token", "print the deployment token", false)
.option("--env [environment]", "the type of deployment environment where to deploy the project", DEFAULT_CONFIG.env)
.action(async (positionalArg: string | undefined, _options: SWACLIConfig, command: Command) => {
positionalArg = positionalArg?.trim();
const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "deploy");
if (positionalArg && !matchLoadedConfigName(positionalArg)) {
if (isUserOption("outputLocation")) {
logger.error(`swa deploy <outputLocation> cannot be used when --output-location option is also set.`);
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
}

// If it's not the config name, then it's the output location
options.outputLocation = positionalArg;
}

await deploy(options);
})
.addHelpText(
"after",
`
Examples:
Deploy using a deployment token
swa deploy ./dist/ --api-location ./api/ --deployment-token <token>
} from "../../../core";
import { chooseOrCreateProjectDetails, getStaticSiteDeployment } from "../../../core/account";
import { cleanUp, getDeployClientPath } from "../../../core/deploy-client";
import { swaCLIEnv } from "../../../core/env";
import { login } from "../login";

Deploy using a deployment token from env
SWA_CLI_DEPLOYMENT_TOKEN=123 swa deploy ./dist/ --api-location ./api/
Deploy using swa-cli.config.json file
swa deploy
swa deploy myconfig
Print the deployment token
swa deploy --print-token
Deploy to a specific environment
swa deploy --env production
`
);
addSharedLoginOptionsToCommand(deployCommand);
}
const packageInfo = require(path.join(__dirname, "..", "..", "..", "..", "package.json"));

export async function deploy(options: SWACLIConfig) {
const { SWA_CLI_DEPLOYMENT_TOKEN, SWA_CLI_DEBUG } = swaCLIEnv();
Expand Down
2 changes: 2 additions & 0 deletions src/cli/commands/deploy/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./deploy";
export { default as registerDeploy } from "./register";
62 changes: 62 additions & 0 deletions src/cli/commands/deploy/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Command } from "commander";
import { DEFAULT_CONFIG } from "../../../config";
import { configureOptions, isUserOption, logger, matchLoadedConfigName } from "../../../core";
import { addSharedLoginOptionsToCommand } from "../login";
import { deploy } from "./deploy";

export default function registerCommand(program: Command) {
const deployCommand = program
.command("deploy [configName|outputLocation]")
.usage("[configName|outputLocation] [options]")
.description("deploy the current project to Azure Static Web Apps")
.option("-a, --app-location <path>", "the folder containing the source code of the front-end application", DEFAULT_CONFIG.appLocation)
.option("-i, --api-location <path>", "the folder containing the source code of the API application", DEFAULT_CONFIG.apiLocation)
.option("-O, --output-location <path>", "the folder containing the built source of the front-end application", DEFAULT_CONFIG.outputLocation)
.option(
"-w, --swa-config-location <swaConfigLocation>",
"the directory where the staticwebapp.config.json file is located",
DEFAULT_CONFIG.swaConfigLocation
)
.option("-d, --deployment-token <secret>", "the secret token used to authenticate with the Static Web Apps")
.option("-dr, --dry-run", "simulate a deploy process without actually running it", DEFAULT_CONFIG.dryRun)
.option("-pt, --print-token", "print the deployment token", false)
.option("--env [environment]", "the type of deployment environment where to deploy the project", DEFAULT_CONFIG.env)
.action(async (positionalArg: string | undefined, _options: SWACLIConfig, command: Command) => {
positionalArg = positionalArg?.trim();
const options = await configureOptions(positionalArg, command.optsWithGlobals(), command, "deploy");
if (positionalArg && !matchLoadedConfigName(positionalArg)) {
if (isUserOption("outputLocation")) {
logger.error(`swa deploy <outputLocation> cannot be used when --output-location option is also set.`);
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
}

// If it's not the config name, then it's the output location
options.outputLocation = positionalArg;
}

await deploy(options);
})
.addHelpText(
"after",
`
Examples:
Deploy using a deployment token
swa deploy ./dist/ --api-location ./api/ --deployment-token <token>
Deploy using a deployment token from env
SWA_CLI_DEPLOYMENT_TOKEN=123 swa deploy ./dist/ --api-location ./api/
Deploy using swa-cli.config.json file
swa deploy
swa deploy myconfig
Print the deployment token
swa deploy --print-token
Deploy to a specific environment
swa deploy --env production
`
);
addSharedLoginOptionsToCommand(deployCommand);
}
2 changes: 1 addition & 1 deletion src/cli/commands/docs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Command } from "commander";
import open from "open";

export default function registerDocs(program: Command) {
export function registerDocs(program: Command) {
program
.command("docs")
.description("open Azure Static Web Apps CLI documentations")
Expand Down
2 changes: 2 additions & 0 deletions src/cli/commands/init/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./init";
export { default as registerInit } from "./register";
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import fs from "fs";
import mockFs from "mock-fs";
import { init } from "./init";
import { DEFAULT_CONFIG } from "../../config";
import { swaCliConfigFilename } from "../../core/utils";
import { convertToNativePaths, convertToUnixPaths } from "../../jest.helpers.";
import { DEFAULT_CONFIG } from "../../../config";
import { swaCliConfigFilename } from "../../../core/utils";
import { convertToNativePaths, convertToUnixPaths } from "../../../jest.helpers.";

jest.mock("prompts", () => jest.fn());

Expand Down
30 changes: 3 additions & 27 deletions src/cli/commands/init.ts → src/cli/commands/init/init.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,16 @@
import chalk from "chalk";
import { Command } from "commander";
import path from "path";
import process from "process";
import { promptOrUseDefault } from "../../core/prompts";
import { promptOrUseDefault } from "../../../core/prompts";
import {
configureOptions,
dasherize,
hasConfigurationNameInConfigFile,
isUserOption,
logger,
swaCliConfigFileExists,
swaCliConfigFilename,
writeConfigFile,
} from "../../core/utils";
import { detectProjectFolders, generateConfiguration, isDescendantPath } from "../../core/frameworks";

export default function registerCommand(program: Command) {
program
.command("init [configName]")
.usage("[configName] [options]")
.description("initialize a new static web app project")
.option("-y, --yes", "answer yes to all prompts (disable interactive mode)", false)
.action(async (configName: string | undefined, _options: SWACLIConfig, command: Command) => {
const options = await configureOptions(undefined, command.optsWithGlobals(), command, "init", false);
if (configName) {
if (isUserOption("configName")) {
logger.error(`swa init <configName> cannot be used when --config-name option is also set.`);
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
}

options.configName = configName;
}

await init(options, !process.env.SWA_CLI_INTERNAL_COMMAND);
});
}
} from "../../../core/utils";
import { detectProjectFolders, generateConfiguration, isDescendantPath } from "../../../core/frameworks";

export async function init(options: SWACLIConfig, showHints: boolean = true) {
const configFilePath = options.config!;
Expand Down
25 changes: 25 additions & 0 deletions src/cli/commands/init/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Command } from "commander";
import process from "process";
import { configureOptions, isUserOption, logger } from "../../../core/utils";
import { init } from "./init";

export default function registerCommand(program: Command) {
program
.command("init [configName]")
.usage("[configName] [options]")
.description("initialize a new static web app project")
.option("-y, --yes", "answer yes to all prompts (disable interactive mode)", false)
.action(async (configName: string | undefined, _options: SWACLIConfig, command: Command) => {
const options = await configureOptions(undefined, command.optsWithGlobals(), command, "init", false);
if (configName) {
if (isUserOption("configName")) {
logger.error(`swa init <configName> cannot be used when --config-name option is also set.`);
logger.error(`You either have to use the positional argument or option, not both at the same time.`, true);
}

options.configName = configName;
}

await init(options, !process.env.SWA_CLI_INTERNAL_COMMAND);
});
}
2 changes: 2 additions & 0 deletions src/cli/commands/login/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./login";
export { default as registerLogin, addSharedLoginOptionsToCommand } from "./register";

0 comments on commit 8b9e061

Please sign in to comment.