Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('parseJsonSchemaToOptions', () => {
},
'arrayWithChoices': {
'type': 'array',
'default': 'default-array',
'default': ['default-array'],
'items': {
'type': 'string',
'enum': ['always', 'never', 'default-array'],
Expand Down Expand Up @@ -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"]]');
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.`,
Expand Down
14 changes: 12 additions & 2 deletions packages/angular_devkit/schematics_cli/bin/schematics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,23 +93,33 @@ function _createPromptProvider(): schema.PromptProvider {
definition.multiselect ? prompts.checkbox : prompts.select
)({
message: definition.message,
default: definition.default,
validate: (values) => {
if (!definition.validator) {
return true;
}

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,
},
),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
37 changes: 17 additions & 20 deletions packages/schematics/angular/ai-config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ import {
} from '@angular-devkit/schematics';
import { Schema as ConfigOptions, Tool } from './schema';

type ToolWithoutNone = Exclude<Tool, Tool.None>;

const AI_TOOLS: { [key in ToolWithoutNone]: ContextFileInfo } = {
const AI_TOOLS: { [key in Exclude<Tool, Tool.None>]: ContextFileInfo } = {
gemini: {
rulesName: 'GEMINI.md',
directory: '.gemini',
Expand Down Expand Up @@ -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);
}
9 changes: 5 additions & 4 deletions packages/schematics/angular/ai-config/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
18 changes: 1 addition & 17 deletions packages/schematics/angular/ai-config/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -50,21 +50,5 @@
"enum": ["none", "gemini", "copilot", "claude", "cursor", "jetbrains", "windsurf"]
}
}
},
"if": {
"properties": {
"tool": {
"contains": {
"const": "none"
}
}
}
},
"then": {
"properties": {
"tool": {
"maxItems": 1
}
}
}
}
2 changes: 1 addition & 1 deletion packages/schematics/angular/ng-new/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down