diff --git a/docs/link-checker/src/markdown.ts b/docs/link-checker/src/markdown.ts index 1d06562455190..efaade3bb7528 100644 --- a/docs/link-checker/src/markdown.ts +++ b/docs/link-checker/src/markdown.ts @@ -51,7 +51,9 @@ const slugger = new GitHubSlugger(); /** Collect the paths of all .mdx files we care about */ const getAllMdxFilePaths = async (): Promise => { const allFiles = await fs.readdir(DOCS_PATH, { recursive: true }); - return allFiles.filter((file) => file.endsWith(".mdx")); + return allFiles.filter( + (file) => file.endsWith(".mdx") && !file.startsWith("openapi/") + ); }; // Returns the slugs of all headings in a tree @@ -146,6 +148,33 @@ const prepareDocumentMapEntry = async ( } }; +/** Checks if the hash exists in a document's headings, accounting for ExperimentalBadge suffixes */ +const hashExistsInHeadings = (headings: string[], hash: string): boolean => { + if (headings.includes(hash)) { + return true; + } + + // Handle experimental badge suffix: -experimental -> -experimentalbadgeexperimentalexperimentalbadge + const experimentalHeading = hash.replace( + "-experimental", + "-experimentalbadgeexperimentalexperimentalbadge" + ); + if (headings.includes(experimentalHeading)) { + return true; + } + + // Handle pre-release badge suffix: -pre-release -> -experimentalbadgepre-releaseexperimentalbadge + const preReleaseHeading = hash.replace( + "-pre-release", + "-experimentalbadgepre-releaseexperimentalbadge" + ); + if (headings.includes(preReleaseHeading)) { + return true; + } + + return false; +}; + /** Checks if the links point to existing documents */ const validateInternalLink = (documentMap: Map) => (doc: Document, href: string) => { @@ -172,7 +201,7 @@ const validateInternalLink = }); } else if (hash && !EXCLUDED_HASHES.includes(hash)) { // Check if the hash link points to an existing section within the document - const hashFound = foundPage.headings.includes(hash); + const hashFound = hashExistsInHeadings(foundPage.headings, hash); if (!hashFound) { errors.push({ @@ -197,18 +226,26 @@ const validateHashLink = (doc: Document, href: string) => { return []; } - if ( - doc.headings.includes( - // Handles when the link has the experimental badge in it. - // Because we're parsing the raw document (not the rendered output), the JSX declaration is still present. - hashLink.replace( - "-experimental", - "-experimentalbadgeexperimentalexperimentalbadge" - ) - ) - ) { + // Handles when the link has the experimental badge in it. + // Because we're parsing the raw document (not the rendered output), the JSX declaration is still present. + const experimentalHeading = hashLink.replace( + "-experimental", + "-experimentalbadgeexperimentalexperimentalbadge" + ); + const preReleaseHeading = + hashLink + "-experimentalbadgepre-releaseexperimentalbadge"; + + if (doc.headings.includes(experimentalHeading)) { + console.warn( + `The hash link "${hashLink}" passed when including the Experimental JSX declaration.` + ); + console.log(); + return []; + } + + if (doc.headings.includes(preReleaseHeading)) { console.warn( - `The hash link "${hashLink}" passed when including the JSX declaration.` + `The hash link "${hashLink}" passed when including the Pre-release JSX declaration.` ); console.log(); return []; diff --git a/docs/site/content/docs/reference/configuration.mdx b/docs/site/content/docs/reference/configuration.mdx index 3fabffde8b6fa..4f4c5bf479417 100644 --- a/docs/site/content/docs/reference/configuration.mdx +++ b/docs/site/content/docs/reference/configuration.mdx @@ -187,6 +187,30 @@ Turborepo's Environment Modes allow you to control which environment variables a Read more about [Environment Modes](/docs/crafting-your-repository/using-environment-variables#environment-modes). +### `futureFlags` + +```jsonc title="./turbo.json" +{ + "futureFlags": { + "turboExtendsKeyword": true + } +} +``` + +Enable experimental features that will become the default behavior in future versions of Turborepo. + + + `futureFlags` can only be set in the root `turbo.json`. An error will be + thrown if set in a [Package + Configuration](/docs/reference/package-configurations). + + +#### `turboExtendsKeyword` + +Default: `false` + +When enabled, allows using the [`$TURBO_EXTENDS$`](#turbo_extends) microsyntax in array fields. This microsyntax changes the behavior of [Package Configurations](/docs/reference/package-configurations) from replacing array fields to appending to them. + ### `tags` Experimental ```jsonc title="./apps/web/turbo.json" @@ -476,6 +500,56 @@ Starting a file glob with `$TURBO_ROOT$` will change the glob to be relative to } ``` +#### `$TURBO_EXTENDS$` Pre-release + + + This feature requires enabling + [`futureFlags.turboExtendsKeyword`](#turboextendskeyword) in your root + `turbo.json`. + + +When using [Package Configurations](/docs/reference/package-configurations), array fields completely replace the values from the root `turbo.json` by default. The `$TURBO_EXTENDS$` microsyntax changes this behavior to **append** instead of **replace**. + +This microsyntax can be used in the following array fields: + +- `dependsOn` +- `env` +- `inputs` +- `outputs` +- `passThroughEnv` +- `with` + +For example, if your root `turbo.json` defines: + +```jsonc title="./turbo.json" +{ + "futureFlags": { + "turboExtendsKeyword": true + }, + "tasks": { + "build": { + "outputs": ["dist/**"] + } + } +} +``` + +A Package Configuration can add additional outputs while keeping the root outputs: + +```jsonc title="./apps/web/turbo.json" +{ + "extends": ["//"], + "tasks": { + "build": { + // Inherits "dist/**" from root, and adds ".next/**" + "outputs": ["$TURBO_EXTENDS$", ".next/**", "!.next/cache/**"] + } + } +} +``` + +Without `$TURBO_EXTENDS$`, the `outputs` array would be completely replaced with `[".next/**", "!.next/cache/**"]`, dropping the `"dist/**"` from the root configuration. + ### `outputLogs` Default: `full` diff --git a/docs/site/content/docs/reference/package-configurations.mdx b/docs/site/content/docs/reference/package-configurations.mdx index bc33ed0928670..2aec3d6bda726 100644 --- a/docs/site/content/docs/reference/package-configurations.mdx +++ b/docs/site/content/docs/reference/package-configurations.mdx @@ -34,9 +34,60 @@ key: special name used to identify the root directory of the monorepo. -Configuration in a package can override any of [the configurations for a -task](/docs/reference/configuration#defining-tasks). Any keys that are not included are inherited -from the extended `turbo.json`. +## Inheritance behavior + +When a Package Configuration extends the root `turbo.json`, task properties are inherited differently depending on their type. + +### Scalar fields are inherited + +Scalar fields like `outputLogs`, `cache`, `persistent`, and `interactive` are **inherited** from the root configuration. You only need to specify them in a Package Configuration if you want to override them. + +For example, if your root `turbo.json` sets `"outputLogs": "hash-only"` for a task, all packages inherit that setting automatically. + +### Array fields replace by default + +Array fields like `outputs`, `env`, `inputs`, `dependsOn`, and `passThroughEnv` **completely replace** the root configuration's values by default. + +```jsonc title="./turbo.json" +{ + "tasks": { + "build": { + "outputs": ["dist/**"], + "env": ["NODE_ENV"] + } + } +} +``` + +```jsonc title="./apps/my-app/turbo.json" +{ + "extends": ["//"], + "tasks": { + "build": { + // This REPLACES the root outputs - "dist/**" is NOT included + "outputs": [".next/**"] + } + } +} +``` + +### Extending arrays with `$TURBO_EXTENDS$` Pre-release + +To **add** to inherited array values instead of replacing them, use the [`$TURBO_EXTENDS$` microsyntax](/docs/reference/configuration#turbo_extends-pre-release): + +```jsonc title="./apps/my-app/turbo.json" +{ + "extends": ["//"], + "tasks": { + "build": { + // Inherits "dist/**" from root AND adds ".next/**" + "outputs": ["$TURBO_EXTENDS$", ".next/**"] + } + } +} +``` + +The `$TURBO_EXTENDS$` marker must be the first element in the array. It works with `outputs`, `env`, `inputs`, `dependsOn`, `passThroughEnv`, and `with`. ## Examples @@ -143,40 +194,9 @@ but continue to inherit any other tasks defined at the root. ## Comparison to package-specific tasks -At first glance, Package Configurations may sound a lot like the -[`package#task` syntax](/docs/crafting-your-repository/configuring-tasks#depending-on-a-specific-task-in-a-specific-package) in the root `turbo.json`. The features are -similar, but have one significant difference: when you declare a package-specific task -in the root `turbo.json`, it _completely_ overwrites the baseline task -configuration. With a Package Configuration, the task configuration is merged -instead. - -Consider the example of the monorepo with multiple Next.js apps and a Sveltekit -app again. With a package-specific task, you might configure your root -`turbo.json` like this: - -```jsonc title="./turbo.json" -{ - "tasks": { - "build": { - "outputLogs": "hash-only", - "inputs": ["src/**"], - "outputs": [".next/**", "!.next/cache/**"] - }, - "my-sveltekit-app#build": { - "outputLogs": "hash-only", // must duplicate this - "inputs": ["src/**"], // must duplicate this - "outputs": [".svelte-kit/**"] - } - } -} -``` - -In this example, `my-sveltekit-app#build` completely overwrites `build` for the -Sveltekit app, so `outputLogs` and `inputs` also need to be duplicated. +The [`package#task` syntax](/docs/crafting-your-repository/configuring-tasks#depending-on-a-specific-task-in-a-specific-package) in the root `turbo.json` **completely overwrites** all task configuration—nothing is inherited. -With Package Configurations, `outputLogs` and `inputs` are inherited, so -you don't need to duplicate them. You only need to override `outputs` in -`my-sveltekit-app` config. +With Package Configurations, scalar fields are inherited and only the fields you specify are overridden. This means less duplication when you only need to change one or two properties. Although there are no plans to remove package-specific task configurations, we @@ -218,18 +238,18 @@ Package Configuration for `my-nextjs-app`: { "tasks": { "my-nextjs-app#build": { - // ❌ This is not allowed. Even though it's + // This is not allowed. Even though it's // referencing the correct package, "my-nextjs-app" // is inferred, and we don't need to specify it again. // This syntax also has different behavior, so we do not want to allow it. // (see "Comparison to package-specific tasks" section) }, "my-sveltekit-app#build": { - // ❌ Changing configuration for the "my-sveltekit-app" package + // Changing configuration for the "my-sveltekit-app" package // from Package Configuration in "my-nextjs-app" is not allowed. }, "build": { - // ✅ just use the task name! + // Just use the task name! } } }