Skip to content

Commit

Permalink
feat(turbo-gen): release @turbo/gen
Browse files Browse the repository at this point in the history
  • Loading branch information
tknickman committed May 11, 2023
1 parent 345cc7c commit 02ec5d1
Show file tree
Hide file tree
Showing 36 changed files with 2,268 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const fs = require("fs-extra");
const path = require("path");

module.exports = function plopConfig(plop) {
// controller generator
plop.setGenerator("controller", {
module.exports = function (plop, config) {
plop.setGenerator("transformer", {
description: "Add a new transformer",
prompts: [
{
Expand Down Expand Up @@ -36,8 +36,13 @@ module.exports = function plopConfig(plop) {
},
function createFixturesDirectory(answers) {
process.chdir(plop.getPlopfilePath());
const directory = `__tests__/__fixtures__/${answers.name}`;
fs.mkdirSync(`__tests__/__fixtures__/${answers.name}`);
const directory = path.join(
config.destBasePath,
"__tests__",
"__fixtures__",
answers.name
);
fs.mkdirSync(directory);

return `created empty ${directory} directory for fixtures`;
},
Expand Down
373 changes: 373 additions & 0 deletions packages/turbo-gen/LICENSE

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions packages/turbo-gen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# `@turbo/gen`

> This package is currently in **beta**. Please report any issues you encounter, and give us feedback about your experience using it!
Easily extend your Turborepo with new apps, and packages. Create new empty workspaces, copy existing workspaces, add workspaces from remote sources (just like `create-turbo`!) or run custom generators defined using [Plop](https://plopjs.com/) configurations.

## Usage

```bash
Usage: @turbo/gen [options] [command]

Extend your Turborepo

Options:
-v, --version Output the current version
-h, --help Display help for command

Commands:
add|a [options] Add a new package or app to your project
generate|g [options] [generator-name] Run custom generators
help [command] display help for command
```

## Add

Extend your Turborepo with new apps or packages. Create new empty workspaces, copy existing workspaces, or add workspaces from remote sources (just like `create-turbo`!).

### Usage

#### Blank Workspace

```bash
@turbo/gen add
```

#### Copy a Local Workspace

```bash
@turbo/gen add --copy
```

#### Copy a Remote Workspace

```bash
@turbo/gen add -e <git-url>
```

## Generate

Extend your Turborepo with custom generators defined using [Plop](https://plopjs.com/) configurations.

### Usage

```bash
@turbo/gen generate [generator-name]
```

### Writing Generators

`@turbo/gen` will search the root of your monorepo, and every workspace for generators defined at:

```bash
turbo/generators/config.js
```

**NOTE**: By default, generators are run from the _root_ of the _workspace_ where they are defined.
34 changes: 34 additions & 0 deletions packages/turbo-gen/__tests__/test-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
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"),
},
},
],
},
};
}
11 changes: 11 additions & 0 deletions packages/turbo-gen/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/** @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,
};
56 changes: 56 additions & 0 deletions packages/turbo-gen/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "@turbo/gen",
"version": "0.0.1-alpha.5",
"description": "Extend a Turborepo",
"homepage": "https://turbo.build/repo",
"license": "MPL-2.0",
"repository": {
"type": "git",
"url": "https://github.com/vercel/turbo",
"directory": "packages/turbo-gen"
},
"bugs": {
"url": "https://github.com/vercel/turbo/issues"
},
"bin": "dist/cli.js",
"scripts": {
"build": "tsup",
"test": "jest",
"lint": "eslint src/**/*.ts",
"check-types": "tsc --noEmit"
},
"dependencies": {
"chalk": "2.4.2",
"commander": "^10.0.0",
"fs-extra": "^10.1.0",
"inquirer": "^8.2.4",
"minimatch": "^9.0.0",
"node-plop": "^0.26.3",
"semver": "^7.3.8",
"update-check": "^1.5.4",
"validate-npm-package-name": "^5.0.0"
},
"devDependencies": {
"@turbo/test-utils": "workspace:*",
"@turbo/tsconfig": "workspace:*",
"@turbo/utils": "workspace:*",
"@turbo/workspaces": "workspace:*",
"@types/fs-extra": "^9.0.13",
"@types/inquirer": "^8.2.5",
"@types/jest": "^27.4.0",
"@types/node": "^16.11.12",
"@types/semver": "^7.3.9",
"@types/validate-npm-package-name": "^4.0.0",
"eslint": "^7.23.0",
"jest": "^27.4.3",
"ts-jest": "^27.1.1",
"tsup": "^6.7.0",
"typescript": "^4.5.5"
},
"files": [
"dist"
],
"publishConfig": {
"access": "public"
}
}
127 changes: 127 additions & 0 deletions packages/turbo-gen/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env node

import chalk from "chalk";
import { Argument, Command, Option } from "commander";
import notifyUpdate from "./utils/notifyUpdate";
import { logger } from "@turbo/utils";

import { add, generate, raw } from "./commands";
import cliPkg from "../package.json";

const turboGenCli = new Command();

turboGenCli
.name(chalk.bold(logger.turboGradient("@turbo/gen")))
.description("Extend your Turborepo")
.version(cliPkg.version, "-v, --version", "Output the current version")
.helpOption("-h, --help", "Display help for command")
.showHelpAfterError(false);

turboGenCli
.command("raw", { hidden: true })
.argument("<type>", "The type of generator to run")
.addOption(new Option("--json <arguments>", "Arguments as raw JSON"))
.action(raw);

turboGenCli
.command("add")
.aliases(["a"])
.description("Add a new package or app to your project")
.addOption(
new Option("-n, --name <workspace-name>", "Name for the new workspace")
)
.addOption(
new Option("-b, --empty", "Generate an empty workspace")
.conflicts("copy")
.default(true)
)
.addOption(
new Option(
"-c, --copy",
"Generate a workspace using an existing workspace as a template"
).conflicts("empty")
)
.addOption(
new Option(
"-d, --destination <dir>",
"Where the new workspace should be created"
)
)
.addOption(
new Option("-w, --what <type>", "The type of workspace to create").choices([
"app",
"package",
])
)
.addOption(
new Option(
"-r, --root <dir>",
"The root of your repository (default: directory with root turbo.json)"
)
)
.addOption(
new Option(
"-e, --example [github-url]",
`An example package to add. You can use a GitHub URL with any branch and/or subdirectory.`
).implies({ copy: true })
)
.addOption(
new 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
`
).implies({ copy: true })
)
.addOption(
new Option(
"--show-all-dependencies",
"Do not filter available dependencies by the workspace type"
).default(false)
)
.action(add);

turboGenCli
.command("generate")
.aliases(["g", "gen"])
.description("Run custom generators")
.addArgument(
new Argument("[generator-name]", "The name of the generator to run")
)
.addOption(
new Option(
"-c, --config <config>",
"Generator configuration file (default: turbo/generators/config.js"
)
)
.addOption(
new Option(
"-r, --root <dir>",
"The root of your repository (default: directory with root turbo.json)"
)
)
.addOption(
new Option(
"-a, --args <args...>",
"Arguments passed directly to generator"
).default([])
)
.action(generate);

turboGenCli
.parseAsync()
.then(notifyUpdate)
.catch(async (reason) => {
console.log();
if (reason.command) {
logger.error(`${chalk.bold(reason.command)} has failed.`);
} else {
logger.error("Unexpected error. Please report it as a bug:");
console.log(reason);
}
console.log();
await notifyUpdate();
process.exit(1);
});
38 changes: 38 additions & 0 deletions packages/turbo-gen/src/commands/add/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { logger } from "@turbo/utils";
import { getProject } from "../../utils/getProject";
import { copy, empty } from "../../generators";
import { WorkspaceType } from "../../generators/types";

export interface TurboGeneratorOptions {
name?: string;
// default to true
empty: boolean;
copy?: boolean;
destination?: string;
what?: WorkspaceType;
root?: string;
example?: string;
examplePath?: string;
// defaults to false
showAllDependencies: boolean;
}

export async function add(opts: TurboGeneratorOptions) {
const project = await getProject(opts);

console.log();
const args = { project, opts };
if (opts.copy) {
if (opts.example) {
logger.info(`Copy a remote workspace from ${opts.example}`);
} else {
logger.info(`Copy an existing workspace from ${project.name}`);
}
console.log();
await copy(args);
} else {
logger.info(`Add an empty workspace to ${project.name}`);
console.log();
await empty(args);
}
}
Loading

0 comments on commit 02ec5d1

Please sign in to comment.