diff options
| author | 2023-11-03 21:25:40 +0800 | |
|---|---|---|
| committer | 2023-11-03 21:25:40 +0800 | |
| commit | 9029588590bea8b10451575c5142dcde77ecd1b5 (patch) | |
| tree | 04cf8aee56c23fd225ff19d340f7cee621d874ef /packages/create-turbo | |
| parent | 94071d7ce16c56641d67d488e2bac6be84ffe731 (diff) | |
| download | HydroRoll-9029588590bea8b10451575c5142dcde77ecd1b5.tar.gz HydroRoll-9029588590bea8b10451575c5142dcde77ecd1b5.zip | |
chore: delete useless files
Diffstat (limited to 'packages/create-turbo')
34 files changed, 0 insertions, 2261 deletions
diff --git a/packages/create-turbo/.gitignore b/packages/create-turbo/.gitignore deleted file mode 100644 index 47f732d..0000000 --- a/packages/create-turbo/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!templates/*/.npmrc diff --git a/packages/create-turbo/LICENSE b/packages/create-turbo/LICENSE deleted file mode 100644 index fa0086a..0000000 --- a/packages/create-turbo/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0.
\ No newline at end of file diff --git a/packages/create-turbo/README.md b/packages/create-turbo/README.md deleted file mode 100644 index 485485f..0000000 --- a/packages/create-turbo/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Welcome to Turborepo - -[Turborepo](https://turbo.build/repo) is a high-performance monorepo build-system for modern JavaScript and TypeScript codebases. - -To get started, open a new shell and run: - -```sh -npx create-turbo@latest -``` - -Then follow the prompts you see in your terminal. - -For more information about Turborepo, [visit turbo.build/repo](https://turbo.build/repo) and follow us on Twitter ([@turborepo](https://twitter.com/turborepo))! diff --git a/packages/create-turbo/__tests__/examples.test.ts b/packages/create-turbo/__tests__/examples.test.ts deleted file mode 100644 index 20d4464..0000000 --- a/packages/create-turbo/__tests__/examples.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -import got from "got"; -import * as Got from "got"; -import { isUrlOk, getRepoInfo, hasRepo } from "../src/utils/examples"; - -jest.mock("got", () => ({ - __esModule: true, - ...jest.requireActual("got"), -})); - -describe("examples", () => { - describe("isUrlOk", () => { - it("returns true if url returns 200", async () => { - const mockGot = jest - .spyOn(got, "head") - .mockReturnValue({ statusCode: 200 } as any); - - const url = "https://github.com/vercel/turbo/"; - const result = await isUrlOk(url); - expect(result).toBe(true); - - expect(mockGot).toHaveBeenCalledWith(url); - mockGot.mockRestore(); - }); - - it("returns false if url returns status != 200", async () => { - const mockGot = jest - .spyOn(got, "head") - .mockReturnValue({ statusCode: 401 } as any); - - const url = "https://not-github.com/vercel/turbo/"; - const result = await isUrlOk(url); - expect(result).toBe(false); - - expect(mockGot).toHaveBeenCalledWith(url); - mockGot.mockRestore(); - }); - }); - - describe("getRepoInfo", () => { - test.each([ - { - repoUrl: "https://github.com/vercel/turbo/", - examplePath: undefined, - defaultBranch: "main", - expectBranchLookup: true, - expected: { - username: "vercel", - name: "turbo", - branch: "main", - filePath: "", - }, - }, - { - repoUrl: - "https://github.com/vercel/turbo/tree/canary/examples/kitchen-sink", - examplePath: undefined, - defaultBranch: "canary", - expectBranchLookup: false, - expected: { - username: "vercel", - name: "turbo", - branch: "canary", - filePath: "examples/kitchen-sink", - }, - }, - { - repoUrl: "https://github.com/vercel/turbo/tree/tek/test-branch/", - examplePath: "examples/basic", - defaultBranch: "canary", - expectBranchLookup: false, - expected: { - username: "vercel", - name: "turbo", - branch: "tek/test-branch", - filePath: "examples/basic", - }, - }, - ])( - "retrieves repo info for $repoUrl and $examplePath", - async ({ - repoUrl, - examplePath, - defaultBranch, - expectBranchLookup, - expected, - }) => { - const mockGot = jest.spyOn(Got, "default").mockReturnValue({ - body: JSON.stringify({ default_branch: defaultBranch }), - } as any); - - const url = new URL(repoUrl); - const result = await getRepoInfo(url, examplePath); - expect(result).toMatchObject(expected); - - if (result && expectBranchLookup) { - expect(mockGot).toHaveBeenCalledWith( - `https://api.github.com/repos/${result.username}/${result.name}` - ); - } - - mockGot.mockRestore(); - } - ); - }); - - describe("hasRepo", () => { - test.each([ - { - repoInfo: { - username: "vercel", - name: "turbo", - branch: "main", - filePath: "", - }, - expected: true, - expectedUrl: - "https://api.github.com/repos/vercel/turbo/contents/package.json?ref=main", - }, - ])( - "checks repo at $expectedUrl", - async ({ expected, repoInfo, expectedUrl }) => { - const mockGot = jest - .spyOn(got, "head") - .mockReturnValue({ statusCode: 200 } as any); - - const result = await hasRepo(repoInfo); - expect(result).toBe(expected); - - expect(mockGot).toHaveBeenCalledWith(expectedUrl); - mockGot.mockRestore(); - } - ); - }); -}); diff --git a/packages/create-turbo/__tests__/git.test.ts b/packages/create-turbo/__tests__/git.test.ts deleted file mode 100644 index 27ac118..0000000 --- a/packages/create-turbo/__tests__/git.test.ts +++ /dev/null @@ -1,239 +0,0 @@ -import path from "path"; -import { - DEFAULT_IGNORE, - GIT_REPO_COMMAND, - HG_REPO_COMMAND, - isInGitRepository, - isInMercurialRepository, - tryGitInit, -} from "../src/utils/git"; -import childProcess from "child_process"; -import { setupTestFixtures } from "@turbo/test-utils"; - -describe("git", () => { - // just to make sure this doesn't get lost - it("default .gitignore includes .turbo", async () => { - expect(DEFAULT_IGNORE).toContain(".turbo"); - }); - - describe("isInGitRepository", () => { - it("returns true when in a repo", async () => { - const mockExecSync = jest - .spyOn(childProcess, "execSync") - .mockReturnValue("true"); - - const result = isInGitRepository(); - expect(result).toBe(true); - - expect(mockExecSync).toHaveBeenCalledWith(GIT_REPO_COMMAND, { - stdio: "ignore", - }); - mockExecSync.mockRestore(); - }); - - it("returns false when not in a repo", async () => { - const mockExecSync = jest - .spyOn(childProcess, "execSync") - .mockImplementation(() => { - throw new Error( - "fatal: not a git repository (or any of the parent directories): .git" - ); - }); - - const result = isInGitRepository(); - expect(result).toBe(false); - - expect(mockExecSync).toHaveBeenCalledWith(GIT_REPO_COMMAND, { - stdio: "ignore", - }); - mockExecSync.mockRestore(); - }); - - it("returns false on error", async () => { - const mockExecSync = jest - .spyOn(childProcess, "execSync") - .mockImplementation(() => { - throw new Error("unknown error"); - }); - - const result = isInGitRepository(); - expect(result).toBe(false); - - expect(mockExecSync).toHaveBeenCalledWith(GIT_REPO_COMMAND, { - stdio: "ignore", - }); - mockExecSync.mockRestore(); - }); - }); - - describe("isInMercurialRepository", () => { - it("returns true when in a repo", async () => { - const mockExecSync = jest - .spyOn(childProcess, "execSync") - .mockReturnValue("true"); - - const result = isInMercurialRepository(); - expect(result).toBe(true); - - expect(mockExecSync).toHaveBeenCalledWith(HG_REPO_COMMAND, { - stdio: "ignore", - }); - mockExecSync.mockRestore(); - }); - - it("returns false when not in a repo", async () => { - const mockExecSync = jest - .spyOn(childProcess, "execSync") - .mockImplementation(() => { - throw new Error("abort: no repository found (.hg not found)"); - }); - - const result = isInMercurialRepository(); - expect(result).toBe(false); - - expect(mockExecSync).toHaveBeenCalledWith(HG_REPO_COMMAND, { - stdio: "ignore", - }); - mockExecSync.mockRestore(); - }); - - it("returns false on error", async () => { - const mockExecSync = jest - .spyOn(childProcess, "execSync") - .mockImplementation(() => { - throw new Error("unknown error"); - }); - - const result = isInMercurialRepository(); - expect(result).toBe(false); - - expect(mockExecSync).toHaveBeenCalledWith(HG_REPO_COMMAND, { - stdio: "ignore", - }); - mockExecSync.mockRestore(); - }); - }); - - describe("tryGitInit", () => { - const { useFixture } = setupTestFixtures({ - directory: path.join(__dirname, "../"), - }); - - it("inits a repo succesfully", async () => { - const { root } = useFixture({ fixture: `git` }); - const mockExecSync = jest - .spyOn(childProcess, "execSync") - .mockReturnValueOnce("git version 2.38.1") - .mockImplementationOnce(() => { - throw new Error( - "fatal: not a git repository (or any of the parent directories): .git" - ); - }) - .mockImplementationOnce(() => { - throw new Error("abort: no repository found (.hg not found)"); - }) - .mockReturnValue("success"); - - const result = tryGitInit(root, "test commit"); - expect(result).toBe(true); - - const calls = [ - "git --version", - "git init", - "git checkout -b main", - "git add -A", - 'git commit -m "test commit"', - ]; - expect(mockExecSync).toHaveBeenCalledTimes(calls.length + 2); - calls.forEach((call) => { - expect(mockExecSync).toHaveBeenCalledWith(call, { - stdio: "ignore", - }); - }); - mockExecSync.mockRestore(); - }); - - it("skips init if already in a repo", async () => { - const { root } = useFixture({ fixture: `git` }); - const mockExecSync = jest - .spyOn(childProcess, "execSync") - .mockReturnValueOnce("git version 2.38.1") - .mockReturnValueOnce("true") - .mockReturnValue("success"); - - const result = tryGitInit(root, "test commit"); - expect(result).toBe(false); - - const calls = ["git --version"]; - - // 1 call for git --version, 1 call for isInGitRepository - expect(mockExecSync).toHaveBeenCalledTimes(calls.length + 1); - calls.forEach((call) => { - expect(mockExecSync).toHaveBeenCalledWith(call, { - stdio: "ignore", - }); - }); - mockExecSync.mockRestore(); - }); - - it("returns false on unexpected error", async () => { - const { root } = useFixture({ fixture: `git` }); - const mockExecSync = jest - .spyOn(childProcess, "execSync") - .mockImplementationOnce(() => { - throw new Error("fatal: unknown command git"); - }); - - const result = tryGitInit(root, "test commit"); - expect(result).toBe(false); - - const calls = ["git --version"]; - - expect(mockExecSync).toHaveBeenCalledTimes(calls.length); - calls.forEach((call) => { - expect(mockExecSync).toHaveBeenCalledWith(call, { - stdio: "ignore", - }); - }); - mockExecSync.mockRestore(); - }); - - it("cleans up from partial init on failure", async () => { - const { root } = useFixture({ fixture: `git` }); - const mockExecSync = jest - .spyOn(childProcess, "execSync") - .mockReturnValueOnce("git version 2.38.1") - .mockImplementationOnce(() => { - throw new Error( - "fatal: not a git repository (or any of the parent directories): .git" - ); - }) - .mockImplementationOnce(() => { - throw new Error("abort: no repository found (.hg not found)"); - }) - .mockReturnValueOnce("success") - .mockReturnValueOnce("success") - .mockImplementationOnce(() => { - throw new Error("fatal: could not add files"); - }); - - const result = tryGitInit(root, "test commit"); - expect(result).toBe(false); - - const calls = [ - "git --version", - "git init", - "git checkout -b main", - "git add -A", - ]; - - expect(mockExecSync).toHaveBeenCalledTimes(calls.length + 2); - calls.forEach((call) => { - expect(mockExecSync).toHaveBeenCalledWith(call, { - stdio: "ignore", - }); - }); - mockExecSync.mockRestore(); - }); - }); -}); diff --git a/packages/create-turbo/__tests__/index.test.ts b/packages/create-turbo/__tests__/index.test.ts deleted file mode 100644 index 641b193..0000000 --- a/packages/create-turbo/__tests__/index.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -import path from "path"; -import chalk from "chalk"; -import childProcess from "child_process"; -import { setupTestFixtures, spyConsole } from "@turbo/test-utils"; -import { create } from "../src/commands/create"; -import type { CreateCommandArgument } from "../src/commands/create/types"; -import { turboGradient } from "../src/logger"; -import type { PackageManager } from "@turbo/workspaces"; - -// imports for mocks -import * as createProject from "../src/commands/create/createProject"; -import * as turboWorkspaces from "@turbo/workspaces"; -import { getWorkspaceDetailsMockReturnValue } from "./test-utils"; - -jest.mock("@turbo/workspaces", () => ({ - __esModule: true, - ...jest.requireActual("@turbo/workspaces"), -})); - -describe("create-turbo", () => { - const { useFixture } = setupTestFixtures({ - directory: path.join(__dirname, "../"), - }); - - const mockConsole = spyConsole(); - - test.each<{ packageManager: PackageManager }>([ - { packageManager: "yarn" }, - { packageManager: "npm" }, - { packageManager: "pnpm" }, - ])( - "outputs expected console messages when using $packageManager", - async ({ packageManager }) => { - const { root } = useFixture({ fixture: `create-turbo` }); - - const availableScripts = ["build", "test", "dev"]; - - const mockCreateProject = jest - .spyOn(createProject, "createProject") - .mockResolvedValue({ - cdPath: "", - hasPackageJson: true, - availableScripts, - }); - - const mockGetWorkspaceDetails = jest - .spyOn(turboWorkspaces, "getWorkspaceDetails") - .mockResolvedValue( - getWorkspaceDetailsMockReturnValue({ - root, - packageManager, - }) - ); - - const mockExecSync = jest - .spyOn(childProcess, "execSync") - .mockImplementation(() => { - return "success"; - }); - - await create( - root as CreateCommandArgument, - packageManager as CreateCommandArgument, - { - skipInstall: true, - example: "default", - } - ); - - const expected = `${chalk.bold( - turboGradient(">>> Success!") - )} Created a new Turborepo at "${path.relative(process.cwd(), root)}".`; - - expect(mockConsole.log).toHaveBeenCalledWith(expected); - expect(mockConsole.log).toHaveBeenCalledWith( - "Inside that directory, you can run several commands:" - ); - - availableScripts.forEach((script) => { - expect(mockConsole.log).toHaveBeenCalledWith( - chalk.cyan(` ${packageManager} run ${script}`) - ); - }); - - mockCreateProject.mockRestore(); - mockGetWorkspaceDetails.mockRestore(); - mockExecSync.mockRestore(); - } - ); -}); diff --git a/packages/create-turbo/__tests__/isFolderEmpty.test.ts b/packages/create-turbo/__tests__/isFolderEmpty.test.ts deleted file mode 100644 index 66b2310..0000000 --- a/packages/create-turbo/__tests__/isFolderEmpty.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import fs from "fs-extra"; -import path from "path"; -import { isFolderEmpty } from "../src/utils/isFolderEmpty"; -import { setupTestFixtures } from "@turbo/test-utils"; - -describe("isFolderEmpty", () => { - const { useFixture } = setupTestFixtures({ - directory: path.join(__dirname, "../"), - }); - - it("correctly identifies an empty directory", async () => { - const { root } = useFixture({ fixture: `is-folder-empty` }); - const result = isFolderEmpty(root); - expect(result.isEmpty).toEqual(true); - expect(result.conflicts).toEqual([]); - }); - - it("correctly identifies a directory with non-conflicting files", async () => { - const { root } = useFixture({ fixture: `is-folder-empty` }); - fs.writeFileSync(path.join(root, "LICENSE"), "MIT"); - const result = isFolderEmpty(root); - expect(result.isEmpty).toEqual(true); - expect(result.conflicts).toEqual([]); - }); - - it("correctly identifies a directory non-conflicting files (intelliJ)", async () => { - const { root } = useFixture({ fixture: `is-folder-empty` }); - fs.writeFileSync(path.join(root, "intellij-idea-config.iml"), "{}"); - const result = isFolderEmpty(root); - expect(result.isEmpty).toEqual(true); - expect(result.conflicts).toEqual([]); - }); - - it("correctly identifies a directory conflicting files", async () => { - const { root } = useFixture({ fixture: `is-folder-empty` }); - fs.writeFileSync(path.join(root, "README.md"), "my cool project"); - const result = isFolderEmpty(root); - expect(result.isEmpty).toEqual(false); - expect(result.conflicts).toEqual(["README.md"]); - }); -}); diff --git a/packages/create-turbo/__tests__/isWritable.test.ts b/packages/create-turbo/__tests__/isWritable.test.ts deleted file mode 100644 index b06670b..0000000 --- a/packages/create-turbo/__tests__/isWritable.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import path from "path"; -import { isWriteable } from "../src/utils/isWriteable"; -import { setupTestFixtures } from "@turbo/test-utils"; -import fs from "fs-extra"; - -describe("isWriteable", () => { - const { useFixture } = setupTestFixtures({ - directory: path.join(__dirname, "../"), - }); - - it("correctly identifies a writeable directory", async () => { - const { root } = useFixture({ fixture: `is-writeable` }); - const result = await isWriteable(root); - expect(result).toEqual(true); - }); - - it("correctly identifies a non-writeable directory", async () => { - const { root } = useFixture({ fixture: `is-writeable` }); - const result = await isWriteable(path.join(root, "does-not-exist")); - expect(result).toEqual(false); - }); - - it("returns false on unexpected failure", async () => { - const { root } = useFixture({ fixture: `is-writeable` }); - const mockFsAccess = jest - .spyOn(fs, "access") - .mockRejectedValue(new Error("unknown error")); - - const result = await isWriteable(root); - expect(result).toEqual(false); - expect(mockFsAccess).toHaveBeenCalledWith(root, fs.constants.W_OK); - - mockFsAccess.mockRestore(); - }); -}); diff --git a/packages/create-turbo/__tests__/test-utils.ts b/packages/create-turbo/__tests__/test-utils.ts deleted file mode 100644 index fa6c204..0000000 --- a/packages/create-turbo/__tests__/test-utils.ts +++ /dev/null @@ -1,34 +0,0 @@ -import path from "path"; -import { PackageManager } from "@turbo/workspaces"; - -export function getWorkspaceDetailsMockReturnValue({ - root, - packageManager = "npm", -}: { - root: string; - packageManager: PackageManager; -}) { - return { - name: "mock-project", - packageManager, - paths: { - root, - packageJson: path.join(root, "package.json"), - lockfile: path.join(root, "yarn.lock"), - nodeModules: path.join(root, "node_modules"), - }, - workspaceData: { - globs: ["packages/*"], - workspaces: [ - { - name: "packages/mock-package", - paths: { - root: path.join(root, "packages/mock-package"), - packageJson: path.join(root, "packages/mock-package/package.json"), - nodeModules: path.join(root, "packages/mock-package/node_modules"), - }, - }, - ], - }, - }; -} diff --git a/packages/create-turbo/jest.config.js b/packages/create-turbo/jest.config.js deleted file mode 100644 index b738f4b..0000000 --- a/packages/create-turbo/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ -module.exports = { - preset: "ts-jest/presets/js-with-ts", - testEnvironment: "node", - testPathIgnorePatterns: ["/__fixtures__/", "/__tests__/test-utils.ts"], - coveragePathIgnorePatterns: ["/__fixtures__/", "/__tests__/test-utils.ts"], - transformIgnorePatterns: ["/node_modules/(?!(ansi-regex)/)"], - modulePathIgnorePatterns: ["<rootDir>/node_modules", "<rootDir>/dist"], - collectCoverage: true, - verbose: true, -}; diff --git a/packages/create-turbo/package.json b/packages/create-turbo/package.json deleted file mode 100644 index 9f723ba..0000000 --- a/packages/create-turbo/package.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "create-turbo", - "version": "1.9.4-canary.2", - "description": "Create a new Turborepo", - "homepage": "https://turbo.build/repo", - "license": "MPL-2.0", - "repository": { - "type": "git", - "url": "https://github.com/vercel/turbo", - "directory": "packages/create-turbo" - }, - "bugs": { - "url": "https://github.com/vercel/turbo/issues" - }, - "bin": { - "create-turbo": "dist/cli.js" - }, - "scripts": { - "build": "tsup", - "test": "jest", - "lint": "eslint src/**/*.ts", - "check-types": "tsc --noEmit" - }, - "dependencies": { - "async-retry": "^1.3.3", - "chalk": "2.4.2", - "commander": "^10.0.0", - "cross-spawn": "^7.0.3", - "execa": "5.1.1", - "fs-extra": "^10.1.0", - "got": "^11.8.5", - "gradient-string": "^2.0.0", - "inquirer": "^8.0.0", - "ora": "4.1.1", - "rimraf": "^3.0.2", - "semver": "^7.3.8", - "tar": "6.1.13", - "update-check": "^1.5.4" - }, - "devDependencies": { - "@turbo/workspaces": "workspace:*", - "@types/async-retry": "^1.4.5", - "@types/cross-spawn": "^6.0.2", - "@types/fs-extra": "^9.0.13", - "@types/gradient-string": "^1.1.2", - "@types/inquirer": "^7.3.1", - "@types/jest": "^27.4.0", - "@types/node": "^16.11.12", - "@types/rimraf": "^3.0.2", - "@types/semver": "^7.3.9", - "@types/tar": "^6.1.4", - "eslint": "^7.23.0", - "jest": "^27.4.3", - "strip-ansi": "^6.0.1", - "ts-jest": "^27.1.1", - "@turbo/tsconfig": "workspace:*", - "tsup": "^5.10.3", - "@turbo/utils": "workspace:*", - "@turbo/test-utils": "workspace:*", - "typescript": "^4.5.5" - }, - "files": [ - "dist" - ] -} diff --git a/packages/create-turbo/src/cli.ts b/packages/create-turbo/src/cli.ts deleted file mode 100644 index 1290a13..0000000 --- a/packages/create-turbo/src/cli.ts +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env node - -import chalk from "chalk"; -import { Command } from "commander"; -import notifyUpdate from "./utils/notifyUpdate"; -import { turboGradient, error } from "./logger"; - -import { create } from "./commands"; -import cliPkg from "../package.json"; - -const createTurboCli = new Command(); - -// create -createTurboCli - .name(chalk.bold(turboGradient("create-turbo"))) - .description("Create a new Turborepo") - .usage(`${chalk.bold("<project-directory> <package-manager>")} [options]`) - .argument("[project-directory]") - .argument("[package-manager]") - .option( - "--skip-install", - "Do not run a package manager install after creating the project", - false - ) - .option( - "--skip-transforms", - "Do not run any code transformation after creating the project", - false - ) - .option( - "-e, --example [name]|[github-url]", - ` - An example to bootstrap the app with. You can use an example name - from the official Turborepo repo or a GitHub URL. The URL can use - any branch and/or subdirectory -` - ) - .option( - "-p, --example-path <path-to-example>", - ` - In a rare case, your GitHub URL might contain a branch name with - a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). - In this case, you must specify the path to the example separately: - --example-path foo/bar -` - ) - .version(cliPkg.version, "-v, --version", "output the current version") - .helpOption() - .action(create); - -createTurboCli - .parseAsync() - .then(notifyUpdate) - .catch(async (reason) => { - console.log(); - if (reason.command) { - error(`${chalk.bold(reason.command)} has failed.`); - } else { - error("Unexpected error. Please report it as a bug:"); - console.log(reason); - } - console.log(); - await notifyUpdate(); - process.exit(1); - }); diff --git a/packages/create-turbo/src/commands/create/createProject.ts b/packages/create-turbo/src/commands/create/createProject.ts deleted file mode 100644 index 0c1d2ac..0000000 --- a/packages/create-turbo/src/commands/create/createProject.ts +++ /dev/null @@ -1,192 +0,0 @@ -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 }; -} diff --git a/packages/create-turbo/src/commands/create/index.ts b/packages/create-turbo/src/commands/create/index.ts deleted file mode 100644 index 419328b..0000000 --- a/packages/create-turbo/src/commands/create/index.ts +++ /dev/null @@ -1,243 +0,0 @@ -import path from "path"; -import chalk from "chalk"; -import type { Project } from "@turbo/workspaces"; -import { - getWorkspaceDetails, - install, - getPackageManagerMeta, - ConvertError, -} from "@turbo/workspaces"; -import { getAvailablePackageManagers } from "@turbo/utils"; -import type { CreateCommandArgument, CreateCommandOptions } from "./types"; -import * as prompts from "./prompts"; -import { createProject } from "./createProject"; -import { tryGitCommit, tryGitInit } from "../../utils/git"; -import { isOnline } from "../../utils/isOnline"; -import { transforms } from "../../transforms"; -import { turboGradient, turboLoader, info, error, warn } from "../../logger"; -import { TransformError } from "../../transforms/errors"; - -function handleErrors(err: unknown) { - // handle errors from ../../transforms - if (err instanceof TransformError) { - error(chalk.bold(err.transform), chalk.red(err.message)); - if (err.fatal) { - process.exit(1); - } - // handle errors from @turbo/workspaces - } else if (err instanceof ConvertError && err.type !== "unknown") { - error(chalk.red(err.message)); - process.exit(1); - // handle unknown errors (no special handling, just re-throw to catch at root) - } else { - throw err; - } -} - -const SCRIPTS_TO_DISPLAY: Record<string, string> = { - build: "Build", - dev: "Develop", - test: "Test", - lint: "Lint", -}; - -export async function create( - directory: CreateCommandArgument, - packageManager: CreateCommandArgument, - opts: CreateCommandOptions -) { - const { skipInstall, skipTransforms } = opts; - console.log(chalk.bold(turboGradient(`\n>>> TURBOREPO\n`))); - info(`Welcome to Turborepo! Let's get you set up with a new codebase.`); - console.log(); - - const [online, availablePackageManagers] = await Promise.all([ - isOnline(), - getAvailablePackageManagers(), - ]); - - if (!online) { - error( - "You appear to be offline. Please check your network connection and try again." - ); - process.exit(1); - } - const { root, projectName } = await prompts.directory({ directory }); - const relativeProjectDir = path.relative(process.cwd(), root); - const projectDirIsCurrentDir = relativeProjectDir === ""; - - // selected package manager can be undefined if the user chooses to skip transforms - const selectedPackageManagerDetails = await prompts.packageManager({ - packageManager, - skipTransforms, - }); - - if (packageManager && opts.skipTransforms) { - warn( - "--skip-transforms conflicts with <package-manager>. The package manager argument will be ignored." - ); - } - - const { example, examplePath } = opts; - const exampleName = example && example !== "default" ? example : "basic"; - const { hasPackageJson, availableScripts, repoInfo } = await createProject({ - appPath: root, - example: exampleName, - examplePath, - }); - - // create a new git repo after creating the project - tryGitInit(root, `feat(create-turbo): create ${exampleName}`); - - // read the project after creating it to get details about workspaces, package manager, etc. - let project: Project = {} as Project; - try { - project = await getWorkspaceDetails({ root }); - } catch (err) { - handleErrors(err); - } - - // run any required transforms - if (!skipTransforms) { - for (const transform of transforms) { - try { - const transformResult = await transform({ - example: { - repo: repoInfo, - name: exampleName, - }, - project, - prompts: { - projectName, - root, - packageManager: selectedPackageManagerDetails, - }, - opts, - }); - if (transformResult.result === "success") { - tryGitCommit( - `feat(create-turbo): apply ${transformResult.name} transform` - ); - } - } catch (err) { - handleErrors(err); - } - } - } - - // if the user opted out of transforms, the package manager will be the same as the source example - const projectPackageManager = - skipTransforms || !selectedPackageManagerDetails - ? { - name: project.packageManager, - version: availablePackageManagers[project.packageManager].version, - } - : selectedPackageManagerDetails; - - info("Created a new Turborepo with the following:"); - console.log(); - if (project.workspaceData.workspaces.length > 0) { - const workspacesForDisplay = project.workspaceData.workspaces - .map((w) => ({ - group: path.relative(root, w.paths.root).split(path.sep)?.[0] || "", - title: path.relative(root, w.paths.root), - description: w.description, - })) - .sort((a, b) => a.title.localeCompare(b.title)); - - let lastGroup: string | undefined; - workspacesForDisplay.forEach(({ group, title, description }, idx) => { - if (idx === 0 || group !== lastGroup) { - console.log(chalk.cyan(group)); - } - console.log( - ` - ${chalk.bold(title)}${description ? `: ${description}` : ""}` - ); - lastGroup = group; - }); - } else { - console.log(chalk.cyan("apps")); - console.log(` - ${chalk.bold(projectName)}`); - } - - // run install - console.log(); - if (hasPackageJson && !skipInstall) { - // in the case when the user opted out of transforms, but not install, we need to make sure the package manager is available - // before we attempt an install - if ( - opts.skipTransforms && - !availablePackageManagers[project.packageManager].available - ) { - warn( - `Unable to install dependencies - "${exampleName}" uses "${project.packageManager}" which could not be found.` - ); - warn( - `Try running without "--skip-transforms" to convert "${exampleName}" to a package manager that is available on your system.` - ); - console.log(); - } else if (projectPackageManager) { - console.log("Installing packages. This might take a couple of minutes."); - console.log(); - - const loader = turboLoader("Installing dependencies...").start(); - await install({ - project, - to: projectPackageManager, - options: { - interactive: false, - }, - }); - - tryGitCommit("feat(create-turbo): install dependencies"); - loader.stop(); - } - } - - if (projectDirIsCurrentDir) { - console.log( - `${chalk.bold( - turboGradient(">>> Success!") - )} Your new Turborepo is ready.` - ); - } else { - console.log( - `${chalk.bold( - turboGradient(">>> Success!") - )} Created a new Turborepo at "${relativeProjectDir}".` - ); - } - - // get the package manager details so we display the right commands to the user in log messages - const packageManagerMeta = getPackageManagerMeta(projectPackageManager); - if (packageManagerMeta && hasPackageJson) { - console.log( - `Inside ${ - projectDirIsCurrentDir ? "this" : "that" - } directory, you can run several commands:` - ); - console.log(); - availableScripts - .filter((script) => SCRIPTS_TO_DISPLAY[script]) - .forEach((script) => { - console.log( - chalk.cyan(` ${packageManagerMeta.command} run ${script}`) - ); - console.log(` ${SCRIPTS_TO_DISPLAY[script]} all apps and packages`); - console.log(); - }); - console.log(`Turborepo will cache locally by default. For an additional`); - console.log(`speed boost, enable Remote Caching with Vercel by`); - console.log(`entering the following command:`); - console.log(); - console.log(chalk.cyan(` ${packageManagerMeta.executable} turbo login`)); - console.log(); - console.log(`We suggest that you begin by typing:`); - console.log(); - if (!projectDirIsCurrentDir) { - console.log(` ${chalk.cyan("cd")} ${relativeProjectDir}`); - } - console.log(chalk.cyan(` ${packageManagerMeta.executable} turbo login`)); - console.log(); - } -} diff --git a/packages/create-turbo/src/commands/create/prompts.ts b/packages/create-turbo/src/commands/create/prompts.ts deleted file mode 100644 index a5ed7bf..0000000 --- a/packages/create-turbo/src/commands/create/prompts.ts +++ /dev/null @@ -1,124 +0,0 @@ -import path from "path"; -import fs from "fs-extra"; -import chalk from "chalk"; -import type { PackageManager } from "@turbo/workspaces"; -import type { CreateCommandArgument } from "./types"; -import { getAvailablePackageManagers } from "@turbo/utils"; -import { isFolderEmpty } from "../../utils/isFolderEmpty"; -import inquirer from "inquirer"; - -function validateDirectory(directory: string): { - valid: boolean; - root: string; - projectName: string; - error?: string; -} { - const root = path.resolve(directory); - const projectName = path.basename(root); - const exists = fs.existsSync(root); - - const stat = fs.lstatSync(root, { throwIfNoEntry: false }); - if (stat && !stat.isDirectory()) { - return { - valid: false, - root, - projectName, - error: `${chalk.dim( - projectName - )} is not a directory - please try a different location`, - }; - } - - if (exists) { - const { isEmpty, conflicts } = isFolderEmpty(root); - if (!isEmpty) { - return { - valid: false, - root, - projectName, - error: `${chalk.dim(projectName)} has ${conflicts.length} conflicting ${ - conflicts.length === 1 ? "file" : "files" - } - please try a different location`, - }; - } - } - - return { valid: true, root, projectName }; -} - -export async function directory({ - directory, -}: { - directory: CreateCommandArgument; -}) { - const projectDirectoryAnswer = await inquirer.prompt<{ - projectDirectory: string; - }>({ - type: "input", - name: "projectDirectory", - message: "Where would you like to create your turborepo?", - when: !directory, - default: "./my-turborepo", - validate: (directory: string) => { - const { valid, error } = validateDirectory(directory); - if (!valid && error) { - return error; - } - return true; - }, - filter: (directory: string) => directory.trim(), - }); - - const { projectDirectory: selectedProjectDirectory = directory as string } = - projectDirectoryAnswer; - - return validateDirectory(selectedProjectDirectory); -} - -export async function packageManager({ - packageManager, - skipTransforms, -}: { - packageManager: CreateCommandArgument; - skipTransforms?: boolean; -}) { - // if skip transforms is passed, we don't need to ask about the package manager (because that requires a transform) - if (skipTransforms) { - return undefined; - } - - const availablePackageManagers = await getAvailablePackageManagers(); - const packageManagerAnswer = await inquirer.prompt<{ - packageManagerInput?: PackageManager; - }>({ - name: "packageManagerInput", - type: "list", - message: "Which package manager do you want to use?", - when: - // prompt for package manager if it wasn't provided as an argument, or if it was - // provided, but isn't available (always allow npm) - !packageManager || - (packageManager as PackageManager) !== "npm" || - !Object.keys(availablePackageManagers).includes(packageManager), - choices: ["npm", "pnpm", "yarn"].map((p) => ({ - name: p, - value: p, - disabled: - // npm should always be available - p === "npm" || - availablePackageManagers?.[p as PackageManager]?.available - ? false - : `not installed`, - })), - }); - - const { - packageManagerInput: - selectedPackageManager = packageManager as PackageManager, - } = packageManagerAnswer; - - return { - name: selectedPackageManager, - version: availablePackageManagers[selectedPackageManager].version, - }; -} diff --git a/packages/create-turbo/src/commands/create/types.ts b/packages/create-turbo/src/commands/create/types.ts deleted file mode 100644 index 094c8d2..0000000 --- a/packages/create-turbo/src/commands/create/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type CreateCommandArgument = "string" | undefined; - -export interface CreateCommandOptions { - skipInstall?: boolean; - skipTransforms?: boolean; - example?: string; - examplePath?: string; -} diff --git a/packages/create-turbo/src/commands/index.ts b/packages/create-turbo/src/commands/index.ts deleted file mode 100644 index 7c5f96b..0000000 --- a/packages/create-turbo/src/commands/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { create } from "./create"; diff --git a/packages/create-turbo/src/logger.ts b/packages/create-turbo/src/logger.ts deleted file mode 100644 index ee6d584..0000000 --- a/packages/create-turbo/src/logger.ts +++ /dev/null @@ -1,32 +0,0 @@ -import chalk from "chalk"; -import ora from "ora"; -import gradient from "gradient-string"; - -const BLUE = "#0099F7"; -const RED = "#F11712"; -const YELLOW = "#FFFF00"; - -export const turboGradient = gradient(BLUE, RED); -export const turboBlue = chalk.hex(BLUE); -export const turboRed = chalk.hex(RED); -export const yellow = chalk.hex(YELLOW); - -export const turboLoader = (text: string) => - ora({ - text, - spinner: { - frames: [" ", turboBlue("> "), turboBlue(">> "), turboBlue(">>>")], - }, - }); - -export const info = (...args: any[]) => { - console.log(turboBlue.bold(">>>"), ...args); -}; - -export const error = (...args: any[]) => { - console.error(turboRed.bold(">>>"), ...args); -}; - -export const warn = (...args: any[]) => { - console.error(yellow.bold(">>>"), ...args); -}; diff --git a/packages/create-turbo/src/transforms/errors.ts b/packages/create-turbo/src/transforms/errors.ts deleted file mode 100644 index a5b8a7a..0000000 --- a/packages/create-turbo/src/transforms/errors.ts +++ /dev/null @@ -1,17 +0,0 @@ -export type TransformErrorOptions = { - transform?: string; - fatal?: boolean; -}; - -export class TransformError extends Error { - public transform: string; - public fatal: boolean; - - constructor(message: string, opts?: TransformErrorOptions) { - super(message); - this.name = "TransformError"; - this.transform = opts?.transform ?? "unknown"; - this.fatal = opts?.fatal ?? true; - Error.captureStackTrace(this, TransformError); - } -} diff --git a/packages/create-turbo/src/transforms/git-ignore.ts b/packages/create-turbo/src/transforms/git-ignore.ts deleted file mode 100644 index bb61ca7..0000000 --- a/packages/create-turbo/src/transforms/git-ignore.ts +++ /dev/null @@ -1,30 +0,0 @@ -import path from "path"; -import fs from "fs-extra"; -import { DEFAULT_IGNORE } from "../utils/git"; -import { TransformInput, TransformResult } from "./types"; -import { TransformError } from "./errors"; - -const meta = { - name: "git-ignore", -}; - -export async function transform(args: TransformInput): TransformResult { - const { prompts } = args; - const ignorePath = path.join(prompts.root, ".gitignore"); - try { - if (!fs.existsSync(ignorePath)) { - fs.writeFileSync(ignorePath, DEFAULT_IGNORE); - } else { - return { result: "not-applicable", ...meta }; - } - } catch (err) { - // existsSync cannot throw, so we don't need to narrow here and can - // assume this came from writeFileSync - throw new TransformError("Unable to write .gitignore", { - transform: meta.name, - fatal: false, - }); - } - - return { result: "success", ...meta }; -} diff --git a/packages/create-turbo/src/transforms/index.ts b/packages/create-turbo/src/transforms/index.ts deleted file mode 100644 index 1918ecc..0000000 --- a/packages/create-turbo/src/transforms/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { transform as packageManagerTransform } from "./package-manager"; -import { transform as officialStarter } from "./official-starter"; -import { transform as gitIgnoreTransform } from "./git-ignore"; -import type { TransformInput, TransformResult } from "./types"; - -/** - * In the future, we may want to support sourcing additional transforms from the templates themselves. - */ -export const transforms: Array<(args: TransformInput) => TransformResult> = [ - officialStarter, - gitIgnoreTransform, - packageManagerTransform, -]; diff --git a/packages/create-turbo/src/transforms/official-starter.ts b/packages/create-turbo/src/transforms/official-starter.ts deleted file mode 100644 index 1d71909..0000000 --- a/packages/create-turbo/src/transforms/official-starter.ts +++ /dev/null @@ -1,73 +0,0 @@ -import path from "path"; -import fs from "fs-extra"; -import semverPrerelease from "semver/functions/prerelease"; -import cliPkgJson from "../../package.json"; -import { isDefaultExample } from "../utils/isDefaultExample"; -import { TransformInput, TransformResult } from "./types"; -import { TransformError } from "./errors"; - -const meta = { - name: "official-starter", -}; - -// applied to "official starter" examples (those hosted within vercel/turbo/examples) -export async function transform(args: TransformInput): TransformResult { - const { prompts, example } = args; - - const defaultExample = isDefaultExample(example.name); - const isOfficialStarter = - !example.repo || - (example.repo?.username === "vercel" && example.repo?.name === "turbo"); - - if (!isOfficialStarter) { - return { result: "not-applicable", ...meta }; - } - - // paths - const rootPackageJsonPath = path.join(prompts.root, "package.json"); - const rootMetaJsonPath = path.join(prompts.root, "meta.json"); - const hasPackageJson = fs.existsSync(rootPackageJsonPath); - - // 1. remove meta file (used for generating the examples page on turbo.build) - try { - fs.rmSync(rootMetaJsonPath, { force: true }); - } catch (_err) {} - - if (hasPackageJson) { - let packageJsonContent; - try { - packageJsonContent = fs.readJsonSync(rootPackageJsonPath); - } catch { - throw new TransformError("Unable to read package.json", { - transform: meta.name, - fatal: false, - }); - } - - // if using the basic example, set the name to the project name (legacy behavior) - if (packageJsonContent) { - if (defaultExample) { - packageJsonContent.name = prompts.projectName; - } - - // if we're using a pre-release version of create-turbo, install turbo canary instead of latest - const shouldUsePreRelease = semverPrerelease(cliPkgJson.version) !== null; - if (shouldUsePreRelease && packageJsonContent?.devDependencies?.turbo) { - packageJsonContent.devDependencies.turbo = "canary"; - } - - try { - fs.writeJsonSync(rootPackageJsonPath, packageJsonContent, { - spaces: 2, - }); - } catch (err) { - throw new TransformError("Unable to write package.json", { - transform: meta.name, - fatal: false, - }); - } - } - } - - return { result: "success", ...meta }; -} diff --git a/packages/create-turbo/src/transforms/package-manager.ts b/packages/create-turbo/src/transforms/package-manager.ts deleted file mode 100644 index 9c0af24..0000000 --- a/packages/create-turbo/src/transforms/package-manager.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { convert } from "@turbo/workspaces"; -import { TransformInput, TransformResult } from "./types"; - -const meta = { - name: "package-manager", -}; - -export async function transform(args: TransformInput): TransformResult { - const { project, prompts } = args; - const { root, packageManager } = prompts; - - if (packageManager && project.packageManager !== packageManager.name) { - await convert({ - root, - to: packageManager.name, - options: { - // skip install after conversion- we will do it later - skipInstall: true, - }, - }); - } else { - return { result: "not-applicable", ...meta }; - } - - return { result: "success", ...meta }; -} diff --git a/packages/create-turbo/src/transforms/types.ts b/packages/create-turbo/src/transforms/types.ts deleted file mode 100644 index 6a8e141..0000000 --- a/packages/create-turbo/src/transforms/types.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { CreateCommandOptions } from "../commands/create/types"; -import { RepoInfo } from "../utils/examples"; -import type { Project, PackageManager } from "@turbo/workspaces"; - -export interface TransformInput { - example: { - repo: RepoInfo | undefined; - name: string; - }; - project: Project; - prompts: { - projectName: string; - root: string; - packageManager: - | { - name: PackageManager; - version: string | undefined; - } - | undefined; - }; - opts: CreateCommandOptions; -} - -export interface TransformResponse { - // errors should be thrown as instances of TransformError - result: "not-applicable" | "success"; - name: string; -} - -export type TransformResult = Promise<TransformResponse>; diff --git a/packages/create-turbo/src/utils/examples.ts b/packages/create-turbo/src/utils/examples.ts deleted file mode 100644 index b7c4812..0000000 --- a/packages/create-turbo/src/utils/examples.ts +++ /dev/null @@ -1,139 +0,0 @@ -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); -} diff --git a/packages/create-turbo/src/utils/git.ts b/packages/create-turbo/src/utils/git.ts deleted file mode 100644 index 593e7ea..0000000 --- a/packages/create-turbo/src/utils/git.ts +++ /dev/null @@ -1,90 +0,0 @@ -import fs from "fs-extra"; -import { execSync } from "child_process"; -import path from "path"; -import rimraf from "rimraf"; - -export const DEFAULT_IGNORE = ` -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -node_modules -.pnp -.pnp.js - -# testing -coverage - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# turbo -.turbo - -# vercel -.vercel -`; - -export const GIT_REPO_COMMAND = "git rev-parse --is-inside-work-tree"; -export const HG_REPO_COMMAND = "hg --cwd . root"; - -export function isInGitRepository(): boolean { - try { - execSync(GIT_REPO_COMMAND, { stdio: "ignore" }); - return true; - } catch (_) {} - return false; -} - -export function isInMercurialRepository(): boolean { - try { - execSync(HG_REPO_COMMAND, { stdio: "ignore" }); - return true; - } catch (_) {} - return false; -} - -export function tryGitInit(root: string, message: string): boolean { - let didInit = false; - try { - execSync("git --version", { stdio: "ignore" }); - if (isInGitRepository() || isInMercurialRepository()) { - return false; - } - - execSync("git init", { stdio: "ignore" }); - didInit = true; - - execSync("git checkout -b main", { stdio: "ignore" }); - - execSync("git add -A", { stdio: "ignore" }); - execSync(`git commit -m "${message}"`, { - stdio: "ignore", - }); - return true; - } catch (err) { - if (didInit) { - try { - rimraf.sync(path.join(root, ".git")); - } catch (_) {} - } - return false; - } -} - -export function tryGitCommit(message: string): boolean { - try { - execSync("git add -A", { stdio: "ignore" }); - execSync(`git commit -m "${message}"`, { - stdio: "ignore", - }); - return true; - } catch (err) { - return false; - } -} diff --git a/packages/create-turbo/src/utils/isDefaultExample.ts b/packages/create-turbo/src/utils/isDefaultExample.ts deleted file mode 100644 index 9fb2ef2..0000000 --- a/packages/create-turbo/src/utils/isDefaultExample.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const DEFAULT_EXAMPLES = new Set(["basic", "default"]); - -export function isDefaultExample(example: string): boolean { - return DEFAULT_EXAMPLES.has(example); -} diff --git a/packages/create-turbo/src/utils/isFolderEmpty.ts b/packages/create-turbo/src/utils/isFolderEmpty.ts deleted file mode 100644 index 4de2d58..0000000 --- a/packages/create-turbo/src/utils/isFolderEmpty.ts +++ /dev/null @@ -1,37 +0,0 @@ -import fs from "fs-extra"; - -const VALID_FILES = [ - ".DS_Store", - ".git", - ".gitattributes", - ".gitignore", - ".gitlab-ci.yml", - ".hg", - ".hgcheck", - ".hgignore", - ".idea", - ".npmignore", - ".travis.yml", - "LICENSE", - "Thumbs.db", - "docs", - "mkdocs.yml", - "npm-debug.log", - "yarn-debug.log", - "yarn-error.log", - "yarnrc.yml", - ".yarn", -]; - -export function isFolderEmpty(root: string): { - isEmpty: boolean; - conflicts: Array<string>; -} { - const conflicts = fs - .readdirSync(root) - .filter((file) => !VALID_FILES.includes(file)) - // Support IntelliJ IDEA-based editors - .filter((file) => !/\.iml$/.test(file)); - - return { isEmpty: conflicts.length === 0, conflicts }; -} diff --git a/packages/create-turbo/src/utils/isOnline.ts b/packages/create-turbo/src/utils/isOnline.ts deleted file mode 100644 index f02b2e6..0000000 --- a/packages/create-turbo/src/utils/isOnline.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { execSync } from "child_process"; -import dns from "dns"; -import url from "url"; - -function getProxy(): string | undefined { - if (process.env.https_proxy) { - return process.env.https_proxy; - } - - try { - const httpsProxy = execSync("npm config get https-proxy").toString().trim(); - return httpsProxy !== "null" ? httpsProxy : undefined; - } catch (e) { - return; - } -} - -export function isOnline(): Promise<boolean> { - return new Promise((resolve) => { - dns.lookup("registry.yarnpkg.com", (registryErr) => { - if (!registryErr) { - return resolve(true); - } - - const proxy = getProxy(); - if (!proxy) { - return resolve(false); - } - - const { hostname } = url.parse(proxy); - if (!hostname) { - return resolve(false); - } - - dns.lookup(hostname, (proxyErr) => { - resolve(proxyErr == null); - }); - }); - }); -} diff --git a/packages/create-turbo/src/utils/isWriteable.ts b/packages/create-turbo/src/utils/isWriteable.ts deleted file mode 100644 index 132c42a..0000000 --- a/packages/create-turbo/src/utils/isWriteable.ts +++ /dev/null @@ -1,10 +0,0 @@ -import fs from "fs-extra"; - -export async function isWriteable(directory: string): Promise<boolean> { - try { - await fs.access(directory, (fs.constants || fs).W_OK); - return true; - } catch (err) { - return false; - } -} diff --git a/packages/create-turbo/src/utils/notifyUpdate.ts b/packages/create-turbo/src/utils/notifyUpdate.ts deleted file mode 100644 index e1dadc0..0000000 --- a/packages/create-turbo/src/utils/notifyUpdate.ts +++ /dev/null @@ -1,22 +0,0 @@ -import chalk from "chalk"; -import checkForUpdate from "update-check"; - -import cliPkgJson from "../../package.json"; - -const update = checkForUpdate(cliPkgJson).catch(() => null); - -export default async function notifyUpdate(): Promise<void> { - try { - const res = await update; - if (res?.latest) { - console.log(); - console.log( - chalk.yellow.bold("A new version of `create-turbo` is available!") - ); - console.log(); - } - process.exit(); - } catch (_e: any) { - // ignore error - } -} diff --git a/packages/create-turbo/tsconfig.json b/packages/create-turbo/tsconfig.json deleted file mode 100644 index abcb2c6..0000000 --- a/packages/create-turbo/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "@turbo/tsconfig/library.json", - "exclude": ["templates"], - "compilerOptions": { - "rootDir": "." - } -} diff --git a/packages/create-turbo/tsup.config.ts b/packages/create-turbo/tsup.config.ts deleted file mode 100644 index 18b0666..0000000 --- a/packages/create-turbo/tsup.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { defineConfig, Options } from "tsup"; - -export default defineConfig((options: Options) => ({ - entry: ["src/cli.ts"], - format: ["cjs"], - clean: true, - minify: true, - ...options, -})); diff --git a/packages/create-turbo/turbo.json b/packages/create-turbo/turbo.json deleted file mode 100644 index 6466b2d..0000000 --- a/packages/create-turbo/turbo.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "../../docs/public/schema.json", - "extends": ["//"], - "pipeline": { - "test": { - "dependsOn": ["build"] - }, - "build": { - "dependsOn": ["^build"] - } - } -} |
