aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages/create-turbo/src/commands/create/createProject.ts
diff options
context:
space:
mode:
author简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:44 +0800
committer简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:44 +0800
commitdd84b9d64fb98746a230cd24233ff50a562c39c9 (patch)
treeb583261ef00b3afe72ec4d6dacb31e57779a6faf /packages/create-turbo/src/commands/create/createProject.ts
parent0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff)
downloadHydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz
HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip
Diffstat (limited to 'packages/create-turbo/src/commands/create/createProject.ts')
-rw-r--r--packages/create-turbo/src/commands/create/createProject.ts192
1 files changed, 192 insertions, 0 deletions
diff --git a/packages/create-turbo/src/commands/create/createProject.ts b/packages/create-turbo/src/commands/create/createProject.ts
new file mode 100644
index 0000000..0c1d2ac
--- /dev/null
+++ b/packages/create-turbo/src/commands/create/createProject.ts
@@ -0,0 +1,192 @@
+import retry from "async-retry";
+import chalk from "chalk";
+import fs from "fs-extra";
+import path from "path";
+
+import {
+ downloadAndExtractExample,
+ downloadAndExtractRepo,
+ getRepoInfo,
+ existsInRepo,
+ hasRepo,
+ RepoInfo,
+} from "../../utils/examples";
+import { isFolderEmpty } from "../../utils/isFolderEmpty";
+import { isWriteable } from "../../utils/isWriteable";
+import { turboLoader, error } from "../../logger";
+import { isDefaultExample } from "../../utils/isDefaultExample";
+
+export class DownloadError extends Error {}
+
+export async function createProject({
+ appPath,
+ example,
+ examplePath,
+}: {
+ appPath: string;
+ example: string;
+ examplePath?: string;
+}): Promise<{
+ cdPath: string;
+ hasPackageJson: boolean;
+ availableScripts: Array<string>;
+ repoInfo?: RepoInfo;
+}> {
+ let repoInfo: RepoInfo | undefined;
+ let repoUrl: URL | undefined;
+ const defaultExample = isDefaultExample(example);
+
+ try {
+ repoUrl = new URL(example);
+ } catch (err: any) {
+ if (err.code !== "ERR_INVALID_URL") {
+ error(err);
+ process.exit(1);
+ }
+ }
+
+ if (repoUrl) {
+ if (repoUrl.origin !== "https://github.com") {
+ error(
+ `Invalid URL: ${chalk.red(
+ `"${example}"`
+ )}. Only GitHub repositories are supported. Please use a GitHub URL and try again.`
+ );
+ process.exit(1);
+ }
+
+ repoInfo = await getRepoInfo(repoUrl, examplePath);
+
+ if (!repoInfo) {
+ error(
+ `Unable to fetch repository information from: ${chalk.red(
+ `"${example}"`
+ )}. Please fix the URL and try again.`
+ );
+ process.exit(1);
+ }
+
+ const found = await hasRepo(repoInfo);
+
+ if (!found) {
+ error(
+ `Could not locate the repository for ${chalk.red(
+ `"${example}"`
+ )}. Please check that the repository exists and try again.`
+ );
+ process.exit(1);
+ }
+ } else {
+ const found = await existsInRepo(example);
+
+ if (!found) {
+ error(
+ `Could not locate an example named ${chalk.red(
+ `"${example}"`
+ )}. It could be due to the following:\n`,
+ `1. Your spelling of example ${chalk.red(
+ `"${example}"`
+ )} might be incorrect.\n`,
+ `2. You might not be connected to the internet or you are behind a proxy.`
+ );
+ process.exit(1);
+ }
+ }
+
+ const root = path.resolve(appPath);
+
+ if (!(await isWriteable(path.dirname(root)))) {
+ error(
+ "The application path is not writable, please check folder permissions and try again."
+ );
+ error("It is likely you do not have write permissions for this folder.");
+ process.exit(1);
+ }
+
+ const appName = path.basename(root);
+ try {
+ await fs.mkdir(root, { recursive: true });
+ } catch (err) {
+ error("Unable to create project directory");
+ console.error(err);
+ process.exit(1);
+ }
+ const { isEmpty, conflicts } = isFolderEmpty(root);
+ if (!isEmpty) {
+ error(
+ `${chalk.dim(root)} has ${conflicts.length} conflicting ${
+ conflicts.length === 1 ? "file" : "files"
+ } - please try a different location`
+ );
+ process.exit(1);
+ }
+
+ const originalDirectory = process.cwd();
+ process.chdir(root);
+
+ /**
+ * clone the example repository
+ */
+ const loader = turboLoader("Downloading files...");
+ try {
+ if (repoInfo) {
+ console.log(
+ `\nDownloading files from repo ${chalk.cyan(
+ example
+ )}. This might take a moment.`
+ );
+ console.log();
+ loader.start();
+ await retry(() => downloadAndExtractRepo(root, repoInfo as RepoInfo), {
+ retries: 3,
+ });
+ } else {
+ console.log(
+ `\nDownloading files${
+ !defaultExample ? ` for example ${chalk.cyan(example)}` : ""
+ }. This might take a moment.`
+ );
+ console.log();
+ loader.start();
+ await retry(() => downloadAndExtractExample(root, example), {
+ retries: 3,
+ });
+ }
+ } catch (reason) {
+ function isErrorLike(err: unknown): err is { message: string } {
+ return (
+ typeof err === "object" &&
+ err !== null &&
+ typeof (err as { message?: unknown }).message === "string"
+ );
+ }
+ throw new DownloadError(isErrorLike(reason) ? reason.message : reason + "");
+ } finally {
+ loader.stop();
+ }
+
+ const rootPackageJsonPath = path.join(root, "package.json");
+ const hasPackageJson = fs.existsSync(rootPackageJsonPath);
+ const availableScripts = [];
+
+ if (hasPackageJson) {
+ let packageJsonContent;
+ try {
+ packageJsonContent = fs.readJsonSync(rootPackageJsonPath);
+ } catch {
+ // ignore
+ }
+
+ if (packageJsonContent) {
+ // read the scripts from the package.json
+ availableScripts.push(...Object.keys(packageJsonContent.scripts || {}));
+ }
+ }
+
+ let cdPath: string = appPath;
+ if (path.join(originalDirectory, appName) === appPath) {
+ cdPath = appName;
+ }
+
+ return { cdPath, hasPackageJson, availableScripts, repoInfo };
+}