aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages/create-turbo/src/utils/examples.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/utils/examples.ts
parent0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff)
downloadHydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz
HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip
Diffstat (limited to 'packages/create-turbo/src/utils/examples.ts')
-rw-r--r--packages/create-turbo/src/utils/examples.ts139
1 files changed, 139 insertions, 0 deletions
diff --git a/packages/create-turbo/src/utils/examples.ts b/packages/create-turbo/src/utils/examples.ts
new file mode 100644
index 0000000..b7c4812
--- /dev/null
+++ b/packages/create-turbo/src/utils/examples.ts
@@ -0,0 +1,139 @@
+import got from "got";
+import tar from "tar";
+import { Stream } from "stream";
+import { promisify } from "util";
+import { join } from "path";
+import { tmpdir } from "os";
+import { createWriteStream, promises as fs } from "fs";
+
+const pipeline = promisify(Stream.pipeline);
+
+export type RepoInfo = {
+ username: string;
+ name: string;
+ branch: string;
+ filePath: string;
+};
+
+export async function isUrlOk(url: string): Promise<boolean> {
+ try {
+ const res = await got.head(url);
+ return res.statusCode === 200;
+ } catch (err) {
+ return false;
+ }
+}
+
+export async function getRepoInfo(
+ url: URL,
+ examplePath?: string
+): Promise<RepoInfo | undefined> {
+ const [, username, name, tree, sourceBranch, ...file] =
+ url.pathname.split("/");
+ const filePath = examplePath
+ ? examplePath.replace(/^\//, "")
+ : file.join("/");
+
+ if (
+ // Support repos whose entire purpose is to be a Turborepo example, e.g.
+ // https://github.com/:username/:my-cool-turborepo-example-repo-name.
+ tree === undefined ||
+ // Support GitHub URL that ends with a trailing slash, e.g.
+ // https://github.com/:username/:my-cool-turborepo-example-repo-name/
+ // In this case "t" will be an empty string while the turbo part "_branch" will be undefined
+ (tree === "" && sourceBranch === undefined)
+ ) {
+ try {
+ const infoResponse = await got(
+ `https://api.github.com/repos/${username}/${name}`
+ );
+ const info = JSON.parse(infoResponse.body);
+ return { username, name, branch: info["default_branch"], filePath };
+ } catch (err) {
+ return;
+ }
+ }
+
+ // If examplePath is available, the branch name takes the entire path
+ const branch = examplePath
+ ? `${sourceBranch}/${file.join("/")}`.replace(
+ new RegExp(`/${filePath}|/$`),
+ ""
+ )
+ : sourceBranch;
+
+ if (username && name && branch && tree === "tree") {
+ return { username, name, branch, filePath };
+ }
+}
+
+export function hasRepo({
+ username,
+ name,
+ branch,
+ filePath,
+}: RepoInfo): Promise<boolean> {
+ const contentsUrl = `https://api.github.com/repos/${username}/${name}/contents`;
+ const packagePath = `${filePath ? `/${filePath}` : ""}/package.json`;
+
+ return isUrlOk(contentsUrl + packagePath + `?ref=${branch}`);
+}
+
+export function existsInRepo(nameOrUrl: string): Promise<boolean> {
+ try {
+ const url = new URL(nameOrUrl);
+ return isUrlOk(url.href);
+ } catch {
+ return isUrlOk(
+ `https://api.github.com/repos/vercel/turbo/contents/examples/${encodeURIComponent(
+ nameOrUrl
+ )}`
+ );
+ }
+}
+
+async function downloadTar(url: string, name: string) {
+ const tempFile = join(tmpdir(), `${name}.temp-${Date.now()}`);
+ await pipeline(got.stream(url), createWriteStream(tempFile));
+ return tempFile;
+}
+
+export async function downloadAndExtractRepo(
+ root: string,
+ { username, name, branch, filePath }: RepoInfo
+) {
+ const tempFile = await downloadTar(
+ `https://codeload.github.com/${username}/${name}/tar.gz/${branch}`,
+ `turbo-ct-example`
+ );
+
+ await tar.x({
+ file: tempFile,
+ cwd: root,
+ strip: filePath ? filePath.split("/").length + 1 : 1,
+ filter: (p: string) =>
+ p.startsWith(
+ `${name}-${branch.replace(/\//g, "-")}${
+ filePath ? `/${filePath}/` : "/"
+ }`
+ ),
+ });
+
+ await fs.unlink(tempFile);
+}
+
+export async function downloadAndExtractExample(root: string, name: string) {
+ const tempFile = await downloadTar(
+ `https://codeload.github.com/vercel/turbo/tar.gz/main`,
+ `turbo-ct-example`
+ );
+
+ await tar.x({
+ file: tempFile,
+ cwd: root,
+ strip: 2 + name.split("/").length,
+ filter: (p: string) => p.includes(`turbo-main/examples/${name}/`),
+ });
+
+ await fs.unlink(tempFile);
+}