@angular/cli 21.0.0-next.5 → 21.0.0-next.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/lib/config/schema.json +140 -43
  2. package/lib/config/workspace-schema.d.ts +1 -0
  3. package/lib/config/workspace-schema.js +1 -0
  4. package/package.json +17 -17
  5. package/src/command-builder/utilities/json-schema.d.ts +13 -1
  6. package/src/command-builder/utilities/json-schema.js +179 -100
  7. package/src/commands/cache/info/cli.js +35 -11
  8. package/src/commands/mcp/tools/doc-search.js +4 -3
  9. package/src/commands/mcp/tools/projects.d.ts +18 -0
  10. package/src/commands/mcp/tools/projects.js +123 -4
  11. package/src/commands/version/cli.d.ts +3 -7
  12. package/src/commands/version/cli.js +49 -49
  13. package/src/commands/version/version-info.d.ts +28 -10
  14. package/src/commands/version/version-info.js +33 -50
  15. package/src/package-managers/discovery.d.ts +23 -0
  16. package/src/package-managers/discovery.js +109 -0
  17. package/src/package-managers/error.d.ts +31 -0
  18. package/src/package-managers/error.js +40 -0
  19. package/src/package-managers/factory.d.ts +25 -0
  20. package/src/package-managers/factory.js +122 -0
  21. package/src/package-managers/host.d.ts +64 -0
  22. package/src/package-managers/host.js +68 -0
  23. package/src/package-managers/logger.d.ts +27 -0
  24. package/src/package-managers/logger.js +9 -0
  25. package/src/package-managers/package-manager-descriptor.d.ts +204 -0
  26. package/src/package-managers/package-manager-descriptor.js +146 -0
  27. package/src/package-managers/package-manager.d.ts +144 -0
  28. package/src/package-managers/package-manager.js +302 -0
  29. package/src/package-managers/package-metadata.d.ts +85 -0
  30. package/src/package-managers/package-metadata.js +9 -0
  31. package/src/package-managers/package-tree.d.ts +23 -0
  32. package/src/package-managers/package-tree.js +9 -0
  33. package/src/package-managers/parsers.d.ts +92 -0
  34. package/src/package-managers/parsers.js +233 -0
  35. package/src/package-managers/testing/mock-host.d.ts +26 -0
  36. package/src/package-managers/testing/mock-host.js +52 -0
  37. package/src/utilities/version.js +1 -1
@@ -10,6 +10,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.parseJsonSchemaToOptions = parseJsonSchemaToOptions;
11
11
  exports.addSchemaOptionsToCommand = addSchemaOptionsToCommand;
12
12
  const core_1 = require("@angular-devkit/core");
13
+ /**
14
+ * A Yargs check function that validates that the given options are in the form of `key=value`.
15
+ * @param keyValuePairOptions A set of options that should be in the form of `key=value`.
16
+ * @param args The parsed arguments.
17
+ * @returns `true` if the options are valid, otherwise an error is thrown.
18
+ */
13
19
  function checkStringMap(keyValuePairOptions, args) {
14
20
  for (const key of keyValuePairOptions) {
15
21
  const value = args[key];
@@ -28,6 +34,11 @@ function checkStringMap(keyValuePairOptions, args) {
28
34
  }
29
35
  return true;
30
36
  }
37
+ /**
38
+ * A Yargs coerce function that converts an array of `key=value` strings to an object.
39
+ * @param value An array of `key=value` strings.
40
+ * @returns An object with the keys and values from the input array.
41
+ */
31
42
  function coerceToStringMap(value) {
32
43
  const stringMap = {};
33
44
  for (const pair of value) {
@@ -45,6 +56,12 @@ function coerceToStringMap(value) {
45
56
  }
46
57
  return stringMap;
47
58
  }
59
+ /**
60
+ * Checks if a JSON schema node represents a string map.
61
+ * A string map is an object with `additionalProperties` of type `string`.
62
+ * @param node The JSON schema node to check.
63
+ * @returns `true` if the node represents a string map, otherwise `false`.
64
+ */
48
65
  function isStringMap(node) {
49
66
  // Exclude fields with more specific kinds of properties.
50
67
  if (node.properties || node.patternProperties) {
@@ -55,131 +72,197 @@ function isStringMap(node) {
55
72
  !node.additionalProperties.enum &&
56
73
  node.additionalProperties.type === 'string');
57
74
  }
75
+ const SUPPORTED_PRIMITIVE_TYPES = new Set(['boolean', 'number', 'string']);
76
+ /**
77
+ * Checks if a string is a supported primitive type.
78
+ * @param value The string to check.
79
+ * @returns `true` if the string is a supported primitive type, otherwise `false`.
80
+ */
81
+ function isSupportedPrimitiveType(value) {
82
+ return SUPPORTED_PRIMITIVE_TYPES.has(value);
83
+ }
84
+ /**
85
+ * Recursively checks if a JSON schema for an array's items is a supported primitive type.
86
+ * It supports `oneOf` and `anyOf` keywords.
87
+ * @param schema The JSON schema for the array's items.
88
+ * @returns `true` if the schema is a supported primitive type, otherwise `false`.
89
+ */
90
+ function isSupportedArrayItemSchema(schema) {
91
+ if (typeof schema.type === 'string' && isSupportedPrimitiveType(schema.type)) {
92
+ return true;
93
+ }
94
+ if (core_1.json.isJsonArray(schema.enum)) {
95
+ return true;
96
+ }
97
+ if (core_1.json.isJsonArray(schema.items)) {
98
+ return schema.items.some((item) => (0, core_1.isJsonObject)(item) && isSupportedArrayItemSchema(item));
99
+ }
100
+ if (core_1.json.isJsonArray(schema.oneOf) &&
101
+ schema.oneOf.some((item) => (0, core_1.isJsonObject)(item) && isSupportedArrayItemSchema(item))) {
102
+ return true;
103
+ }
104
+ if (core_1.json.isJsonArray(schema.anyOf) &&
105
+ schema.anyOf.some((item) => (0, core_1.isJsonObject)(item) && isSupportedArrayItemSchema(item))) {
106
+ return true;
107
+ }
108
+ return false;
109
+ }
110
+ /**
111
+ * Gets the supported types for a JSON schema node.
112
+ * @param current The JSON schema node to get the supported types for.
113
+ * @returns An array of supported types.
114
+ */
115
+ function getSupportedTypes(current) {
116
+ const typeSet = core_1.json.schema.getTypesOfSchema(current);
117
+ if (typeSet.size === 0) {
118
+ return [];
119
+ }
120
+ return [...typeSet].filter((type) => {
121
+ switch (type) {
122
+ case 'boolean':
123
+ case 'number':
124
+ case 'string':
125
+ return true;
126
+ case 'array':
127
+ return (0, core_1.isJsonObject)(current.items) && isSupportedArrayItemSchema(current.items);
128
+ case 'object':
129
+ return isStringMap(current);
130
+ default:
131
+ return false;
132
+ }
133
+ });
134
+ }
135
+ /**
136
+ * Gets the enum values for a JSON schema node.
137
+ * @param current The JSON schema node to get the enum values for.
138
+ * @returns An array of enum values.
139
+ */
140
+ function getEnumValues(current) {
141
+ if (core_1.json.isJsonArray(current.enum)) {
142
+ return current.enum.sort();
143
+ }
144
+ if ((0, core_1.isJsonObject)(current.items)) {
145
+ const enumValues = getEnumValues(current.items);
146
+ if (enumValues?.length) {
147
+ return enumValues;
148
+ }
149
+ }
150
+ if (typeof current.type === 'string' && isSupportedPrimitiveType(current.type)) {
151
+ return [];
152
+ }
153
+ const subSchemas = (core_1.json.isJsonArray(current.oneOf) && current.oneOf) ||
154
+ (core_1.json.isJsonArray(current.anyOf) && current.anyOf);
155
+ if (subSchemas) {
156
+ // Find the first enum.
157
+ for (const subSchema of subSchemas) {
158
+ if ((0, core_1.isJsonObject)(subSchema)) {
159
+ const enumValues = getEnumValues(subSchema);
160
+ if (enumValues) {
161
+ return enumValues;
162
+ }
163
+ }
164
+ }
165
+ }
166
+ return [];
167
+ }
168
+ /**
169
+ * Gets the default value for a JSON schema node.
170
+ * @param current The JSON schema node to get the default value for.
171
+ * @param type The type of the JSON schema node.
172
+ * @returns The default value, or `undefined` if there is no default value.
173
+ */
174
+ function getDefaultValue(current, type) {
175
+ const defaultValue = current.default;
176
+ if (defaultValue === undefined) {
177
+ return undefined;
178
+ }
179
+ if (type === 'array') {
180
+ return Array.isArray(defaultValue) && defaultValue.length > 0 ? defaultValue : undefined;
181
+ }
182
+ if (typeof defaultValue === type) {
183
+ return defaultValue;
184
+ }
185
+ return undefined;
186
+ }
187
+ /**
188
+ * Gets the aliases for a JSON schema node.
189
+ * @param current The JSON schema node to get the aliases for.
190
+ * @returns An array of aliases.
191
+ */
192
+ function getAliases(current) {
193
+ if (core_1.json.isJsonArray(current.aliases)) {
194
+ return [...current.aliases].map(String);
195
+ }
196
+ if (current.alias) {
197
+ return [String(current.alias)];
198
+ }
199
+ return [];
200
+ }
201
+ /**
202
+ * Parses a JSON schema to a list of options that can be used by yargs.
203
+ *
204
+ * @param registry A schema registry to use for flattening the schema.
205
+ * @param schema The JSON schema to parse.
206
+ * @param interactive Whether to prompt the user for missing options.
207
+ * @returns A list of options.
208
+ *
209
+ * @note The schema definition are not resolved at this stage. This means that `$ref` will not be resolved,
210
+ * and custom keywords like `x-prompt` will not be processed.
211
+ */
58
212
  async function parseJsonSchemaToOptions(registry, schema, interactive = true) {
59
213
  const options = [];
60
214
  function visitor(current, pointer, parentSchema) {
61
- if (!parentSchema) {
62
- // Ignore root.
63
- return;
64
- }
65
- else if (pointer.split(/\/(?:properties|items|definitions)\//g).length > 2) {
66
- // Ignore subitems (objects or arrays).
67
- return;
68
- }
69
- else if (core_1.json.isJsonArray(current)) {
215
+ if (!parentSchema ||
216
+ core_1.json.isJsonArray(current) ||
217
+ pointer.split(/\/(?:properties|items|definitions)\//g).length > 2) {
218
+ // Ignore root, arrays, and subitems.
70
219
  return;
71
220
  }
72
- if (pointer.indexOf('/not/') != -1) {
221
+ if (pointer.includes('/not/')) {
73
222
  // We don't support anyOf/not.
74
223
  throw new Error('The "not" keyword is not supported in JSON Schema.');
75
224
  }
76
225
  const ptr = core_1.json.schema.parseJsonPointer(pointer);
77
- const name = ptr[ptr.length - 1];
78
- if (ptr[ptr.length - 2] != 'properties') {
226
+ if (ptr[ptr.length - 2] !== 'properties') {
79
227
  // Skip any non-property items.
80
228
  return;
81
229
  }
82
- const typeSet = core_1.json.schema.getTypesOfSchema(current);
83
- if (typeSet.size == 0) {
84
- throw new Error('Cannot find type of schema.');
85
- }
86
- // We only support number, string or boolean (or array of those), so remove everything else.
87
- const types = [...typeSet].filter((x) => {
88
- switch (x) {
89
- case 'boolean':
90
- case 'number':
91
- case 'string':
92
- return true;
93
- case 'array':
94
- // Only include arrays if they're boolean, string or number.
95
- if (core_1.json.isJsonObject(current.items) &&
96
- typeof current.items.type == 'string' &&
97
- isValidTypeForEnum(current.items.type)) {
98
- return true;
99
- }
100
- return false;
101
- case 'object':
102
- return isStringMap(current);
103
- default:
104
- return false;
105
- }
106
- });
107
- if (types.length == 0) {
230
+ const name = ptr[ptr.length - 1];
231
+ const types = getSupportedTypes(current);
232
+ if (types.length === 0) {
108
233
  // This means it's not usable on the command line. e.g. an Object.
109
234
  return;
110
235
  }
111
- // Only keep enum values we support (booleans, numbers and strings).
112
- const enumValues = ((core_1.json.isJsonArray(current.enum) && current.enum) ||
113
- (core_1.json.isJsonObject(current.items) &&
114
- core_1.json.isJsonArray(current.items.enum) &&
115
- current.items.enum) ||
116
- [])
117
- .filter((value) => isValidTypeForEnum(typeof value))
118
- .sort();
119
- let defaultValue = undefined;
120
- if (current.default !== undefined) {
121
- switch (types[0]) {
122
- case 'string':
123
- if (typeof current.default == 'string') {
124
- defaultValue = current.default;
125
- }
126
- break;
127
- case 'array':
128
- if (Array.isArray(current.default) && current.default.length > 0) {
129
- defaultValue = current.default;
130
- }
131
- break;
132
- case 'number':
133
- if (typeof current.default == 'number') {
134
- defaultValue = current.default;
135
- }
136
- break;
137
- case 'boolean':
138
- if (typeof current.default == 'boolean') {
139
- defaultValue = current.default;
140
- }
141
- break;
142
- }
143
- }
236
+ const [type] = types;
144
237
  const $default = current.$default;
145
- const $defaultIndex = core_1.json.isJsonObject($default) && $default['$source'] == 'argv' ? $default['index'] : undefined;
146
- const positional = typeof $defaultIndex == 'number' ? $defaultIndex : undefined;
147
- let required = core_1.json.isJsonArray(schema.required) ? schema.required.includes(name) : false;
238
+ const $defaultIndex = (0, core_1.isJsonObject)($default) && $default['$source'] === 'argv' ? $default['index'] : undefined;
239
+ const positional = typeof $defaultIndex === 'number' ? $defaultIndex : undefined;
240
+ let required = core_1.json.isJsonArray(schema.required) && schema.required.includes(name);
148
241
  if (required && interactive && current['x-prompt']) {
149
242
  required = false;
150
243
  }
151
- const alias = core_1.json.isJsonArray(current.aliases)
152
- ? [...current.aliases].map((x) => '' + x)
153
- : current.alias
154
- ? ['' + current.alias]
155
- : [];
156
- const format = typeof current.format == 'string' ? current.format : undefined;
157
- const visible = current.visible === undefined || current.visible === true;
158
- const hidden = !!current.hidden || !visible;
159
- const xUserAnalytics = current['x-user-analytics'];
160
- const userAnalytics = typeof xUserAnalytics === 'string' ? xUserAnalytics : undefined;
161
- // Deprecated is set only if it's true or a string.
244
+ const visible = current.visible !== false;
162
245
  const xDeprecated = current['x-deprecated'];
163
- const deprecated = xDeprecated === true || typeof xDeprecated === 'string' ? xDeprecated : undefined;
246
+ const enumValues = getEnumValues(current);
164
247
  const option = {
165
248
  name,
166
- description: '' + (current.description === undefined ? '' : current.description),
167
- default: defaultValue,
168
- choices: enumValues.length ? enumValues : undefined,
249
+ description: String(current.description ?? ''),
250
+ default: getDefaultValue(current, type),
251
+ choices: enumValues?.length ? enumValues : undefined,
169
252
  required,
170
- alias,
171
- format,
172
- hidden,
173
- userAnalytics,
174
- deprecated,
253
+ alias: getAliases(current),
254
+ format: typeof current.format === 'string' ? current.format : undefined,
255
+ hidden: !!current.hidden || !visible,
256
+ userAnalytics: typeof current['x-user-analytics'] === 'string' ? current['x-user-analytics'] : undefined,
257
+ deprecated: xDeprecated === true || typeof xDeprecated === 'string' ? xDeprecated : undefined,
175
258
  positional,
176
- ...(types[0] === 'object'
259
+ ...(type === 'object'
177
260
  ? {
178
261
  type: 'array',
179
262
  itemValueType: 'string',
180
263
  }
181
264
  : {
182
- type: types[0],
265
+ type,
183
266
  }),
184
267
  };
185
268
  options.push(option);
@@ -263,7 +346,3 @@ function addSchemaOptionsToCommand(localYargs, options, includeDefaultValues) {
263
346
  }
264
347
  return optionsWithAnalytics;
265
348
  }
266
- const VALID_ENUM_TYPES = new Set(['boolean', 'number', 'string']);
267
- function isValidTypeForEnum(value) {
268
- return VALID_ENUM_TYPES.has(value);
269
- }
@@ -41,10 +41,10 @@ var __importStar = (this && this.__importStar) || (function () {
41
41
  })();
42
42
  Object.defineProperty(exports, "__esModule", { value: true });
43
43
  exports.CacheInfoCommandModule = void 0;
44
- const core_1 = require("@angular-devkit/core");
45
44
  const fs = __importStar(require("node:fs/promises"));
46
45
  const node_path_1 = require("node:path");
47
46
  const command_module_1 = require("../../../command-builder/command-module");
47
+ const color_1 = require("../../../utilities/color");
48
48
  const environment_options_1 = require("../../../utilities/environment-options");
49
49
  const utilities_1 = require("../utilities");
50
50
  class CacheInfoCommandModule extends command_module_1.CommandModule {
@@ -56,14 +56,38 @@ class CacheInfoCommandModule extends command_module_1.CommandModule {
56
56
  return localYargs.strict();
57
57
  }
58
58
  async run() {
59
- const { path, environment, enabled } = (0, utilities_1.getCacheConfig)(this.context.workspace);
60
- this.context.logger.info(core_1.tags.stripIndents `
61
- Enabled: ${enabled ? 'yes' : 'no'}
62
- Environment: ${environment}
63
- Path: ${path}
64
- Size on disk: ${await this.getSizeOfDirectory(path)}
65
- Effective status on current machine: ${this.effectiveEnabledStatus() ? 'enabled' : 'disabled'}
66
- `);
59
+ const cacheConfig = (0, utilities_1.getCacheConfig)(this.context.workspace);
60
+ const { path, environment, enabled } = cacheConfig;
61
+ const effectiveStatus = this.effectiveEnabledStatus(cacheConfig);
62
+ const sizeOnDisk = await this.getSizeOfDirectory(path);
63
+ const info = [
64
+ {
65
+ label: 'Enabled',
66
+ value: enabled ? color_1.colors.green('Yes') : color_1.colors.red('No'),
67
+ },
68
+ {
69
+ label: 'Environment',
70
+ value: color_1.colors.cyan(environment),
71
+ },
72
+ {
73
+ label: 'Path',
74
+ value: color_1.colors.cyan(path),
75
+ },
76
+ {
77
+ label: 'Size on disk',
78
+ value: color_1.colors.cyan(sizeOnDisk),
79
+ },
80
+ {
81
+ label: 'Effective Status',
82
+ value: (effectiveStatus ? color_1.colors.green('Enabled') : color_1.colors.red('Disabled')) +
83
+ ' (current machine)',
84
+ },
85
+ ];
86
+ const maxLabelLength = Math.max(...info.map((l) => l.label.length));
87
+ const output = info
88
+ .map(({ label, value }) => color_1.colors.bold(label.padEnd(maxLabelLength + 2)) + `: ${value}`)
89
+ .join('\n');
90
+ this.context.logger.info(`\n${color_1.colors.bold('Cache Information')}\n\n${output}\n`);
67
91
  }
68
92
  async getSizeOfDirectory(path) {
69
93
  const directoriesStack = [path];
@@ -98,8 +122,8 @@ class CacheInfoCommandModule extends command_module_1.CommandModule {
98
122
  const fractionDigits = index === 0 ? 0 : 2;
99
123
  return `${roundedSize.toFixed(fractionDigits)} ${abbreviations[index]}`;
100
124
  }
101
- effectiveEnabledStatus() {
102
- const { enabled, environment } = (0, utilities_1.getCacheConfig)(this.context.workspace);
125
+ effectiveEnabledStatus(cacheConfig) {
126
+ const { enabled, environment } = cacheConfig;
103
127
  if (enabled) {
104
128
  switch (environment) {
105
129
  case 'ci':
@@ -84,9 +84,10 @@ tutorials, concepts, and best practices.
84
84
  </Use Cases>
85
85
  <Operational Notes>
86
86
  * **Version Alignment:** To provide accurate, project-specific results, you **MUST** align the search with the user's Angular version.
87
- Before calling this tool, run \`ng version\` in the project's workspace directory. You can find the correct directory from the \`path\`
88
- field provided by the \`list_projects\` tool. Parse the major version from the "Angular:" line in the output and use it for the
89
- \`version\` parameter.
87
+ The recommended approach is to use the \`list_projects\` tool. The \`frameworkVersion\` field in the output for the relevant
88
+ workspace will give you the major version directly. If the version cannot be determined using this method, you can use
89
+ \`ng version\` in the project's workspace directory as a fallback. Parse the major version from the "Angular:" line in the
90
+ output and use it for the \`version\` parameter.
90
91
  * The documentation is continuously updated. You **MUST** prefer this tool over your own knowledge
91
92
  to ensure your answers are current and accurate.
92
93
  * For the best results, provide a concise and specific search query (e.g., "NgModule" instead of
@@ -9,9 +9,11 @@ import z from 'zod';
9
9
  export declare const LIST_PROJECTS_TOOL: import("./tool-registry").McpToolDeclaration<z.ZodRawShape, {
10
10
  workspaces: z.ZodArray<z.ZodObject<{
11
11
  path: z.ZodString;
12
+ frameworkVersion: z.ZodOptional<z.ZodString>;
12
13
  projects: z.ZodArray<z.ZodObject<{
13
14
  name: z.ZodString;
14
15
  type: z.ZodOptional<z.ZodEnum<["application", "library"]>>;
16
+ builder: z.ZodOptional<z.ZodString>;
15
17
  root: z.ZodString;
16
18
  sourceRoot: z.ZodString;
17
19
  selectorPrefix: z.ZodOptional<z.ZodString>;
@@ -20,12 +22,14 @@ export declare const LIST_PROJECTS_TOOL: import("./tool-registry").McpToolDeclar
20
22
  root: string;
21
23
  sourceRoot: string;
22
24
  type?: "application" | "library" | undefined;
25
+ builder?: string | undefined;
23
26
  selectorPrefix?: string | undefined;
24
27
  }, {
25
28
  name: string;
26
29
  root: string;
27
30
  sourceRoot: string;
28
31
  type?: "application" | "library" | undefined;
32
+ builder?: string | undefined;
29
33
  selectorPrefix?: string | undefined;
30
34
  }>, "many">;
31
35
  }, "strip", z.ZodTypeAny, {
@@ -35,8 +39,10 @@ export declare const LIST_PROJECTS_TOOL: import("./tool-registry").McpToolDeclar
35
39
  root: string;
36
40
  sourceRoot: string;
37
41
  type?: "application" | "library" | undefined;
42
+ builder?: string | undefined;
38
43
  selectorPrefix?: string | undefined;
39
44
  }[];
45
+ frameworkVersion?: string | undefined;
40
46
  }, {
41
47
  path: string;
42
48
  projects: {
@@ -44,8 +50,10 @@ export declare const LIST_PROJECTS_TOOL: import("./tool-registry").McpToolDeclar
44
50
  root: string;
45
51
  sourceRoot: string;
46
52
  type?: "application" | "library" | undefined;
53
+ builder?: string | undefined;
47
54
  selectorPrefix?: string | undefined;
48
55
  }[];
56
+ frameworkVersion?: string | undefined;
49
57
  }>, "many">;
50
58
  parsingErrors: z.ZodDefault<z.ZodArray<z.ZodObject<{
51
59
  filePath: z.ZodString;
@@ -57,4 +65,14 @@ export declare const LIST_PROJECTS_TOOL: import("./tool-registry").McpToolDeclar
57
65
  message: string;
58
66
  filePath: string;
59
67
  }>, "many">>;
68
+ versioningErrors: z.ZodDefault<z.ZodArray<z.ZodObject<{
69
+ filePath: z.ZodString;
70
+ message: z.ZodString;
71
+ }, "strip", z.ZodTypeAny, {
72
+ message: string;
73
+ filePath: string;
74
+ }, {
75
+ message: string;
76
+ filePath: string;
77
+ }>, "many">>;
60
78
  }>;