@angular/cli 20.2.0-next.2 → 20.2.0-rc.0

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 (42) hide show
  1. package/lib/code-examples.db +0 -0
  2. package/lib/config/schema.json +18 -2
  3. package/lib/config/workspace-schema.d.ts +14 -0
  4. package/lib/config/workspace-schema.js +11 -1
  5. package/package.json +17 -17
  6. package/src/commands/mcp/cli.d.ts +5 -1
  7. package/src/commands/mcp/cli.js +25 -4
  8. package/src/commands/mcp/mcp-server.d.ts +13 -1
  9. package/src/commands/mcp/mcp-server.js +51 -27
  10. package/src/commands/mcp/{instructions → resources}/best-practices.md +0 -5
  11. package/src/commands/mcp/resources/instructions.d.ts +9 -0
  12. package/src/commands/mcp/resources/instructions.js +28 -0
  13. package/src/commands/mcp/tools/best-practices.d.ts +1 -2
  14. package/src/commands/mcp/tools/best-practices.js +30 -24
  15. package/src/commands/mcp/tools/doc-search.d.ts +5 -9
  16. package/src/commands/mcp/tools/doc-search.js +34 -37
  17. package/src/commands/mcp/tools/examples.d.ts +4 -11
  18. package/src/commands/mcp/tools/examples.js +68 -34
  19. package/src/commands/mcp/tools/modernize.d.ts +31 -0
  20. package/src/commands/mcp/tools/modernize.js +135 -0
  21. package/src/commands/mcp/tools/projects.d.ts +22 -5
  22. package/src/commands/mcp/tools/projects.js +37 -35
  23. package/src/commands/mcp/tools/tool-registry.d.ts +35 -0
  24. package/src/commands/mcp/tools/tool-registry.js +33 -0
  25. package/src/commands/version/cli.d.ts +24 -2
  26. package/src/commands/version/cli.js +74 -115
  27. package/src/commands/version/version-info.d.ts +33 -0
  28. package/src/commands/version/version-info.js +122 -0
  29. package/src/utilities/config.js +3 -0
  30. package/src/utilities/environment-options.d.ts +13 -0
  31. package/src/utilities/environment-options.js +43 -14
  32. package/src/utilities/eol.d.ts +12 -0
  33. package/src/utilities/eol.js +12 -0
  34. package/src/utilities/error.d.ts +8 -0
  35. package/src/utilities/error.js +24 -4
  36. package/src/utilities/json-file.d.ts +15 -2
  37. package/src/utilities/json-file.js +100 -27
  38. package/src/utilities/tty.d.ts +8 -0
  39. package/src/utilities/tty.js +10 -10
  40. package/src/utilities/version.js +1 -1
  41. package/src/utilities/load-esm.d.ts +0 -20
  42. package/src/utilities/load-esm.js +0 -30
@@ -13,34 +13,70 @@ exports.parseJson = parseJson;
13
13
  const jsonc_parser_1 = require("jsonc-parser");
14
14
  const node_fs_1 = require("node:fs");
15
15
  const eol_1 = require("./eol");
16
- /** @internal */
16
+ const error_1 = require("./error");
17
+ /**
18
+ * Represents a JSON file, allowing for reading, modifying, and saving.
19
+ * This class uses `jsonc-parser` to preserve comments and formatting, including
20
+ * indentation and end-of-line sequences.
21
+ * @internal
22
+ */
17
23
  class JSONFile {
18
- path;
19
- content;
20
- eol;
24
+ /** The raw content of the JSON file. */
25
+ #content;
26
+ /** The end-of-line sequence used in the file. */
27
+ #eol;
28
+ /** Whether the file uses spaces for indentation. */
29
+ #insertSpaces = true;
30
+ /** The number of spaces or tabs used for indentation. */
31
+ #tabSize = 2;
32
+ /** The path to the JSON file. */
33
+ #path;
34
+ /** The parsed JSON abstract syntax tree. */
35
+ #jsonAst;
36
+ /** The raw content of the JSON file. */
37
+ get content() {
38
+ return this.#content;
39
+ }
40
+ /**
41
+ * Creates an instance of JSONFile.
42
+ * @param path The path to the JSON file.
43
+ */
21
44
  constructor(path) {
22
- this.path = path;
23
- const buffer = (0, node_fs_1.readFileSync)(this.path);
24
- if (buffer) {
25
- this.content = buffer.toString();
45
+ this.#path = path;
46
+ try {
47
+ this.#content = (0, node_fs_1.readFileSync)(this.#path, 'utf-8');
26
48
  }
27
- else {
28
- throw new Error(`Could not read '${path}'.`);
49
+ catch (e) {
50
+ (0, error_1.assertIsError)(e);
51
+ // We don't have to worry about ENOENT, since we'll be creating the file.
52
+ if (e.code !== 'ENOENT') {
53
+ throw e;
54
+ }
55
+ this.#content = '';
29
56
  }
30
- this.eol = (0, eol_1.getEOL)(this.content);
57
+ this.#eol = (0, eol_1.getEOL)(this.#content);
58
+ this.#detectIndentation();
31
59
  }
32
- _jsonAst;
60
+ /**
61
+ * Gets the parsed JSON abstract syntax tree.
62
+ * The AST is lazily parsed and cached.
63
+ */
33
64
  get JsonAst() {
34
- if (this._jsonAst) {
35
- return this._jsonAst;
65
+ if (this.#jsonAst) {
66
+ return this.#jsonAst;
36
67
  }
37
68
  const errors = [];
38
- this._jsonAst = (0, jsonc_parser_1.parseTree)(this.content, errors, { allowTrailingComma: true });
69
+ this.#jsonAst = (0, jsonc_parser_1.parseTree)(this.#content, errors, { allowTrailingComma: true });
39
70
  if (errors.length) {
40
- formatError(this.path, errors);
71
+ formatError(this.#path, errors);
41
72
  }
42
- return this._jsonAst;
73
+ return this.#jsonAst;
43
74
  }
75
+ /**
76
+ * Gets a value from the JSON file at a specific path.
77
+ * @param jsonPath The path to the value.
78
+ * @returns The value at the given path, or `undefined` if not found.
79
+ */
44
80
  get(jsonPath) {
45
81
  const jsonAstNode = this.JsonAst;
46
82
  if (!jsonAstNode) {
@@ -52,6 +88,13 @@ class JSONFile {
52
88
  const node = (0, jsonc_parser_1.findNodeAtLocation)(jsonAstNode, jsonPath);
53
89
  return node === undefined ? undefined : (0, jsonc_parser_1.getNodeValue)(node);
54
90
  }
91
+ /**
92
+ * Modifies a value in the JSON file.
93
+ * @param jsonPath The path to the value to modify.
94
+ * @param value The new value to insert.
95
+ * @param insertInOrder A function to determine the insertion index, or `false` to insert at the end.
96
+ * @returns `true` if the modification was successful, `false` otherwise.
97
+ */
55
98
  modify(jsonPath, value, insertInOrder) {
56
99
  if (value === undefined && this.get(jsonPath) === undefined) {
57
100
  // Cannot remove a value which doesn't exist.
@@ -65,28 +108,49 @@ class JSONFile {
65
108
  else if (insertInOrder !== false) {
66
109
  getInsertionIndex = insertInOrder;
67
110
  }
68
- const edits = (0, jsonc_parser_1.modify)(this.content, jsonPath, value, {
111
+ const edits = (0, jsonc_parser_1.modify)(this.#content, jsonPath, value, {
69
112
  getInsertionIndex,
70
- // TODO: use indentation from original file.
71
113
  formattingOptions: {
72
- insertSpaces: true,
73
- tabSize: 2,
74
- eol: this.eol,
114
+ insertSpaces: this.#insertSpaces,
115
+ tabSize: this.#tabSize,
116
+ eol: this.#eol,
75
117
  },
76
118
  });
77
119
  if (edits.length === 0) {
78
120
  return false;
79
121
  }
80
- this.content = (0, jsonc_parser_1.applyEdits)(this.content, edits);
81
- this._jsonAst = undefined;
122
+ this.#content = (0, jsonc_parser_1.applyEdits)(this.#content, edits);
123
+ this.#jsonAst = undefined;
82
124
  return true;
83
125
  }
126
+ /**
127
+ * Deletes a value from the JSON file at a specific path.
128
+ * @param jsonPath The path to the value to delete.
129
+ * @returns `true` if the deletion was successful, `false` otherwise.
130
+ */
131
+ delete(jsonPath) {
132
+ return this.modify(jsonPath, undefined);
133
+ }
134
+ /** Saves the modified content back to the file. */
84
135
  save() {
85
- (0, node_fs_1.writeFileSync)(this.path, this.content);
136
+ (0, node_fs_1.writeFileSync)(this.#path, this.#content);
137
+ }
138
+ /** Detects the indentation of the file. */
139
+ #detectIndentation() {
140
+ // Find the first line that has indentation.
141
+ const match = this.#content.match(/^(?:( )+|\t+)\S/m);
142
+ if (match) {
143
+ this.#insertSpaces = !!match[1];
144
+ this.#tabSize = match[0].length - 1;
145
+ }
86
146
  }
87
147
  }
88
148
  exports.JSONFile = JSONFile;
89
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
149
+ /**
150
+ * Reads and parses a JSON file, supporting comments and trailing commas.
151
+ * @param path The path to the JSON file.
152
+ * @returns The parsed JSON object.
153
+ */
90
154
  function readAndParseJson(path) {
91
155
  const errors = [];
92
156
  const content = (0, jsonc_parser_1.parse)((0, node_fs_1.readFileSync)(path, 'utf-8'), errors, { allowTrailingComma: true });
@@ -95,11 +159,20 @@ function readAndParseJson(path) {
95
159
  }
96
160
  return content;
97
161
  }
162
+ /**
163
+ * Formats a JSON parsing error and throws an exception.
164
+ * @param path The path to the file that failed to parse.
165
+ * @param errors The list of parsing errors.
166
+ */
98
167
  function formatError(path, errors) {
99
168
  const { error, offset } = errors[0];
100
169
  throw new Error(`Failed to parse "${path}" as JSON AST Object. ${(0, jsonc_parser_1.printParseErrorCode)(error)} at location: ${offset}.`);
101
170
  }
102
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
+ /**
172
+ * Parses a JSON string, supporting comments and trailing commas.
173
+ * @param content The JSON string to parse.
174
+ * @returns The parsed JSON object.
175
+ */
103
176
  function parseJson(content) {
104
177
  return (0, jsonc_parser_1.parse)(content, undefined, { allowTrailingComma: true });
105
178
  }
@@ -5,4 +5,12 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
+ /**
9
+ * Determines if the `stream` is a TTY.
10
+ *
11
+ * @param stream A NodeJS stream to check. Defaults to `process.stdout`.
12
+ * @returns `true` if the `stream` is a TTY, `false` otherwise. This detection is overridden
13
+ * by the `NG_FORCE_TTY` environment variable. In a CI environment, this will also be `false`
14
+ * unless `NG_FORCE_TTY` is set.
15
+ */
8
16
  export declare function isTTY(stream?: NodeJS.WriteStream): boolean;
@@ -8,15 +8,15 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.isTTY = isTTY;
11
- function _isTruthy(value) {
12
- // Returns true if value is a string that is anything but 0 or false.
13
- return value !== undefined && value !== '0' && value.toUpperCase() !== 'FALSE';
14
- }
11
+ const environment_options_1 = require("./environment-options");
12
+ /**
13
+ * Determines if the `stream` is a TTY.
14
+ *
15
+ * @param stream A NodeJS stream to check. Defaults to `process.stdout`.
16
+ * @returns `true` if the `stream` is a TTY, `false` otherwise. This detection is overridden
17
+ * by the `NG_FORCE_TTY` environment variable. In a CI environment, this will also be `false`
18
+ * unless `NG_FORCE_TTY` is set.
19
+ */
15
20
  function isTTY(stream = process.stdout) {
16
- // If we force TTY, we always return true.
17
- const force = process.env['NG_FORCE_TTY'];
18
- if (force !== undefined) {
19
- return _isTruthy(force);
20
- }
21
- return !!stream.isTTY && !_isTruthy(process.env['CI']);
21
+ return environment_options_1.forceTty ?? (!!stream.isTTY && !environment_options_1.isCI);
22
22
  }
@@ -22,4 +22,4 @@ class Version {
22
22
  this.patch = patch;
23
23
  }
24
24
  }
25
- exports.VERSION = new Version('20.2.0-next.2');
25
+ exports.VERSION = new Version('20.2.0-rc.0');
@@ -1,20 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright Google LLC All Rights Reserved.
4
- *
5
- * Use of this source code is governed by an MIT-style license that can be
6
- * found in the LICENSE file at https://angular.dev/license
7
- */
8
- /**
9
- * This uses a dynamic import to load a module which may be ESM.
10
- * CommonJS code can load ESM code via a dynamic import. Unfortunately, TypeScript
11
- * will currently, unconditionally downlevel dynamic import into a require call.
12
- * require calls cannot load ESM code and will result in a runtime error. To workaround
13
- * this, a Function constructor is used to prevent TypeScript from changing the dynamic import.
14
- * Once TypeScript provides support for keeping the dynamic import this workaround can
15
- * be dropped.
16
- *
17
- * @param modulePath The path of the module to load.
18
- * @returns A Promise that resolves to the dynamically imported module.
19
- */
20
- export declare function loadEsmModule<T>(modulePath: string | URL): Promise<T>;
@@ -1,30 +0,0 @@
1
- "use strict";
2
- /**
3
- * @license
4
- * Copyright Google LLC All Rights Reserved.
5
- *
6
- * Use of this source code is governed by an MIT-style license that can be
7
- * found in the LICENSE file at https://angular.dev/license
8
- */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.loadEsmModule = loadEsmModule;
11
- /**
12
- * Lazily compiled dynamic import loader function.
13
- */
14
- let load;
15
- /**
16
- * This uses a dynamic import to load a module which may be ESM.
17
- * CommonJS code can load ESM code via a dynamic import. Unfortunately, TypeScript
18
- * will currently, unconditionally downlevel dynamic import into a require call.
19
- * require calls cannot load ESM code and will result in a runtime error. To workaround
20
- * this, a Function constructor is used to prevent TypeScript from changing the dynamic import.
21
- * Once TypeScript provides support for keeping the dynamic import this workaround can
22
- * be dropped.
23
- *
24
- * @param modulePath The path of the module to load.
25
- * @returns A Promise that resolves to the dynamically imported module.
26
- */
27
- function loadEsmModule(modulePath) {
28
- load ??= new Function('modulePath', `return import(modulePath);`);
29
- return load(modulePath);
30
- }