feat(misc): enable ts solution setup by default for new workspaces (#30372)

## Current Behavior

To create a new workspace that uses the new TS solution setup the
`--workspaces` flag must be provided (Node, React, and Vue stacks).

## Expected Behavior

New workspaces should be created by default using the new TS solution
setup (Node, React, and Vue stacks). Users can opt out of it by
providing the `--no-workspaces` flag.

## Related Issue(s)

Fixes #
This commit is contained in:
Leosvel Pérez Espinosa 2025-03-14 17:42:31 +01:00 committed by GitHub
parent 398ab354e5
commit 2d69aa12bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 66 additions and 65 deletions

View File

@ -47,7 +47,7 @@ Install `create-nx-workspace` globally to invoke the command directly, or use `n
| `--unitTestRunner` | `jest`, `vitest`, `none` | Test runner to use for unit tests. | | `--unitTestRunner` | `jest`, `vitest`, `none` | Test runner to use for unit tests. |
| `--useGitHub` | boolean | Will you be using GitHub as your git hosting provider? (Default: `false`) | | `--useGitHub` | boolean | Will you be using GitHub as your git hosting provider? (Default: `false`) |
| `--version` | boolean | Show version number. | | `--version` | boolean | Show version number. |
| `--workspaces` | boolean | Use package manager workspaces. (Default: `false`) | | `--workspaces` | boolean | Use package manager workspaces. (Default: `true`) |
| `--workspaceType` | `integrated`, `package-based`, `standalone` | The type of workspace to create. | | `--workspaceType` | `integrated`, `package-based`, `standalone` | The type of workspace to create. |
## Presets ## Presets

View File

@ -47,7 +47,7 @@ Install `create-nx-workspace` globally to invoke the command directly, or use `n
| `--unitTestRunner` | `jest`, `vitest`, `none` | Test runner to use for unit tests. | | `--unitTestRunner` | `jest`, `vitest`, `none` | Test runner to use for unit tests. |
| `--useGitHub` | boolean | Will you be using GitHub as your git hosting provider? (Default: `false`) | | `--useGitHub` | boolean | Will you be using GitHub as your git hosting provider? (Default: `false`) |
| `--version` | boolean | Show version number. | | `--version` | boolean | Show version number. |
| `--workspaces` | boolean | Use package manager workspaces. (Default: `false`) | | `--workspaces` | boolean | Use package manager workspaces. (Default: `true`) |
| `--workspaceType` | `integrated`, `package-based`, `standalone` | The type of workspace to create. | | `--workspaceType` | `integrated`, `package-based`, `standalone` | The type of workspace to create. |
## Presets ## Presets

View File

@ -102,7 +102,7 @@
"workspaces": { "workspaces": {
"description": "Whether to use package manager workspaces.", "description": "Whether to use package manager workspaces.",
"type": "boolean", "type": "boolean",
"default": false "default": true
} }
}, },
"additionalProperties": true, "additionalProperties": true,

View File

@ -119,7 +119,7 @@
"workspaces": { "workspaces": {
"description": "Whether to use package manager workspaces.", "description": "Whether to use package manager workspaces.",
"type": "boolean", "type": "boolean",
"default": false "default": true
} }
}, },
"required": ["preset", "name"], "required": ["preset", "name"],

View File

@ -29,12 +29,16 @@ There are two different methods that Nx supports for linking TypeScript projects
## Project Linking with Workspaces ## Project Linking with Workspaces
To create a new Nx workspace that links projects with package manager workspaces, use the `--workspaces` flag. Create a new Nx workspace that links projects with package manager workspaces:
```shell ```shell
npx create-nx-workspace --workspaces npx create-nx-workspace
``` ```
{% callout type="note" title="Opt-out of Workspaces" %}
You can opt-out of workspaces by running `npx create-nx-workspace --no-workspaces`.
{% /callout %}
### Set Up Package Manager Workspaces ### Set Up Package Manager Workspaces
The configuration for package manager workspaces varies based on which package manager you're using. The configuration for package manager workspaces varies based on which package manager you're using.

View File

@ -7,39 +7,38 @@ description: A comprehensive reference of all environment variables that can be
The following environment variables are ones that you can set to change the behavior of Nx in different environments. The following environment variables are ones that you can set to change the behavior of Nx in different environments.
| Property | Type | Description | | Property | Type | Description |
| ------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| NX_ADD_PLUGINS | boolean | If set to `false`, Nx will not add plugins to infer tasks. This is `true` by default. Workspaces created before Nx 18 will have this disabled via a migration for backwards compatibility | | NX_ADD_PLUGINS | boolean | If set to `false`, Nx will not add plugins to infer tasks. This is `true` by default. Workspaces created before Nx 18 will have this disabled via a migration for backwards compatibility |
| NX_ADD_TS_PLUGIN | boolean | If set to `false` when creating a new workspace using the `ts` preset, Nx will not add the `@nx/js/typescript` plugin to infer tasks and will not set up the workspace with [TypeScript project references](https://www.typescriptlang.org/docs/handbook/project-references.html). This is `true` by default. | | NX_BASE | string | The default base branch to use when calculating the affected projects. Can be overridden on the command line with `--base`. |
| NX_BASE | string | The default base branch to use when calculating the affected projects. Can be overridden on the command line with `--base`. | | NX_CACHE_DIRECTORY | string | The cache for task outputs is stored in `.nx/cache` by default. Set this variable to use a different directory. |
| NX_CACHE_DIRECTORY | string | The cache for task outputs is stored in `.nx/cache` by default. Set this variable to use a different directory. | | NX_CACHE_PROJECT_GRAPH | boolean | If set to `false`, disables the project graph cache. Most useful when developing a plugin that modifies the project graph. |
| NX_CACHE_PROJECT_GRAPH | boolean | If set to `false`, disables the project graph cache. Most useful when developing a plugin that modifies the project graph. | | NX_DAEMON | boolean | If set to `false`, disables the Nx daemon process. Disable the daemon to print `console.log` statements in plugin code you are developing. |
| NX_DAEMON | boolean | If set to `false`, disables the Nx daemon process. Disable the daemon to print `console.log` statements in plugin code you are developing. | | NX_DEFAULT_PROJECT | string | The default project used for commands which require a project. e.g. `nx build`, `nx g component`, etc. |
| NX_DEFAULT_PROJECT | string | The default project used for commands which require a project. e.g. `nx build`, `nx g component`, etc. | | NX_HEAD | string | The default head branch to use when calculating the affected projects. Can be overridden on the command line with `--head`. |
| NX_HEAD | string | The default head branch to use when calculating the affected projects. Can be overridden on the command line with `--head`. | | NX_PERF_LOGGING | boolean | If set to `true`, will print debug information useful for profiling executors and Nx itself |
| NX_PERF_LOGGING | boolean | If set to `true`, will print debug information useful for profiling executors and Nx itself | | NX_PROFILE | string | Prepend `NX_PROFILE=profile.json` before running targets with Nx to generate a file that be [loaded in Chrome dev tools](/troubleshooting/performance-profiling) to visualize the performance of Nx across multiple processes. |
| NX_PROFILE | string | Prepend `NX_PROFILE=profile.json` before running targets with Nx to generate a file that be [loaded in Chrome dev tools](/troubleshooting/performance-profiling) to visualize the performance of Nx across multiple processes. | | NX_WORKSPACE_DATA_DIRECTORY | string | The project graph cache and some other internal nx caches are stored in `.nx/workspace-data` by default. Set this variable to use a different directory. |
| NX_WORKSPACE_DATA_DIRECTORY | string | The project graph cache and some other internal nx caches are stored in `.nx/workspace-data` by default. Set this variable to use a different directory. | | NX_PARALLEL | number | The number of tasks Nx should run in parallel. Overrides any configured value inside nx.json |
| NX_PARALLEL | number | The number of tasks Nx should run in parallel. Overrides any configured value inside nx.json | | NX_RUNNER | string | The name of task runner from the config to use. Can be overridden on the command line with `--runner`. Not read if `NX_TASKS_RUNNER` is set. |
| NX_RUNNER | string | The name of task runner from the config to use. Can be overridden on the command line with `--runner`. Not read if `NX_TASKS_RUNNER` is set. | | NX_SKIP_NX_CACHE | boolean | Rerun the tasks even when the results are available in the cache. |
| NX_SKIP_NX_CACHE | boolean | Rerun the tasks even when the results are available in the cache. | | NX_DISABLE_NX_CACHE | boolean | Rerun the tasks even when the results are available in the cache. |
| NX_DISABLE_NX_CACHE | boolean | Rerun the tasks even when the results are available in the cache. | | NX_SKIP_REMOTE_CACHE | boolean | Disable all remote caching features. This means that the remote cache will not be read from or written to. Includes Nx Cloud and Powerpack Caches. |
| NX_SKIP_REMOTE_CACHE | boolean | Disable all remote caching features. This means that the remote cache will not be read from or written to. Includes Nx Cloud and Powerpack Caches. | | NX_DISABLE_REMOTE_CACHE | boolean | Disable all remote caching features. This means that the remote cache will not be read from or written to. Includes Nx Cloud and Powerpack Caches. |
| NX_DISABLE_REMOTE_CACHE | boolean | Disable all remote caching features. This means that the remote cache will not be read from or written to. Includes Nx Cloud and Powerpack Caches. | | NX_TASKS_RUNNER | string | The name of task runner from the config to use. Can be overridden on the command line with `--runner`. Preferred over `NX_RUNNER`. |
| NX_TASKS_RUNNER | string | The name of task runner from the config to use. Can be overridden on the command line with `--runner`. Preferred over `NX_RUNNER`. | | NX_TASKS_RUNNER_DYNAMIC_OUTPUT | boolean | If set to `false`, will use non-dynamic terminal output strategy (what you see in CI), even when you terminal can support the dynamic version |
| NX_TASKS_RUNNER_DYNAMIC_OUTPUT | boolean | If set to `false`, will use non-dynamic terminal output strategy (what you see in CI), even when you terminal can support the dynamic version | | NX_VERBOSE_LOGGING | boolean | If set to `true`, will print debug information useful for troubleshooting |
| NX_VERBOSE_LOGGING | boolean | If set to `true`, will print debug information useful for troubleshooting | | NX_DRY_RUN | boolean | If set to `true`, will perform a dry run of the generator. No files will be created and no packages will be installed. |
| NX_DRY_RUN | boolean | If set to `true`, will perform a dry run of the generator. No files will be created and no packages will be installed. | | NX_INTERACTIVE | boolean | If set to `true`, will allow Nx to prompt you in the terminal to answer some further questions when running generators. |
| NX_INTERACTIVE | boolean | If set to `true`, will allow Nx to prompt you in the terminal to answer some further questions when running generators. | | NX_GENERATE_QUIET | boolean | If set to `true`, will prevent Nx logging file operations during generate |
| NX_GENERATE_QUIET | boolean | If set to `true`, will prevent Nx logging file operations during generate | | NX_PREFER_TS_NODE | boolean | If set to `true`, Nx will use `ts-node` for local execution of plugins even if `@swc-node/register` is installed. |
| NX_PREFER_TS_NODE | boolean | If set to `true`, Nx will use `ts-node` for local execution of plugins even if `@swc-node/register` is installed. | | NX_IGNORE_CYCLES | boolean | If set to `true`, Nx will ignore errors created by a task graph circular dependency. Can be overridden on the command line with `--nxIgnoreCycles` |
| NX_IGNORE_CYCLES | boolean | If set to `true`, Nx will ignore errors created by a task graph circular dependency. Can be overridden on the command line with `--nxIgnoreCycles` | | NX_BATCH_MODE | boolean | If set to `true`, Nx will run task(s) in batches for executors which support batches. |
| NX_BATCH_MODE | boolean | If set to `true`, Nx will run task(s) in batches for executors which support batches. | | NX_SKIP_LOG_GROUPING | boolean | If set to `true`, Nx will not group command's logs on CI. |
| NX_SKIP_LOG_GROUPING | boolean | If set to `true`, Nx will not group command's logs on CI. | | NX_MIGRATE_CLI_VERSION | string | The version of Nx to use for running the `nx migrate` command. If not set, it defaults to `latest`. |
| NX_MIGRATE_CLI_VERSION | string | The version of Nx to use for running the `nx migrate` command. If not set, it defaults to `latest`. | | NX_LOAD_DOT_ENV_FILES | boolean | If set to 'false', Nx will not load any environment files (e.g. `.local.env`, `.env.local`) |
| NX_LOAD_DOT_ENV_FILES | boolean | If set to 'false', Nx will not load any environment files (e.g. `.local.env`, `.env.local`) | | NX_NATIVE_FILE_CACHE_DIRECTORY | string | The cache for native `.node` files is stored under a global temp directory by default. Set this variable to use a different directory. This is interpreted as an absolute path. |
| NX_NATIVE_FILE_CACHE_DIRECTORY | string | The cache for native `.node` files is stored under a global temp directory by default. Set this variable to use a different directory. This is interpreted as an absolute path. | | NX_PLUGIN_NO_TIMEOUTS | boolean | If set to `true`, plugin operations will not timeout |
| NX_PLUGIN_NO_TIMEOUTS | boolean | If set to `true`, plugin operations will not timeout |
Nx will set the following environment variables so they can be accessible within the process even outside of executors and generators. Nx will set the following environment variables so they can be accessible within the process even outside of executors and generators.

View File

@ -52,7 +52,7 @@ title="Nx React Monorepo Tutorial Walkthrough"
Create a new React monorepo with the following command: Create a new React monorepo with the following command:
```{% command="npx create-nx-workspace@latest react-monorepo --preset=react-monorepo --workspaces" path="~" %} ```{% command="npx create-nx-workspace@latest react-monorepo --preset=react-monorepo" path="~" %}
NX Let's create a new workspace [https://nx.dev/getting-started/intro] NX Let's create a new workspace [https://nx.dev/getting-started/intro]

View File

@ -167,7 +167,7 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
.option('workspaces', { .option('workspaces', {
describe: chalk.dim`Use package manager workspaces.`, describe: chalk.dim`Use package manager workspaces.`,
type: 'boolean', type: 'boolean',
default: false, default: true,
}) })
.option('formatter', { .option('formatter', {
describe: chalk.dim`Code formatter to use.`, describe: chalk.dim`Code formatter to use.`,
@ -281,6 +281,8 @@ async function normalizeArgsMiddleware(
"Let's create a new workspace [https://nx.dev/getting-started/intro]", "Let's create a new workspace [https://nx.dev/getting-started/intro]",
}); });
argv.workspaces ??= true;
try { try {
argv.name = await determineFolder(argv); argv.name = await determineFolder(argv);
if (!argv.preset || isKnownPreset(argv.preset)) { if (!argv.preset || isKnownPreset(argv.preset)) {
@ -414,8 +416,7 @@ async function determineStack(
{ {
name: `none`, name: `none`,
message: message:
process.env.NX_ADD_PLUGINS !== 'false' && process.env.NX_ADD_PLUGINS !== 'false' && parsedArgs.workspaces
process.env.NX_ADD_TS_PLUGIN !== 'false'
? `None: Configures a TypeScript/JavaScript monorepo.` ? `None: Configures a TypeScript/JavaScript monorepo.`
: `None: Configures a TypeScript/JavaScript project with minimal structure.`, : `None: Configures a TypeScript/JavaScript project with minimal structure.`,
}, },
@ -519,7 +520,7 @@ async function determineNoneOptions(
if ( if (
(!parsedArgs.preset || parsedArgs.preset === Preset.TS) && (!parsedArgs.preset || parsedArgs.preset === Preset.TS) &&
process.env.NX_ADD_PLUGINS !== 'false' && process.env.NX_ADD_PLUGINS !== 'false' &&
process.env.NX_ADD_TS_PLUGIN !== 'false' parsedArgs.workspaces
) { ) {
return { return {
preset: Preset.TS, preset: Preset.TS,
@ -595,7 +596,7 @@ async function determineReactOptions(
let linter: undefined | 'none' | 'eslint'; let linter: undefined | 'none' | 'eslint';
let formatter: undefined | 'none' | 'prettier'; let formatter: undefined | 'none' | 'prettier';
const workspaces = parsedArgs.workspaces ?? false; const workspaces = parsedArgs.workspaces;
if (parsedArgs.preset && parsedArgs.preset !== Preset.React) { if (parsedArgs.preset && parsedArgs.preset !== Preset.React) {
preset = parsedArgs.preset; preset = parsedArgs.preset;
@ -764,7 +765,7 @@ async function determineVueOptions(
let linter: undefined | 'none' | 'eslint'; let linter: undefined | 'none' | 'eslint';
let formatter: undefined | 'none' | 'prettier'; let formatter: undefined | 'none' | 'prettier';
const workspaces = parsedArgs.workspaces ?? false; const workspaces = parsedArgs.workspaces;
if (parsedArgs.preset && parsedArgs.preset !== Preset.Vue) { if (parsedArgs.preset && parsedArgs.preset !== Preset.Vue) {
preset = parsedArgs.preset; preset = parsedArgs.preset;
@ -1032,7 +1033,7 @@ async function determineNodeOptions(
let linter: undefined | 'none' | 'eslint'; let linter: undefined | 'none' | 'eslint';
let formatter: undefined | 'none' | 'prettier'; let formatter: undefined | 'none' | 'prettier';
let unitTestRunner: undefined | 'none' | 'jest' = undefined; let unitTestRunner: undefined | 'none' | 'jest' = undefined;
const workspaces = parsedArgs.workspaces ?? false; const workspaces = parsedArgs.workspaces;
if (parsedArgs.preset) { if (parsedArgs.preset) {
preset = parsedArgs.preset; preset = parsedArgs.preset;

View File

@ -89,8 +89,7 @@ export async function initGeneratorInternal(
schema.addPlugin ??= schema.addPlugin ??=
process.env.NX_ADD_PLUGINS !== 'false' && process.env.NX_ADD_PLUGINS !== 'false' &&
nxJson.useInferencePlugins !== false; nxJson.useInferencePlugins !== false;
schema.addTsPlugin ??= schema.addTsPlugin ??= schema.addPlugin;
schema.addPlugin && process.env.NX_ADD_TS_PLUGIN !== 'false';
if (schema.addTsPlugin) { if (schema.addTsPlugin) {
await addPlugin( await addPlugin(

View File

@ -62,10 +62,6 @@ export async function libraryGeneratorInternal(host: Tree, schema: Schema) {
const options = await normalizeOptions(host, schema); const options = await normalizeOptions(host, schema);
if (options.isUsingTsSolutionConfig) {
await addProjectToTsSolutionWorkspace(host, options.projectRoot);
}
if (options.publishable === true && !schema.importPath) { if (options.publishable === true && !schema.importPath) {
throw new Error( throw new Error(
`For publishable libs you have to provide a proper "--importPath" which needs to be a valid npm package name (e.g. my-awesome-lib or @myorg/my-lib)` `For publishable libs you have to provide a proper "--importPath" which needs to be a valid npm package name (e.g. my-awesome-lib or @myorg/my-lib)`
@ -112,6 +108,10 @@ export async function libraryGeneratorInternal(host: Tree, schema: Schema) {
createFiles(host, options); createFiles(host, options);
if (options.isUsingTsSolutionConfig) {
await addProjectToTsSolutionWorkspace(host, options.projectRoot);
}
const lintTask = await addLinting(host, options); const lintTask = await addLinting(host, options);
tasks.push(lintTask); tasks.push(lintTask);

View File

@ -99,7 +99,7 @@ export function generatePreset(host: Tree, opts: NormalizedSchema) {
opts.prefix !== undefined ? `--prefix=${opts.prefix}` : null, opts.prefix !== undefined ? `--prefix=${opts.prefix}` : null,
opts.nxCloudToken ? `--nxCloudToken=${opts.nxCloudToken}` : null, opts.nxCloudToken ? `--nxCloudToken=${opts.nxCloudToken}` : null,
opts.formatter ? `--formatter=${opts.formatter}` : null, opts.formatter ? `--formatter=${opts.formatter}` : null,
opts.workspaces ? `--workspaces` : null, opts.workspaces !== false ? `--workspaces` : `--no-workspaces`,
].filter((e) => !!e); ].filter((e) => !!e);
} }
} }

View File

@ -85,6 +85,7 @@ describe('@nx/workspace:generateWorkspaceFiles', () => {
appName, appName,
isCustomPreset: false, isCustomPreset: false,
nxCloud: nxCloud, nxCloud: nxCloud,
workspaces: true,
}); });
await formatFiles(tree); await formatFiles(tree);
const dir = join(__dirname, 'tmp', `${preset}-${nxCloud}`); const dir = join(__dirname, 'tmp', `${preset}-${nxCloud}`);

View File

@ -275,8 +275,8 @@ function createFiles(tree: Tree, options: NormalizedSchema) {
options.preset === Preset.TsStandalone options.preset === Preset.TsStandalone
? './files-root-app' ? './files-root-app'
: (options.preset === Preset.TS && : (options.preset === Preset.TS &&
process.env.NX_ADD_PLUGINS !== 'false' && options.workspaces &&
process.env.NX_ADD_TS_PLUGIN !== 'false') || process.env.NX_ADD_PLUGINS !== 'false') ||
options.preset === Preset.NPM options.preset === Preset.NPM
? './files-package-based-repo' ? './files-package-based-repo'
: './files-integrated-repo'; : './files-integrated-repo';
@ -294,7 +294,7 @@ function createFiles(tree: Tree, options: NormalizedSchema) {
async function createReadme( async function createReadme(
tree: Tree, tree: Tree,
{ name, appName, directory, preset, nxCloud }: NormalizedSchema, { name, appName, directory, preset, nxCloud, workspaces }: NormalizedSchema,
nxCloudToken?: string nxCloudToken?: string
) { ) {
const formattedNames = names(name); const formattedNames = names(name);
@ -314,8 +314,7 @@ async function createReadme(
isJsStandalone: preset === Preset.TsStandalone, isJsStandalone: preset === Preset.TsStandalone,
isTsPreset: preset === Preset.TS, isTsPreset: preset === Preset.TS,
isUsingNewTsSolutionSetup: isUsingNewTsSolutionSetup:
process.env.NX_ADD_PLUGINS !== 'false' && process.env.NX_ADD_PLUGINS !== 'false' && workspaces,
process.env.NX_ADD_TS_PLUGIN !== 'false',
isEmptyRepo: !appName, isEmptyRepo: !appName,
appName, appName,
generateAppCmd: presetInfo.generateAppCmd, generateAppCmd: presetInfo.generateAppCmd,
@ -416,7 +415,7 @@ function setUpWorkspacesInPackageJson(tree: Tree, options: NormalizedSchema) {
options.preset === Preset.NPM || options.preset === Preset.NPM ||
(options.preset === Preset.TS && (options.preset === Preset.TS &&
process.env.NX_ADD_PLUGINS !== 'false' && process.env.NX_ADD_PLUGINS !== 'false' &&
process.env.NX_ADD_TS_PLUGIN !== 'false') || options.workspaces) ||
((options.preset === Preset.Expo || ((options.preset === Preset.Expo ||
options.preset === Preset.NextJs || options.preset === Preset.NextJs ||
options.preset === Preset.ReactMonorepo || options.preset === Preset.ReactMonorepo ||

View File

@ -105,7 +105,7 @@
"workspaces": { "workspaces": {
"description": "Whether to use package manager workspaces.", "description": "Whether to use package manager workspaces.",
"type": "boolean", "type": "boolean",
"default": false "default": true
} }
}, },
"additionalProperties": true "additionalProperties": true

View File

@ -309,9 +309,7 @@ async function createPreset(tree: Tree, options: Schema) {
const { initGenerator } = require('@nx' + '/js'); const { initGenerator } = require('@nx' + '/js');
return initGenerator(tree, { return initGenerator(tree, {
formatter: options.formatter, formatter: options.formatter,
addTsPlugin: addTsPlugin: process.env.NX_ADD_PLUGINS !== 'false' && options.workspaces,
process.env.NX_ADD_PLUGINS !== 'false' &&
process.env.NX_ADD_TS_PLUGIN !== 'false',
}); });
} else if (options.preset === Preset.TsStandalone) { } else if (options.preset === Preset.TsStandalone) {
const { libraryGenerator } = require('@nx' + '/js'); const { libraryGenerator } = require('@nx' + '/js');

View File

@ -122,7 +122,7 @@
"workspaces": { "workspaces": {
"description": "Whether to use package manager workspaces.", "description": "Whether to use package manager workspaces.",
"type": "boolean", "type": "boolean",
"default": false "default": true
} }
}, },
"required": ["preset", "name"] "required": ["preset", "name"]