diff --git a/.gitignore b/.gitignore index 065ed71f3ea10..6eaa206c79ecd 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ cli/turbo cli/turbo-new cli/turbo-new.exe cli/turbo.exe +cli/schema-generation.exe .DS_Store cache .env @@ -58,4 +59,4 @@ store todos.md examples/*/*.lock examples/*/*-lock.yaml -.store \ No newline at end of file +.store diff --git a/cli/Makefile b/cli/Makefile index 3e67a1f28b869..be0a03fedec70 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -11,6 +11,9 @@ GO_FLAGS += -trimpath turbo: cmd/turbo/version.go cmd/turbo/*.go internal/*/*.go go.mod CGO_ENABLED=0 go build $(GO_FLAGS) ./cmd/turbo +schema-generation: cmd/schema-generation/* internal/*/*.go go.mod + CGO_ENABLED=0 go build -o schema-generation.exe $(GO_FLAGS) ./cmd/schema-generation + # These tests are for development test: test-go vet-go diff --git a/cli/cmd/schema-generation/main.go b/cli/cmd/schema-generation/main.go new file mode 100644 index 0000000000000..db4467fcff8b8 --- /dev/null +++ b/cli/cmd/schema-generation/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/invopop/jsonschema" + "github.com/vercel/turborepo/cli/internal/fs" +) + +func main() { + r := new(jsonschema.Reflector) + + if err := r.AddGoComments("github.com/vercel/turborepo/cli", "internal/fs"); err != nil { + panic(err) + } + + for key, value := range r.CommentMap { + r.CommentMap[key] = applySoftLineBreaks(value) + } + + var schema = r.Reflect(&fs.TurboConfigJSON{}) + var schemajson, err = schema.MarshalJSON() + if err != nil { + panic(err) + } + fmt.Println(string(schemajson)) +} + +// applySoftLineBreaks is a function that replaces all soft line breaks +// with a space, and hard line breaks with a newline. +func applySoftLineBreaks(comment string) string { + replaced := strings.ReplaceAll(comment, "\n\n", "[[newline]]") + replaced = strings.ReplaceAll(replaced, "\n", " ") + replaced = strings.ReplaceAll(replaced, "[[newline]]", "\n") + return replaced +} diff --git a/cli/go.mod b/cli/go.mod index 151fcb9a5946e..93724ec3c3e57 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -37,4 +37,5 @@ require ( golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + github.com/invopop/jsonschema v0.3.0 ) diff --git a/cli/go.sum b/cli/go.sum index af92ceb7a0a97..4557d871f0d46 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -265,6 +265,8 @@ github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDG github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk= +github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -272,6 +274,8 @@ github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/invopop/jsonschema v0.3.0 h1:bLc7vCIFtaJYARy0JXnc/IKZygCms+lfOK4BaJM+fkI= +github.com/invopop/jsonschema v0.3.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -399,6 +403,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/cli/internal/fs/package_json.go b/cli/internal/fs/package_json.go index 47a8994b5e370..e82c8018dc273 100644 --- a/cli/internal/fs/package_json.go +++ b/cli/internal/fs/package_json.go @@ -11,17 +11,36 @@ import ( // TurboConfigJSON is the root turborepo configuration type TurboConfigJSON struct { - // Base Git branch - Base string `json:"baseBranch,omitempty"` - // Global root filesystem dependencies + JsonSchema string `json:"$schema"` + // Base is the branch or your git repository. Git is used by turbo in its hashing algorithm + // and --since CLI flag. + Base string `json:"baseBranch,omitempty" jsonschema:"default=origin/master,example=origin/main"` + // GlobalDependencies is a list of globs and environment variables for implicit global hash dependencies. + // Environment variables should be prefixed with $ (e.g. $GITHUB_TOKEN). + // + // Any other entry without this prefix, will be considered filesystem glob. The + // contents of these files will be included in the global hashing algorithm and affect + // the hashes of all tasks. + // + // This is useful for busting the cache based on .env files (not in Git), environment + // variables, or any root level file that impacts package tasks (but are not represented + // in the traditional dependency graph + // + // (e.g. a root tsconfig.json, jest.config.js, .eslintrc, etc.)). GlobalDependencies []string `json:"globalDependencies,omitempty"` TurboCacheOptions string `json:"cacheOptions,omitempty"` Outputs []string `json:"outputs,omitempty"` // RemoteCacheUrl is the Remote Cache API URL RemoteCacheUrl string `json:"remoteCacheUrl,omitempty"` - // Pipeline is a map of Turbo pipeline entries which define the task graph - // and cache behavior on a per task or per package-task basis. - Pipeline map[string]Pipeline + // Pipeline is an object representing the task dependency graph of your project. turbo interprets + // these conventions to properly schedule, execute, and cache the outputs of tasks in + // your project. + // + // Each key in this object is the name of a task that can be executed by turbo run. If turbo finds a workspace + // package with a package.json scripts object with a matching key, it will apply the + // pipeline task configuration to that NPM script during execution. This allows you to + // use pipeline to set conventions across your entire Turborepo. + Pipeline map[string]Pipeline `json:"pipeline"` } func ReadTurboConfigJSON(path string) (*TurboConfigJSON, error) { @@ -41,9 +60,32 @@ func ReadTurboConfigJSON(path string) (*TurboConfigJSON, error) { } type PPipeline struct { - Outputs *[]string `json:"outputs"` - Cache *bool `json:"cache,omitempty"` - DependsOn []string `json:"dependsOn,omitempty"` + // The set of glob patterns of a task's cacheable filesystem outputs. + // + // Note: turbo automatically logs stderr/stdout to .turbo/run-.log. This file is + // always treated as a cacheable artifact and never needs to be specified. + // + // Passing an empty array can be used to tell turbo that a task is a side-effect and + // thus doesn't emit any filesystem artifacts (e.g. like a linter), but you still want + // to cache its logs (and treat them like an artifact). + Outputs *[]string `json:"outputs" jsonschema:"default=dist/**,default=build/**"` + // Whether or not to cache the task outputs. Setting cache to false is useful for daemon + // or long-running "watch" or development mode tasks that you don't want to cache. + Cache *bool `json:"cache,omitempty" jsonschema:"default=true"` + // The list of tasks and environment variables that this task depends on. + // + // Prefixing an item in dependsOn with a ^ tells turbo that this pipeline task depends + // on the package's topological dependencies completing the task with the ^ prefix first + // (e.g. "a package's build tasks should only run once all of its dependencies and + // devDependencies have completed their own build commands"). + // + // Items in dependsOn without ^ prefix, express the relationships between tasks at the + // package level (e.g. "a package's test and lint commands depend on build being + // completed first"). + // + // Prefixing an item in dependsOn with a $ tells turbo that this pipeline task depends + // the value of that environment variable. + DependsOn []string `json:"dependsOn,omitempty"` } type Pipeline struct { diff --git a/cli/package.json b/cli/package.json index be0ac97291c5b..efe362a148304 100644 --- a/cli/package.json +++ b/cli/package.json @@ -9,6 +9,7 @@ "e2e": "node -r esbuild-register scripts/e2e/e2e.ts", "watch": "watchlist e2e -- pnpm test", "publish": "make publish-all", + "schema": "make schema-generation", "format": "go fmt ./cmd/... ./internal/...", "lint": "go vet ./cmd/... ./internal/..." }, diff --git a/docs/package.json b/docs/package.json index 4c5a719d187bb..9b74eeca00c59 100644 --- a/docs/package.json +++ b/docs/package.json @@ -8,7 +8,7 @@ "start": "next start", "build": "next build ", "lint": "next lint", - "schema": "ts-json-schema-generator -p ./schema.d.ts -o ./public/schema.json -t Schema --minify" + "schema": "(cd ../cli && ./schema-generation.exe) > public/schema.json" }, "author": "Jared Palmer", "license": "MPL-2.0", @@ -61,7 +61,6 @@ "eslint-config-prettier": "8.4.0", "postcss": "8.3.5", "tailwindcss": "3.0.15", - "ts-json-schema-generator": "0.98.0-next.0", "typescript": "4.5.5", "webpack": "5.65.0" }, diff --git a/docs/schema.d.ts b/docs/schema.d.ts deleted file mode 100644 index 6616283b79a70..0000000000000 --- a/docs/schema.d.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* This file generates the `schema.json` file. */ - -export interface Schema { - /** @default https://turborepo.org/schema.json */ - $schema?: string; - - /** - * The base branch or your git repository. Git is used by turbo in its hashing algorithm - * and --since CLI flag. - * - * @default origin/master - */ - baseBranch?: string; - - /** - * A list of globs and environment variables for implicit global hash dependencies. - * Environment variables should be prefixed with $ (e.g. $GITHUB_TOKEN). - * - * Any other entry without this prefix, will be considered filesystem glob. The - * contents of these files will be included in the global hashing algorithm and affect - * the hashes of all tasks. - * - * This is useful for busting the cache based on .env files (not in Git), environment - * variables, or any root level file that impacts package tasks (but are not represented - * in the traditional dependency graph - * - * (e.g. a root tsconfig.json, jest.config.js, .eslintrc, etc.)). - * - * @default [] - */ - globalDependencies?: string[]; - - /** - * An object representing the task dependency graph of your project. turbo interprets - * these conventions to properly schedule, execute, and cache the outputs of tasks in - * your project. - * - * @default {} - */ - pipeline: { - /** - * The name of a task that can be executed by turbo run. If turbo finds a workspace - * package with a package.json scripts object with a matching key, it will apply the - * pipeline task configuration to that NPM script during execution. This allows you to - * use pipeline to set conventions across your entire Turborepo. - */ - [script: string]: Pipeline; - }; -} - -export interface Pipeline { - /** - * The list of tasks and environment variables that this task depends on. - * - * Prefixing an item in dependsOn with a ^ tells turbo that this pipeline task depends - * on the package's topological dependencies completing the task with the ^ prefix first - * (e.g. "a package's build tasks should only run once all of its dependencies and - * devDependencies have completed their own build commands"). - * - * Items in dependsOn without ^ prefix, express the relationships between tasks at the - * package level (e.g. "a package's test and lint commands depend on build being - * completed first"). - * - * Prefixing an item in dependsOn with a $ tells turbo that this pipeline task depends - * the value of that environment variable. - * - * @default [] - */ - dependsOn?: string[]; - - /** - * The set of glob patterns of a task's cacheable filesystem outputs. - * - * Note: turbo automatically logs stderr/stdout to .turbo/run-.log. This file is - * always treated as a cacheable artifact and never needs to be specified. - * - * Passing an empty array can be used to tell turbo that a task is a side-effect and - * thus doesn't emit any filesystem artifacts (e.g. like a linter), but you still want - * to cache its logs (and treat them like an artifact). - * - * @default ["dist/**", "build/**"] - */ - outputs?: string[]; - - /** - * Whether or not to cache the task outputs. Setting cache to false is useful for daemon - * or long-running "watch" or development mode tasks that you don't want to cache. - * - * @default true - */ - cache?: boolean; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6d89100c923b..b422e840a250c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -106,7 +106,6 @@ importers: swr: 1.0.1 tailwindcss: 3.0.15 title: 3.4.3 - ts-json-schema-generator: 0.98.0-next.0 typescript: 4.5.5 webpack: 5.65.0 dependencies: @@ -157,7 +156,6 @@ importers: eslint-config-prettier: 8.4.0_eslint@8.10.0 postcss: 8.3.5 tailwindcss: 3.0.15_72be89de93206f86f2d67dd5290ec14d - ts-json-schema-generator: 0.98.0-next.0 typescript: 4.5.5 webpack: 5.65.0 @@ -2689,11 +2687,6 @@ packages: engines: {node: '>= 12'} dev: true - /commander/9.0.0: - resolution: {integrity: sha512-JJfP2saEKbQqvW+FI93OYUB4ByV5cizMpFMiiJI8xDbBvQvSkIk0VvQdn1CZ8mqAO8Loq2h0gYTYtDFUZUeERw==} - engines: {node: ^12.20.0 || >=14} - dev: true - /component-emitter/1.3.0: resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} dev: true @@ -7419,11 +7412,6 @@ packages: ret: 0.1.15 dev: true - /safe-stable-stringify/2.3.1: - resolution: {integrity: sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==} - engines: {node: '>=10'} - dev: true - /safer-buffer/2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -8241,19 +8229,6 @@ packages: yargs-parser: 20.2.9 dev: true - /ts-json-schema-generator/0.98.0-next.0: - resolution: {integrity: sha512-3kdpdPOJhaFpiK55XZe9pzTBELG8eStp7B7hl2rRh8BUu3170u2v/JqqGeB2pvY3ecH4eXq6fZZ6odQnO7J/pg==} - engines: {node: '>=10.0.0'} - hasBin: true - dependencies: - '@types/json-schema': 7.0.9 - commander: 9.0.0 - glob: 7.2.0 - json5: 2.2.0 - safe-stable-stringify: 2.3.1 - typescript: 4.5.5 - dev: true - /tsconfig-paths/3.12.0: resolution: {integrity: sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==} dependencies: diff --git a/turbo.json b/turbo.json index d5fd4bd9819e2..7ff8a11d7483d 100644 --- a/turbo.json +++ b/turbo.json @@ -26,7 +26,15 @@ "dependsOn": [] }, "schema": { - "outputs": ["public/schema.json"] + "outputs": [ + "schema-generation", + "schema-generation.exe", + "public/schema.json" + ] + }, + "docs#schema": { + "outputs": ["public/schema.json"], + "dependsOn": ["cli#schema"] }, "create-turbo#test": { "dependsOn": ["create-turbo#build"], @@ -34,7 +42,7 @@ }, "docs#build": { "outputs": [".next/**"], - "dependsOn": ["^build", "docs#schema"] + "dependsOn": ["^build", "cli#schema"] } } }