Improved the CLI prompts
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@trigger.dev/init": patch
|
||||
---
|
||||
|
||||
Improved the CLI prompts
|
||||
@@ -21,6 +21,7 @@
|
||||
"dirty-needles-kneel",
|
||||
"four-ligers-perform",
|
||||
"great-meals-listen",
|
||||
"heavy-mails-explain",
|
||||
"itchy-rockets-provide",
|
||||
"modern-emus-suffer",
|
||||
"quick-stingrays-own",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# create-trigger
|
||||
|
||||
## 0.2.1-next.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Improved the CLI prompts
|
||||
|
||||
## 0.2.1-next.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/init",
|
||||
"version": "0.2.1-next.2",
|
||||
"version": "0.2.1-next.3",
|
||||
"description": "The CLI to easily initialize Trigger.dev in your Next.js project",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Command } from "commander";
|
||||
import inquirer from "inquirer";
|
||||
import { COMMAND_NAME } from "../consts.js";
|
||||
import { COMMAND_NAME, DEFAULT_TRIGGER_URL } from "../consts.js";
|
||||
import { getVersion } from "../utils/getVersion.js";
|
||||
import { logger } from "../utils/logger.js";
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface CliResults {
|
||||
const defaultOptions: CliResults = {
|
||||
flags: {
|
||||
projectPath: ".",
|
||||
triggerUrl: "https://cloud.trigger.dev",
|
||||
triggerUrl: DEFAULT_TRIGGER_URL,
|
||||
endpointUrl: "http://localhost:3000",
|
||||
endpointSlug: "my-nextjs-project",
|
||||
},
|
||||
@@ -48,13 +48,11 @@ export const parseCliOptions = async () => {
|
||||
)
|
||||
.option(
|
||||
"-u, --endpoint-url <endpoint-url>",
|
||||
"The URL of your local Next.js project. (e.g. http://localhost:3000). NOTE: Must be a publicly accessible URL if you are using a deployed Trigger.dev instance",
|
||||
"http://localhost:3000"
|
||||
"The URL of your local Next.js project. (e.g. http://localhost:3000). NOTE: Must be a publicly accessible URL if you are using a deployed Trigger.dev instance"
|
||||
)
|
||||
.option(
|
||||
"-t, --trigger-url <trigger-url>",
|
||||
"The URL of the Trigger.dev instance to use. (e.g. https://cloud.trigger.dev)",
|
||||
"https://cloud.trigger.dev"
|
||||
"The URL of the Trigger.dev instance to use. (e.g. https://cloud.trigger.dev)"
|
||||
)
|
||||
.version(getVersion(), "-v, --version", "Display the version number")
|
||||
.parse(process.argv);
|
||||
@@ -66,13 +64,23 @@ export const parseCliOptions = async () => {
|
||||
|
||||
export const runCliPrompts = async (cliResults: CliResults) => {
|
||||
try {
|
||||
if (!cliResults.flags.triggerUrl) {
|
||||
cliResults.flags.triggerUrl = await promptTriggerUrl();
|
||||
}
|
||||
|
||||
if (!cliResults.flags.apiKey) {
|
||||
cliResults.flags.apiKey = await promptApiKey();
|
||||
cliResults.flags.apiKey = await promptApiKey(cliResults.flags.triggerUrl);
|
||||
}
|
||||
|
||||
if (!cliResults.flags.endpointSlug) {
|
||||
cliResults.flags.endpointSlug = await promptEndpointSlug();
|
||||
}
|
||||
|
||||
if (!cliResults.flags.endpointUrl) {
|
||||
cliResults.flags.endpointUrl = await promptEndpointUrl(
|
||||
cliResults.flags.triggerUrl
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
// If the user is not calling the command from an interactive terminal, inquirer will throw an error with isTTYError = true
|
||||
// If this happens, we catch the error, tell the user what has happened, and then continue to run the program with a default trigger project
|
||||
@@ -104,12 +112,54 @@ export const runCliPrompts = async (cliResults: CliResults) => {
|
||||
return cliResults;
|
||||
};
|
||||
|
||||
const promptApiKey = async (): Promise<string> => {
|
||||
const promptTriggerUrl = async (): Promise<string> => {
|
||||
const { instanceType } = await inquirer.prompt<{
|
||||
instanceType: "cloud" | "self-hosted";
|
||||
}>([
|
||||
{
|
||||
type: "list",
|
||||
name: "instanceType",
|
||||
message: "Are you using the Trigger.dev cloud or self-hosted?",
|
||||
choices: [
|
||||
{
|
||||
name: "Trigger.dev Cloud",
|
||||
value: "cloud",
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
name: "Self hosted",
|
||||
value: "self-hosted",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
if (instanceType === "cloud") {
|
||||
return DEFAULT_TRIGGER_URL;
|
||||
}
|
||||
|
||||
const { triggerUrl } = await inquirer.prompt<{ triggerUrl: string }>({
|
||||
type: "input",
|
||||
name: "triggerUrl",
|
||||
message: "Enter the URL of your self-hosted Trigger.dev instance",
|
||||
validate: (input) => {
|
||||
if (!input) {
|
||||
return "Please enter the URL of your self-hosted Trigger.dev instance";
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
return triggerUrl;
|
||||
};
|
||||
|
||||
const promptApiKey = async (instanceUrl: string): Promise<string> => {
|
||||
// First prompt if they want to enter their API key now, and if they say Yes, then prompt for it and return it
|
||||
const { apiKey } = await inquirer.prompt<{ apiKey: string }>({
|
||||
type: "input",
|
||||
name: "apiKey",
|
||||
message: "Enter your development API key (required)",
|
||||
message: `Enter your development API key (Find yours ➡️ ${instanceUrl})`,
|
||||
validate: (input) => {
|
||||
// Make sure they enter something like tr_dev_********
|
||||
if (!input) {
|
||||
@@ -146,6 +196,43 @@ const promptEndpointSlug = async (): Promise<string> => {
|
||||
return endpointSlug;
|
||||
};
|
||||
|
||||
const promptEndpointUrl = async (instanceUrl: string): Promise<string> => {
|
||||
const { endpointUrl } = await inquirer.prompt<{
|
||||
endpointUrl: string;
|
||||
}>({
|
||||
type: "input",
|
||||
name: "endpointUrl",
|
||||
message:
|
||||
"What's the URL of your Next.js project? (e.g. http://localhost:3000)",
|
||||
validate: (input) => {
|
||||
if (!input) {
|
||||
return "Please enter the URL of your Next.js project";
|
||||
}
|
||||
|
||||
// If instanceUrl is a cloud instance, then the URL must be publicly accessible
|
||||
const url = new URL(input);
|
||||
const triggerUrl = new URL(instanceUrl);
|
||||
|
||||
if (triggerUrl.hostname !== "localhost" && url.hostname === "localhost") {
|
||||
return `Your Trigger.dev instance is hosted at ${triggerUrl.hostname}, so your Next.js project must also be publicly accessible. See our docs for more info: https://trigger.dev/docs/guides/tunneling`;
|
||||
}
|
||||
|
||||
// Make sure triggerUrl and url don't use the same port if they are both localhost
|
||||
if (
|
||||
triggerUrl.hostname === "localhost" &&
|
||||
url.hostname === "localhost" &&
|
||||
triggerUrl.port === url.port
|
||||
) {
|
||||
return `Your Trigger.dev instance and your Next.js project are both trying to use port ${triggerUrl.port}. Please use a different port for one of them`;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
return endpointUrl;
|
||||
};
|
||||
|
||||
export const obfuscateApiKey = (apiKey: string) => {
|
||||
const [prefix, slug, secretPart] = apiKey.split("_") as [
|
||||
string,
|
||||
|
||||
@@ -18,5 +18,4 @@ export const TITLE_TEXT = `
|
||||
export const DEFAULT_APP_NAME = "my-triggers";
|
||||
export const COMMAND_NAME = "@trigger.dev/init";
|
||||
export const TEMPLATE_ORGANIZATION = "triggerdotdev";
|
||||
export const TRIGGER_BASE_URL =
|
||||
process.env.TRIGGER_BASE_URL ?? "https://cloud.trigger.dev";
|
||||
export const DEFAULT_TRIGGER_URL = "https://cloud.trigger.dev";
|
||||
|
||||
@@ -9,12 +9,22 @@ import fs from "fs/promises";
|
||||
import pathModule from "path";
|
||||
import { simpleGit } from "simple-git";
|
||||
import { TriggerApi } from "./utils/triggerApi.js";
|
||||
import { DEFAULT_TRIGGER_URL } from "./consts.js";
|
||||
import ora from "ora";
|
||||
|
||||
const main = async () => {
|
||||
renderTitle();
|
||||
|
||||
const cliOptions = await parseCliOptions();
|
||||
|
||||
if (cliOptions.flags.triggerUrl === DEFAULT_TRIGGER_URL) {
|
||||
logger.info(`✨ Initializing project in Trigger.dev Cloud`);
|
||||
} else {
|
||||
logger.info(
|
||||
`✨ Initializing project using Trigger.dev at ${cliOptions.flags.triggerUrl}`
|
||||
);
|
||||
}
|
||||
|
||||
const resolvedPath = resolvePath(cliOptions.flags.projectPath);
|
||||
// Detect if are are in a Next.js project
|
||||
const isNextJsProject = await detectNextJsProject(resolvedPath);
|
||||
@@ -61,12 +71,7 @@ const main = async () => {
|
||||
]);
|
||||
|
||||
// Setup environment variables
|
||||
const addedEnvVars = await setupEnvironmentVariables(
|
||||
resolvedPath,
|
||||
cliResults
|
||||
);
|
||||
|
||||
logger.success(`✅ Setup environment variables ${addedEnvVars.join(", ")}`);
|
||||
await setupEnvironmentVariables(resolvedPath, cliResults);
|
||||
|
||||
const usesSrcDir = await detectUseOfSrcDir(resolvedPath);
|
||||
|
||||
@@ -84,6 +89,8 @@ const main = async () => {
|
||||
await createTriggerAppRoute(routeDir, cliResults, usesSrcDir);
|
||||
}
|
||||
|
||||
await waitForProjectToBuild();
|
||||
|
||||
const api = new TriggerApi(apiKey, cliResults.flags.triggerUrl);
|
||||
|
||||
const endpoint = await api.createEndpoint({
|
||||
@@ -305,14 +312,7 @@ export const { POST, dynamic } = createAppRoute(client, {
|
||||
);
|
||||
}
|
||||
|
||||
type EnvironmentVariable = "TRIGGER_API_KEY" | "TRIGGER_API_URL" | "VERCEL_URL";
|
||||
|
||||
async function setupEnvironmentVariables(
|
||||
path: string,
|
||||
cliResults: CliResults
|
||||
): Promise<Array<EnvironmentVariable>> {
|
||||
let results: Array<EnvironmentVariable> = [];
|
||||
|
||||
async function setupEnvironmentVariables(path: string, cliResults: CliResults) {
|
||||
const envFilePath = pathModule.join(path, ".env.local");
|
||||
const envFileExists = await pathExists(envFilePath);
|
||||
|
||||
@@ -327,13 +327,15 @@ async function setupEnvironmentVariables(
|
||||
);
|
||||
|
||||
await fs.writeFile(envFilePath, updatedEnvFileContent);
|
||||
results.push("TRIGGER_API_KEY");
|
||||
|
||||
logger.success("✅ Updated TRIGGER_API_KEY in .env.local");
|
||||
} else {
|
||||
await fs.appendFile(
|
||||
envFilePath,
|
||||
`TRIGGER_API_KEY=${cliResults.flags.apiKey}\n`
|
||||
);
|
||||
results.push("TRIGGER_API_KEY");
|
||||
|
||||
logger.success("✅ Added TRIGGER_API_KEY to .env.local");
|
||||
}
|
||||
|
||||
if (envFileContent.includes("TRIGGER_API_URL")) {
|
||||
@@ -344,13 +346,15 @@ async function setupEnvironmentVariables(
|
||||
);
|
||||
|
||||
await fs.writeFile(envFilePath, updatedEnvFileContent);
|
||||
results.push("TRIGGER_API_URL");
|
||||
|
||||
logger.success("✅ Updated TRIGGER_API_URL in .env.local");
|
||||
} else {
|
||||
await fs.appendFile(
|
||||
envFilePath,
|
||||
`TRIGGER_API_URL=${cliResults.flags.triggerUrl}\n`
|
||||
);
|
||||
results.push("TRIGGER_API_URL");
|
||||
|
||||
logger.success("✅ Added TRIGGER_API_URL to .env.local");
|
||||
}
|
||||
|
||||
if (!envFileContent.includes("VERCEL_URL")) {
|
||||
@@ -358,7 +362,8 @@ async function setupEnvironmentVariables(
|
||||
envFilePath,
|
||||
`VERCEL_URL=${cliResults.flags.endpointUrl}\n`
|
||||
);
|
||||
results.push("VERCEL_URL");
|
||||
|
||||
logger.success("✅ Added VERCEL_URL to .env.local");
|
||||
}
|
||||
} else {
|
||||
const envFileContent = `
|
||||
@@ -368,10 +373,11 @@ VERCEL_URL=${cliResults.flags.endpointUrl}
|
||||
`;
|
||||
|
||||
await fs.writeFile(envFilePath, envFileContent);
|
||||
results = ["TRIGGER_API_KEY", "TRIGGER_API_URL", "VERCEL_URL"];
|
||||
}
|
||||
|
||||
return results;
|
||||
logger.success(
|
||||
"✅ Created .env.local and added required environment variables"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function pathExists(path: string): Promise<boolean> {
|
||||
@@ -382,3 +388,13 @@ async function pathExists(path: string): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForProjectToBuild() {
|
||||
const spinner = ora("Waiting for project to build...").start();
|
||||
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 1000);
|
||||
});
|
||||
|
||||
spinner.stop();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user