Add guided flow using prompts for cli (#2252)

## Description

This PR adds prompts, when the cli is ran using `webstudio` and without
any commands. It requests the users if they want to create a folder. If
yes, it will create a folder and changes the directory to the newly
created ones.

This PR even allows `webstudio link` command to be executed without
prompts using

```sh
webstudio link -l <link>
```

## Code Review

- [ ] hi @kof, I need you to do
  - conceptual review (architecture, feature-correctness)
  - test it on preview
 
- [ ] hi @TrySound , I need you to do
  - conceptual review (architecture, feature-correctness)
 

## Before requesting a review
- [x] made a self-review
This commit is contained in:
Jaya Krishna Namburu authored and GitHub committed 2023-09-06 14:34:36 +02:00
1 parent 87e69b2e85
commit 81781ee199
7 files changed
+112 -10

No files matched your search

+2
View File
@@ -34,11 +34,13 @@
"env-paths": "^3.0.0",
"ora": "^7.0.1",
"p-limit": "^4.0.0",
"prompts": "^2.4.2",
"strip-indent": "^4.0.0",
"yargs": "^17.7.2"
},
"devDependencies": {
"@types/node": "^18.13.0",
"@types/prompts": "^2.4.4",
"@webstudio-is/form-handlers": "workspace:^",
"@webstudio-is/scripts": "workspace:^",
"@webstudio-is/tsconfig": "workspace:^",
+11 -5
View File
@@ -3,9 +3,10 @@ import { exit, argv } from "node:process";
import { hideBin } from "yargs/helpers";
import { GLOBAL_CONFIG_FILE } from "./config";
import { ensureFileInPath } from "./fs-utils";
import { link } from "./commands/link";
import { link, linkOptions } from "./commands/link";
import { sync } from "./commands/sync";
import { build, buildOptions } from "./commands/build";
import { initFlow } from "./commands/init-flow";
import makeCLI from "yargs";
import packageJson from "../package.json" assert { type: "json" };
import type { CommonYargsArgv } from "./commands/yargs-types";
@@ -34,18 +35,23 @@ export const main = async () => {
type: "boolean",
})
.option("h", {
describe: "Show version number",
describe: "Show all commands",
alias: "help",
type: "boolean",
})
.scriptName("webstudio")
.demandCommand(1, "Command is required");
.scriptName("webstudio");
cmd.version(packageJson.version).alias("v", "version");
cmd.command(["build"], "build the project", buildOptions, build);
cmd.command(["link"], "link the project from your workspace", {}, link);
cmd.command(
["link"],
"link the project from your workspace",
linkOptions,
link
);
cmd.command(["sync"], "sync your project", {}, sync);
cmd.command("$0", "setup the project", {}, initFlow);
await cmd.parse();
} catch (error) {
+3 -1
View File
@@ -15,7 +15,9 @@ export const buildOptions = (yargs: CommonYargsArgv) =>
// @todo: use options.assets to define if we need to download assets
export const build = async (
options: StrictYargsOptionsToInterface<typeof buildOptions>
options:
| StrictYargsOptionsToInterface<typeof buildOptions>
| { assets?: boolean }
) => {
try {
await access(LOCAL_DATA_FILE);
+50
View File
@@ -0,0 +1,50 @@
import prompts, { type PromptObject } from "prompts";
import { ensureFolderExists, isFileExists } from "../fs-utils";
import { chdir, cwd } from "node:process";
import { join } from "node:path";
import { link } from "./link";
import { sync } from "./sync";
import { build } from "./build";
export const initFlow = async () => {
const isProjectConfigured = await isFileExists(".webstudio/config.json");
const prompsList: PromptObject[] = [];
if (isProjectConfigured === false) {
prompsList.push(
{
type: "confirm",
name: "folder",
message: "Do you want to create a folder",
initial: true,
},
{
type: (prev) => (prev === true ? "text" : null),
name: "folderName",
message: "Enter a project name",
},
{
type: (prev) => (prev === undefined ? null : "text"),
name: "projectLink",
message: "Enter a project link",
}
);
}
const response = await prompts(prompsList);
/*
If users wanted to create a folder, we need to create it and link the project.
*/
if (response.folderName) {
await ensureFolderExists(join(cwd(), response.folderName));
chdir(join(cwd(), response.folderName));
await link({ link: response.projectLink });
}
/*
If the project is already set up, we can sync and build it.
*/
await sync();
await build({ assets: true });
};
+24 -4
View File
@@ -4,10 +4,31 @@ import * as readline from "node:readline/promises";
import { readFile, writeFile } from "node:fs/promises";
import { GLOBAL_CONFIG_FILE, LOCAL_CONFIG_FILE } from "../config";
import { ensureFileInPath } from "../fs-utils";
import type {
CommonYargsArgv,
StrictYargsOptionsToInterface,
} from "./yargs-types";
export const link = async () => {
const rl = readline.createInterface({ input: stdin, output: stdout });
const shareLink = await rl.question(`Paste share link (with build access): `);
export const linkOptions = (yargs: CommonYargsArgv) =>
yargs.option("link", {
alias: "l",
type: "string",
describe: "Link to a webstudio project",
});
export const link = async (
options: StrictYargsOptionsToInterface<typeof linkOptions> | { link?: string }
) => {
let shareLink: string;
if (options.link) {
shareLink = options.link;
} else {
const rl = readline.createInterface({ input: stdin, output: stdout });
shareLink = await rl.question(
`Please paste a link from the Share Dialog in the builder: `
);
rl.close();
}
const shareLinkUrl = new URL(shareLink);
const host = shareLinkUrl.origin;
@@ -34,7 +55,6 @@ export const link = async () => {
};
await writeFile(GLOBAL_CONFIG_FILE, JSON.stringify(newConfig, null, 2));
rl.close();
console.info(`Saved credentials for project ${projectId}.
You can find your config at ${GLOBAL_CONFIG_FILE}
`);
+9
View File
@@ -9,6 +9,15 @@ import {
import { join } from "node:path";
import type { Folder } from "./args";
export const isFileExists = async (filePath: string) => {
try {
await access(filePath, constants.F_OK);
return true;
} catch {
return false;
}
};
export const ensureFileInPath = async (filePath: string, content?: string) => {
const dir = dirname(filePath);
+13
View File
@@ -480,6 +480,9 @@ importers:
p-limit:
specifier: ^4.0.0
version: 4.0.0
prompts:
specifier: ^2.4.2
version: 2.4.2
strip-indent:
specifier: ^4.0.0
version: 4.0.0
@@ -490,6 +493,9 @@ importers:
'@types/node':
specifier: ^18.13.0
version: 18.13.0
'@types/prompts':
specifier: ^2.4.4
version: 2.4.4
'@webstudio-is/form-handlers':
specifier: workspace:^
version: link:../form-handlers
@@ -7579,6 +7585,13 @@ packages:
resolution: {integrity: sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==}
dev: true
/@types/prompts@2.4.4:
resolution: {integrity: sha512-p5N9uoTH76lLvSAaYSZtBCdEXzpOOufsRjnhjVSrZGXikVGHX9+cc9ERtHRV4hvBKHyZb1bg4K+56Bd2TqUn4A==}
dependencies:
'@types/node': 18.17.1
kleur: 3.0.3
dev: true
/@types/prop-types@15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}