diff --git a/packages/angular/cli/src/command-builder/schematics-command-module.ts b/packages/angular/cli/src/command-builder/schematics-command-module.ts index 529d47b078f1..ef317700d1a6 100644 --- a/packages/angular/cli/src/command-builder/schematics-command-module.ts +++ b/packages/angular/cli/src/command-builder/schematics-command-module.ts @@ -204,13 +204,20 @@ export abstract class SchematicsCommandModule ? { name: item, value: item, - checked: item === definition.default, + checked: + definition.multiselect && Array.isArray(definition.default) + ? definition.default?.includes(item) + : item === definition.default, } : { ...item, name: item.label, value: item.value, - checked: item.value === definition.default, + checked: + definition.multiselect && Array.isArray(definition.default) + ? // eslint-disable-next-line @typescript-eslint/no-explicit-any + definition.default?.includes(item.value as any) + : item.value === definition.default, }, ), }); diff --git a/packages/angular/cli/src/command-builder/utilities/json-schema.ts b/packages/angular/cli/src/command-builder/utilities/json-schema.ts index 90c619dc024e..869cab6abe4d 100644 --- a/packages/angular/cli/src/command-builder/utilities/json-schema.ts +++ b/packages/angular/cli/src/command-builder/utilities/json-schema.ts @@ -197,15 +197,19 @@ export async function parseJsonSchemaToOptions( .filter((value) => isValidTypeForEnum(typeof value)) .sort() as (string | true | number)[]; - let defaultValue: string | number | boolean | undefined = undefined; + let defaultValue: string | number | boolean | unknown[] | undefined = undefined; if (current.default !== undefined) { switch (types[0]) { case 'string': - case 'array': if (typeof current.default == 'string') { defaultValue = current.default; } break; + case 'array': + if (Array.isArray(current.default)) { + defaultValue = current.default; + } + break; case 'number': if (typeof current.default == 'number') { defaultValue = current.default; diff --git a/packages/angular/cli/src/command-builder/utilities/json-schema_spec.ts b/packages/angular/cli/src/command-builder/utilities/json-schema_spec.ts index cc86cc99dddc..ea7043339d65 100644 --- a/packages/angular/cli/src/command-builder/utilities/json-schema_spec.ts +++ b/packages/angular/cli/src/command-builder/utilities/json-schema_spec.ts @@ -37,7 +37,7 @@ describe('parseJsonSchemaToOptions', () => { }, 'arrayWithChoices': { 'type': 'array', - 'default': 'default-array', + 'default': ['default-array'], 'items': { 'type': 'string', 'enum': ['always', 'never', 'default-array'], @@ -91,7 +91,7 @@ describe('parseJsonSchemaToOptions', () => { }); it('should add default value to help', async () => { - expect(await localYargs.getHelp()).toContain('[default: "default-array"]'); + expect(await localYargs.getHelp()).toContain('[default: ["default-array"]]'); }); }); diff --git a/packages/angular_devkit/build_angular/src/builders/browser/specs/unused-files-warning_spec.ts b/packages/angular_devkit/build_angular/src/builders/browser/specs/unused-files-warning_spec.ts index 6e08bedfb5b6..b25d599f18a4 100644 --- a/packages/angular_devkit/build_angular/src/builders/browser/specs/unused-files-warning_spec.ts +++ b/packages/angular_devkit/build_angular/src/builders/browser/specs/unused-files-warning_spec.ts @@ -238,7 +238,7 @@ describe('Browser Builder unused files warnings', () => { host.appendToFile('src/main.ts', ''); break; case 2: - // The second should should have type.ts as unused but shouldn't warn. + // The second should have type.ts as unused but shouldn't warn. expect(logs.join().includes(warningMessageSuffix)).toBe( false, `Case ${buildNumber} failed.`, diff --git a/packages/angular_devkit/schematics_cli/bin/schematics.ts b/packages/angular_devkit/schematics_cli/bin/schematics.ts index 2c71e0698ad4..8e9779728a77 100644 --- a/packages/angular_devkit/schematics_cli/bin/schematics.ts +++ b/packages/angular_devkit/schematics_cli/bin/schematics.ts @@ -93,7 +93,6 @@ function _createPromptProvider(): schema.PromptProvider { definition.multiselect ? prompts.checkbox : prompts.select )({ message: definition.message, - default: definition.default, validate: (values) => { if (!definition.validator) { return true; @@ -101,15 +100,26 @@ function _createPromptProvider(): schema.PromptProvider { return definition.validator(Object.values(values).map(({ value }) => value)); }, - choices: definition.items.map((item) => + default: definition.multiselect ? undefined : definition.default, + choices: definition.items?.map((item) => typeof item == 'string' ? { name: item, value: item, + checked: + definition.multiselect && Array.isArray(definition.default) + ? definition.default?.includes(item) + : item === definition.default, } : { + ...item, name: item.label, value: item.value, + checked: + definition.multiselect && Array.isArray(definition.default) + ? // eslint-disable-next-line @typescript-eslint/no-explicit-any + definition.default?.includes(item.value as any) + : item.value === definition.default, }, ), }); diff --git a/packages/ngtools/webpack/src/transformers/replace_resources_spec.ts b/packages/ngtools/webpack/src/transformers/replace_resources_spec.ts index e4c905f86430..102d6e1e0879 100644 --- a/packages/ngtools/webpack/src/transformers/replace_resources_spec.ts +++ b/packages/ngtools/webpack/src/transformers/replace_resources_spec.ts @@ -105,7 +105,7 @@ describe('@ngtools/webpack transformers', () => { expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`); }); - it('should should support svg as templates', () => { + it('should support svg as templates', () => { const input = tags.stripIndent` import { Component } from '@angular/core'; diff --git a/packages/schematics/angular/ai-config/index.ts b/packages/schematics/angular/ai-config/index.ts index 6790bc4a1c1d..4d234b35e5d4 100644 --- a/packages/schematics/angular/ai-config/index.ts +++ b/packages/schematics/angular/ai-config/index.ts @@ -19,9 +19,7 @@ import { } from '@angular-devkit/schematics'; import { Schema as ConfigOptions, Tool } from './schema'; -type ToolWithoutNone = Exclude; - -const AI_TOOLS: { [key in ToolWithoutNone]: ContextFileInfo } = { +const AI_TOOLS: { [key in Exclude]: ContextFileInfo } = { gemini: { rulesName: 'GEMINI.md', directory: '.gemini', @@ -57,26 +55,25 @@ interface ContextFileInfo { } export default function ({ tool }: ConfigOptions): Rule { - if (!tool || tool.includes(Tool.None)) { + if (!tool) { return noop(); } - const files: ContextFileInfo[] = (tool as ToolWithoutNone[]).map( - (selectedTool) => AI_TOOLS[selectedTool], - ); - - const rules = files.map(({ rulesName, directory, frontmatter }) => - mergeWith( - apply(url('./files'), [ - applyTemplates({ - ...strings, - rulesName, - frontmatter, - }), - move(directory), - ]), - ), - ); + const rules = tool + .filter((tool) => tool !== Tool.None) + .map((selectedTool) => AI_TOOLS[selectedTool]) + .map(({ rulesName, directory, frontmatter }) => + mergeWith( + apply(url('./files'), [ + applyTemplates({ + ...strings, + rulesName, + frontmatter, + }), + move(directory), + ]), + ), + ); return chain(rules); } diff --git a/packages/schematics/angular/ai-config/index_spec.ts b/packages/schematics/angular/ai-config/index_spec.ts index 45518f7d17d6..d21186be408a 100644 --- a/packages/schematics/angular/ai-config/index_spec.ts +++ b/packages/schematics/angular/ai-config/index_spec.ts @@ -72,13 +72,14 @@ describe('Ai Config Schematic', () => { expect(tree.exists('.cursor/rules/cursor.mdc')).toBeTruthy(); }); - it('should error is None is associated with other values', () => { - return expectAsync(runConfigSchematic([ConfigTool.None, ConfigTool.Cursor])).toBeRejected(); - }); - it('should not create any files if None is selected', async () => { const filesCount = workspaceTree.files.length; const tree = await runConfigSchematic([ConfigTool.None]); expect(tree.files.length).toBe(filesCount); }); + + it('should create for tool if None and Gemini are selected', async () => { + const tree = await runConfigSchematic([ConfigTool.Gemini, ConfigTool.None]); + expect(tree.exists('.gemini/GEMINI.md')).toBeTruthy(); + }); }); diff --git a/packages/schematics/angular/ai-config/schema.json b/packages/schematics/angular/ai-config/schema.json index 8595f9f7c558..3f46fbc6dede 100644 --- a/packages/schematics/angular/ai-config/schema.json +++ b/packages/schematics/angular/ai-config/schema.json @@ -9,7 +9,7 @@ "tool": { "type": "array", "uniqueItems": true, - "default": "none", + "default": ["none"], "x-prompt": { "message": "Which AI tools do you want to configure with Angular best practices? https://angular.dev/ai/develop-with-ai", "type": "list", @@ -50,21 +50,5 @@ "enum": ["none", "gemini", "copilot", "claude", "cursor", "jetbrains", "windsurf"] } } - }, - "if": { - "properties": { - "tool": { - "contains": { - "const": "none" - } - } - } - }, - "then": { - "properties": { - "tool": { - "maxItems": 1 - } - } } } diff --git a/packages/schematics/angular/ng-new/index_spec.ts b/packages/schematics/angular/ng-new/index_spec.ts index 0b0334ba3432..0d202bffa590 100644 --- a/packages/schematics/angular/ng-new/index_spec.ts +++ b/packages/schematics/angular/ng-new/index_spec.ts @@ -58,7 +58,7 @@ describe('Ng New Schematic', () => { ); }); - it('should should set the prefix in angular.json and in app.ts', async () => { + it('should set the prefix in angular.json and in app.ts', async () => { const options = { ...defaultOptions, prefix: 'pre' }; const tree = await schematicRunner.runSchematic('ng-new', options);