@angular/cli 14.0.0-next.9 → 14.0.0-rc.2
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.
- package/lib/cli/index.js +1 -1
- package/lib/config/schema.json +51 -1
- package/lib/config/workspace-schema.d.ts +12 -0
- package/lib/init.js +1 -1
- package/package.json +14 -14
- package/src/command-builder/architect-base-command-module.d.ts +1 -1
- package/src/command-builder/command-module.js +8 -6
- package/src/command-builder/command-runner.js +2 -7
- package/src/command-builder/schematics-command-module.js +38 -11
- package/src/command-builder/utilities/command.d.ts +1 -1
- package/src/command-builder/utilities/schematic-engine-host.js +3 -1
- package/src/commands/completion/cli.d.ts +1 -1
- package/src/commands/completion/cli.js +39 -2
- package/src/commands/completion/long-description.md +73 -1
- package/src/commands/config/cli.js +1 -0
- package/src/commands/doc/cli.js +2 -2
- package/src/commands/update/cli.js +4 -6
- package/src/utilities/completion.d.ts +30 -0
- package/src/utilities/completion.js +282 -0
- package/src/utilities/environment-options.d.ts +1 -0
- package/src/utilities/environment-options.js +8 -1
- package/src/utilities/json-file.js +1 -0
package/lib/cli/index.js
CHANGED
|
@@ -73,7 +73,7 @@ async function default_1(options) {
|
|
|
73
73
|
// Log nothing.
|
|
74
74
|
}
|
|
75
75
|
else {
|
|
76
|
-
logger.fatal(
|
|
76
|
+
logger.fatal(`An unexpected error occurred: ${'toString' in err ? err.toString() : JSON.stringify(err)}`);
|
|
77
77
|
}
|
|
78
78
|
return 1;
|
|
79
79
|
}
|
package/lib/config/schema.json
CHANGED
|
@@ -742,6 +742,9 @@
|
|
|
742
742
|
"path": {
|
|
743
743
|
"type": "string",
|
|
744
744
|
"format": "path",
|
|
745
|
+
"$default": {
|
|
746
|
+
"$source": "workingDirectory"
|
|
747
|
+
},
|
|
745
748
|
"description": "The path at which to create the class, relative to the workspace root.",
|
|
746
749
|
"visible": false
|
|
747
750
|
},
|
|
@@ -773,6 +776,9 @@
|
|
|
773
776
|
"path": {
|
|
774
777
|
"type": "string",
|
|
775
778
|
"format": "path",
|
|
779
|
+
"$default": {
|
|
780
|
+
"$source": "workingDirectory"
|
|
781
|
+
},
|
|
776
782
|
"description": "The path at which to create the component file, relative to the current workspace. Default is a folder with the same name as the component in the project root.",
|
|
777
783
|
"visible": false
|
|
778
784
|
},
|
|
@@ -812,6 +818,12 @@
|
|
|
812
818
|
"alias": "t",
|
|
813
819
|
"x-user-analytics": 10
|
|
814
820
|
},
|
|
821
|
+
"standalone": {
|
|
822
|
+
"description": "Whether the generated component is standalone.",
|
|
823
|
+
"type": "boolean",
|
|
824
|
+
"default": false,
|
|
825
|
+
"x-user-analytics": 15
|
|
826
|
+
},
|
|
815
827
|
"viewEncapsulation": {
|
|
816
828
|
"description": "The view encapsulation strategy to use in the new component.",
|
|
817
829
|
"enum": [
|
|
@@ -923,6 +935,9 @@
|
|
|
923
935
|
"path": {
|
|
924
936
|
"type": "string",
|
|
925
937
|
"format": "path",
|
|
938
|
+
"$default": {
|
|
939
|
+
"$source": "workingDirectory"
|
|
940
|
+
},
|
|
926
941
|
"description": "The path at which to create the interface that defines the directive, relative to the workspace root.",
|
|
927
942
|
"visible": false
|
|
928
943
|
},
|
|
@@ -964,6 +979,12 @@
|
|
|
964
979
|
"format": "html-selector",
|
|
965
980
|
"description": "The HTML selector to use for this directive."
|
|
966
981
|
},
|
|
982
|
+
"standalone": {
|
|
983
|
+
"description": "Whether the generated directive is standalone.",
|
|
984
|
+
"type": "boolean",
|
|
985
|
+
"default": false,
|
|
986
|
+
"x-user-analytics": 15
|
|
987
|
+
},
|
|
967
988
|
"flat": {
|
|
968
989
|
"type": "boolean",
|
|
969
990
|
"description": "When true (the default), creates the new files at the top level of the current project.",
|
|
@@ -1000,6 +1021,9 @@
|
|
|
1000
1021
|
"path": {
|
|
1001
1022
|
"type": "string",
|
|
1002
1023
|
"format": "path",
|
|
1024
|
+
"$default": {
|
|
1025
|
+
"$source": "workingDirectory"
|
|
1026
|
+
},
|
|
1003
1027
|
"description": "The path at which to create the enum definition, relative to the current workspace.",
|
|
1004
1028
|
"visible": false
|
|
1005
1029
|
},
|
|
@@ -1045,6 +1069,9 @@
|
|
|
1045
1069
|
"path": {
|
|
1046
1070
|
"type": "string",
|
|
1047
1071
|
"format": "path",
|
|
1072
|
+
"$default": {
|
|
1073
|
+
"$source": "workingDirectory"
|
|
1074
|
+
},
|
|
1048
1075
|
"description": "The path at which to create the interface that defines the guard, relative to the current workspace.",
|
|
1049
1076
|
"visible": false
|
|
1050
1077
|
},
|
|
@@ -1094,6 +1121,9 @@
|
|
|
1094
1121
|
"path": {
|
|
1095
1122
|
"type": "string",
|
|
1096
1123
|
"format": "path",
|
|
1124
|
+
"$default": {
|
|
1125
|
+
"$source": "workingDirectory"
|
|
1126
|
+
},
|
|
1097
1127
|
"description": "The path at which to create the interceptor, relative to the workspace root.",
|
|
1098
1128
|
"visible": false
|
|
1099
1129
|
},
|
|
@@ -1135,6 +1165,9 @@
|
|
|
1135
1165
|
"path": {
|
|
1136
1166
|
"type": "string",
|
|
1137
1167
|
"format": "path",
|
|
1168
|
+
"$default": {
|
|
1169
|
+
"$source": "workingDirectory"
|
|
1170
|
+
},
|
|
1138
1171
|
"description": "The path at which to create the interface, relative to the workspace root.",
|
|
1139
1172
|
"visible": false
|
|
1140
1173
|
},
|
|
@@ -1224,6 +1257,9 @@
|
|
|
1224
1257
|
"path": {
|
|
1225
1258
|
"type": "string",
|
|
1226
1259
|
"format": "path",
|
|
1260
|
+
"$default": {
|
|
1261
|
+
"$source": "workingDirectory"
|
|
1262
|
+
},
|
|
1227
1263
|
"description": "The path at which to create the pipe, relative to the workspace root.",
|
|
1228
1264
|
"visible": false
|
|
1229
1265
|
},
|
|
@@ -1251,6 +1287,12 @@
|
|
|
1251
1287
|
"description": "Do not import this pipe into the owning NgModule.",
|
|
1252
1288
|
"x-user-analytics": 18
|
|
1253
1289
|
},
|
|
1290
|
+
"standalone": {
|
|
1291
|
+
"description": "Whether the generated pipe is standalone.",
|
|
1292
|
+
"type": "boolean",
|
|
1293
|
+
"default": false,
|
|
1294
|
+
"x-user-analytics": 15
|
|
1295
|
+
},
|
|
1254
1296
|
"module": {
|
|
1255
1297
|
"type": "string",
|
|
1256
1298
|
"description": "The declaring NgModule.",
|
|
@@ -1448,6 +1490,9 @@
|
|
|
1448
1490
|
"path": {
|
|
1449
1491
|
"type": "string",
|
|
1450
1492
|
"format": "path",
|
|
1493
|
+
"$default": {
|
|
1494
|
+
"$source": "workingDirectory"
|
|
1495
|
+
},
|
|
1451
1496
|
"description": "The path at which to create the interface that defines the resolver, relative to the current workspace.",
|
|
1452
1497
|
"visible": false
|
|
1453
1498
|
},
|
|
@@ -1477,7 +1522,9 @@
|
|
|
1477
1522
|
},
|
|
1478
1523
|
"path": {
|
|
1479
1524
|
"type": "string",
|
|
1480
|
-
"
|
|
1525
|
+
"$default": {
|
|
1526
|
+
"$source": "workingDirectory"
|
|
1527
|
+
},
|
|
1481
1528
|
"description": "The path at which to create the service, relative to the workspace root.",
|
|
1482
1529
|
"visible": false
|
|
1483
1530
|
},
|
|
@@ -1510,6 +1557,9 @@
|
|
|
1510
1557
|
"path": {
|
|
1511
1558
|
"type": "string",
|
|
1512
1559
|
"format": "path",
|
|
1560
|
+
"$default": {
|
|
1561
|
+
"$source": "workingDirectory"
|
|
1562
|
+
},
|
|
1513
1563
|
"description": "The path at which to create the worker file, relative to the current workspace.",
|
|
1514
1564
|
"visible": false
|
|
1515
1565
|
},
|
|
@@ -292,6 +292,10 @@ export interface AngularComponentOptionsSchema {
|
|
|
292
292
|
* Do not create "spec.ts" test files for the new component.
|
|
293
293
|
*/
|
|
294
294
|
skipTests?: boolean;
|
|
295
|
+
/**
|
|
296
|
+
* Whether the generated component is standalone.
|
|
297
|
+
*/
|
|
298
|
+
standalone?: boolean;
|
|
295
299
|
/**
|
|
296
300
|
* The file extension or preprocessor to use for style files, or 'none' to skip generating
|
|
297
301
|
* the style file.
|
|
@@ -369,6 +373,10 @@ export interface AngularDirectiveOptionsSchema {
|
|
|
369
373
|
* Do not create "spec.ts" test files for the new class.
|
|
370
374
|
*/
|
|
371
375
|
skipTests?: boolean;
|
|
376
|
+
/**
|
|
377
|
+
* Whether the generated directive is standalone.
|
|
378
|
+
*/
|
|
379
|
+
standalone?: boolean;
|
|
372
380
|
}
|
|
373
381
|
/**
|
|
374
382
|
* Generates a new, generic enum definition for the given or default project.
|
|
@@ -640,6 +648,10 @@ export interface AngularPipeOptionsSchema {
|
|
|
640
648
|
* Do not create "spec.ts" test files for the new pipe.
|
|
641
649
|
*/
|
|
642
650
|
skipTests?: boolean;
|
|
651
|
+
/**
|
|
652
|
+
* Whether the generated pipe is standalone.
|
|
653
|
+
*/
|
|
654
|
+
standalone?: boolean;
|
|
643
655
|
}
|
|
644
656
|
/**
|
|
645
657
|
* Generates a new, generic resolver definition in the given or default project.
|
package/lib/init.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular/cli",
|
|
3
|
-
"version": "14.0.0-
|
|
3
|
+
"version": "14.0.0-rc.2",
|
|
4
4
|
"description": "CLI tool for Angular",
|
|
5
5
|
"main": "lib/cli/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -25,23 +25,23 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://github.com/angular/angular-cli",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@angular-devkit/architect": "0.1400.0-
|
|
29
|
-
"@angular-devkit/core": "14.0.0-
|
|
30
|
-
"@angular-devkit/schematics": "14.0.0-
|
|
31
|
-
"@schematics/angular": "14.0.0-
|
|
28
|
+
"@angular-devkit/architect": "0.1400.0-rc.2",
|
|
29
|
+
"@angular-devkit/core": "14.0.0-rc.2",
|
|
30
|
+
"@angular-devkit/schematics": "14.0.0-rc.2",
|
|
31
|
+
"@schematics/angular": "14.0.0-rc.2",
|
|
32
32
|
"@yarnpkg/lockfile": "1.1.0",
|
|
33
33
|
"ansi-colors": "4.1.1",
|
|
34
34
|
"debug": "4.3.4",
|
|
35
35
|
"ini": "3.0.0",
|
|
36
|
-
"inquirer": "8.2.
|
|
36
|
+
"inquirer": "8.2.4",
|
|
37
37
|
"jsonc-parser": "3.0.0",
|
|
38
38
|
"npm-package-arg": "9.0.2",
|
|
39
39
|
"npm-pick-manifest": "7.0.1",
|
|
40
40
|
"open": "8.4.0",
|
|
41
41
|
"ora": "5.4.1",
|
|
42
|
-
"pacote": "13.
|
|
42
|
+
"pacote": "13.3.0",
|
|
43
43
|
"resolve": "1.22.0",
|
|
44
|
-
"semver": "7.3.
|
|
44
|
+
"semver": "7.3.7",
|
|
45
45
|
"symbol-observable": "4.0.0",
|
|
46
46
|
"uuid": "8.3.2",
|
|
47
47
|
"yargs": "17.4.1"
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"ng-update": {
|
|
50
50
|
"migrations": "@schematics/angular/migrations/migration-collection.json",
|
|
51
51
|
"packageGroup": {
|
|
52
|
-
"@angular/cli": "14.0.0-
|
|
53
|
-
"@angular-devkit/architect": "0.1400.0-
|
|
54
|
-
"@angular-devkit/build-angular": "14.0.0-
|
|
55
|
-
"@angular-devkit/build-webpack": "0.1400.0-
|
|
56
|
-
"@angular-devkit/core": "14.0.0-
|
|
57
|
-
"@angular-devkit/schematics": "14.0.0-
|
|
52
|
+
"@angular/cli": "14.0.0-rc.2",
|
|
53
|
+
"@angular-devkit/architect": "0.1400.0-rc.2",
|
|
54
|
+
"@angular-devkit/build-angular": "14.0.0-rc.2",
|
|
55
|
+
"@angular-devkit/build-webpack": "0.1400.0-rc.2",
|
|
56
|
+
"@angular-devkit/core": "14.0.0-rc.2",
|
|
57
|
+
"@angular-devkit/schematics": "14.0.0-rc.2"
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"engines": {
|
|
@@ -13,7 +13,7 @@ export interface MissingTargetChoice {
|
|
|
13
13
|
name: string;
|
|
14
14
|
value: string;
|
|
15
15
|
}
|
|
16
|
-
export declare abstract class ArchitectBaseCommandModule<T> extends CommandModule<T> implements CommandModuleImplementation<T> {
|
|
16
|
+
export declare abstract class ArchitectBaseCommandModule<T extends object> extends CommandModule<T> implements CommandModuleImplementation<T> {
|
|
17
17
|
static scope: CommandScope;
|
|
18
18
|
protected shouldReportAnalytics: boolean;
|
|
19
19
|
protected readonly missingTargetChoices: MissingTargetChoice[] | undefined;
|
|
@@ -42,6 +42,7 @@ const fs_1 = require("fs");
|
|
|
42
42
|
const path = __importStar(require("path"));
|
|
43
43
|
const helpers_1 = require("yargs/helpers");
|
|
44
44
|
const analytics_1 = require("../analytics/analytics");
|
|
45
|
+
const completion_1 = require("../utilities/completion");
|
|
45
46
|
const memoize_1 = require("../utilities/memoize");
|
|
46
47
|
var CommandScope;
|
|
47
48
|
(function (CommandScope) {
|
|
@@ -89,6 +90,12 @@ class CommandModule {
|
|
|
89
90
|
for (const [key, value] of Object.entries(options)) {
|
|
90
91
|
camelCasedOptions[helpers_1.Parser.camelCase(key)] = value;
|
|
91
92
|
}
|
|
93
|
+
// Set up autocompletion if appropriate.
|
|
94
|
+
const autocompletionExitCode = await (0, completion_1.considerSettingUpAutocompletion)(this.commandName, this.context.logger);
|
|
95
|
+
if (autocompletionExitCode !== undefined) {
|
|
96
|
+
process.exitCode = autocompletionExitCode;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
92
99
|
// Gather and report analytics.
|
|
93
100
|
const analytics = await this.getAnalytics();
|
|
94
101
|
if (this.shouldReportAnalytics) {
|
|
@@ -141,9 +148,8 @@ class CommandModule {
|
|
|
141
148
|
* **Note:** This method should be called from the command bundler method.
|
|
142
149
|
*/
|
|
143
150
|
addSchemaOptionsToCommand(localYargs, options) {
|
|
144
|
-
const workingDir = (0, core_1.normalize)(path.relative(this.context.root, process.cwd()));
|
|
145
151
|
for (const option of options) {
|
|
146
|
-
const { default: defaultVal, positional, deprecated, description, alias, userAnalytics, type, hidden, name, choices,
|
|
152
|
+
const { default: defaultVal, positional, deprecated, description, alias, userAnalytics, type, hidden, name, choices, } = option;
|
|
147
153
|
const sharedOptions = {
|
|
148
154
|
alias,
|
|
149
155
|
hidden,
|
|
@@ -153,10 +159,6 @@ class CommandModule {
|
|
|
153
159
|
// This should only be done when `--help` is used otherwise default will override options set in angular.json.
|
|
154
160
|
...(this.context.args.options.help ? { default: defaultVal } : {}),
|
|
155
161
|
};
|
|
156
|
-
// Special case for schematics
|
|
157
|
-
if (workingDir && format === 'path' && name === 'path' && hidden) {
|
|
158
|
-
sharedOptions.default = workingDir;
|
|
159
|
-
}
|
|
160
162
|
if (positional === undefined) {
|
|
161
163
|
localYargs = localYargs.option(core_1.strings.dasherize(name), {
|
|
162
164
|
type,
|
|
@@ -110,16 +110,11 @@ async function runCommand(args, logger) {
|
|
|
110
110
|
}
|
|
111
111
|
localYargs = (0, command_1.addCommandModuleToYargs)(localYargs, CommandModule, context);
|
|
112
112
|
}
|
|
113
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
114
|
-
const usageInstance = localYargs.getInternalMethods().getUsageInstance();
|
|
115
113
|
if (jsonHelp) {
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
115
|
+
const usageInstance = localYargs.getInternalMethods().getUsageInstance();
|
|
116
116
|
usageInstance.help = () => (0, json_help_1.jsonHelpUsage)();
|
|
117
117
|
}
|
|
118
|
-
if (getYargsCompletions) {
|
|
119
|
-
// When in auto completion mode avoid printing description as it causes a slugish
|
|
120
|
-
// experience when there are a large set of options.
|
|
121
|
-
usageInstance.getDescriptions = () => ({});
|
|
122
|
-
}
|
|
123
118
|
await localYargs
|
|
124
119
|
.scriptName('ng')
|
|
125
120
|
// https://github.com/yargs/yargs/blob/main/docs/advanced.md#customizing-yargs-parser
|
|
@@ -40,6 +40,7 @@ exports.SchematicsCommandModule = exports.DEFAULT_SCHEMATICS_COLLECTION = void 0
|
|
|
40
40
|
const core_1 = require("@angular-devkit/core");
|
|
41
41
|
const schematics_1 = require("@angular-devkit/schematics");
|
|
42
42
|
const tools_1 = require("@angular-devkit/schematics/tools");
|
|
43
|
+
const path_1 = require("path");
|
|
43
44
|
const config_1 = require("../utilities/config");
|
|
44
45
|
const memoize_1 = require("../utilities/memoize");
|
|
45
46
|
const tty_1 = require("../utilities/tty");
|
|
@@ -109,9 +110,7 @@ class SchematicsCommandModule extends command_module_1.CommandModule {
|
|
|
109
110
|
optionTransforms: [
|
|
110
111
|
// Add configuration file defaults
|
|
111
112
|
async (schematic, current) => {
|
|
112
|
-
const projectName = typeof current.project === 'string'
|
|
113
|
-
? current.project
|
|
114
|
-
: this.getProjectName();
|
|
113
|
+
const projectName = typeof (current === null || current === void 0 ? void 0 : current.project) === 'string' ? current.project : this.getProjectName();
|
|
115
114
|
return {
|
|
116
115
|
...(await (0, config_1.getSchematicDefaults)(schematic.collection.name, schematic.name, projectName)),
|
|
117
116
|
...current,
|
|
@@ -121,8 +120,10 @@ class SchematicsCommandModule extends command_module_1.CommandModule {
|
|
|
121
120
|
engineHostCreator: (options) => new schematic_engine_host_1.SchematicEngineHost(options.resolvePaths),
|
|
122
121
|
});
|
|
123
122
|
workflow.registry.addPostTransform(core_1.schema.transforms.addUndefinedDefaults);
|
|
124
|
-
workflow.registry.addSmartDefaultProvider('projectName', () => this.getProjectName());
|
|
125
123
|
workflow.registry.useXDeprecatedProvider((msg) => logger.warn(msg));
|
|
124
|
+
workflow.registry.addSmartDefaultProvider('projectName', () => this.getProjectName());
|
|
125
|
+
const workingDir = (0, core_1.normalize)((0, path_1.relative)(this.context.root, process.cwd()));
|
|
126
|
+
workflow.registry.addSmartDefaultProvider('workingDirectory', () => workingDir === '' ? undefined : workingDir);
|
|
126
127
|
let shouldReportAnalytics = true;
|
|
127
128
|
workflow.engineHost.registerOptionsTransform(async (schematic, options) => {
|
|
128
129
|
var _a;
|
|
@@ -136,6 +137,28 @@ class SchematicsCommandModule extends command_module_1.CommandModule {
|
|
|
136
137
|
schematic.name.replace(/\//g, '_'),
|
|
137
138
|
]);
|
|
138
139
|
}
|
|
140
|
+
// TODO: The below should be removed in version 15 when we change 1P schematics to use the `workingDirectory smart default`.
|
|
141
|
+
// Handle `"format": "path"` options.
|
|
142
|
+
const schema = schematic === null || schematic === void 0 ? void 0 : schematic.schemaJson;
|
|
143
|
+
if (!options || !schema || !(0, core_1.isJsonObject)(schema)) {
|
|
144
|
+
return options;
|
|
145
|
+
}
|
|
146
|
+
if (!('path' in options && options['path'] === undefined)) {
|
|
147
|
+
return options;
|
|
148
|
+
}
|
|
149
|
+
const properties = schema === null || schema === void 0 ? void 0 : schema['properties'];
|
|
150
|
+
if (!properties || !(0, core_1.isJsonObject)(properties)) {
|
|
151
|
+
return options;
|
|
152
|
+
}
|
|
153
|
+
const property = properties['path'];
|
|
154
|
+
if (!property || !(0, core_1.isJsonObject)(property)) {
|
|
155
|
+
return options;
|
|
156
|
+
}
|
|
157
|
+
if (property['format'] === 'path' && !property['$default']) {
|
|
158
|
+
options['path'] = workingDir || undefined;
|
|
159
|
+
this.context.logger.warn(`The 'path' option in '${schematic === null || schematic === void 0 ? void 0 : schematic.schema}' is using deprecated behaviour.` +
|
|
160
|
+
`'workingDirectory' smart default provider should be used instead.`);
|
|
161
|
+
}
|
|
139
162
|
return options;
|
|
140
163
|
});
|
|
141
164
|
if (options.interactive !== false && (0, tty_1.isTTY)()) {
|
|
@@ -211,16 +234,20 @@ class SchematicsCommandModule extends command_module_1.CommandModule {
|
|
|
211
234
|
}
|
|
212
235
|
async getSchematicCollections() {
|
|
213
236
|
var _a;
|
|
237
|
+
// Resolve relative collections from the location of `angular.json`
|
|
238
|
+
const resolveRelativeCollection = (collectionName) => collectionName.charAt(0) === '.'
|
|
239
|
+
? (0, path_1.resolve)(this.context.root, collectionName)
|
|
240
|
+
: collectionName;
|
|
214
241
|
const getSchematicCollections = (configSection) => {
|
|
215
242
|
if (!configSection) {
|
|
216
243
|
return undefined;
|
|
217
244
|
}
|
|
218
245
|
const { schematicCollections, defaultCollection } = configSection;
|
|
219
246
|
if (Array.isArray(schematicCollections)) {
|
|
220
|
-
return new Set(schematicCollections);
|
|
247
|
+
return new Set(schematicCollections.map((c) => resolveRelativeCollection(c)));
|
|
221
248
|
}
|
|
222
249
|
else if (typeof defaultCollection === 'string') {
|
|
223
|
-
return new Set([defaultCollection]);
|
|
250
|
+
return new Set([resolveRelativeCollection(defaultCollection)]);
|
|
224
251
|
}
|
|
225
252
|
return undefined;
|
|
226
253
|
};
|
|
@@ -277,11 +304,11 @@ class SchematicsCommandModule extends command_module_1.CommandModule {
|
|
|
277
304
|
if (err instanceof schematics_1.UnsuccessfulWorkflowExecution) {
|
|
278
305
|
// "See above" because we already printed the error.
|
|
279
306
|
logger.fatal('The Schematic workflow failed. See above.');
|
|
280
|
-
return 1;
|
|
281
307
|
}
|
|
282
308
|
else {
|
|
283
|
-
|
|
309
|
+
logger.fatal(err.message);
|
|
284
310
|
}
|
|
311
|
+
return 1;
|
|
285
312
|
}
|
|
286
313
|
finally {
|
|
287
314
|
unsubscribe();
|
|
@@ -301,9 +328,9 @@ class SchematicsCommandModule extends command_module_1.CommandModule {
|
|
|
301
328
|
if (typeof defaultProjectName === 'string' && defaultProjectName) {
|
|
302
329
|
if (!this.defaultProjectDeprecationWarningShown) {
|
|
303
330
|
logger.warn(core_1.tags.oneLine `
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
331
|
+
DEPRECATED: The 'defaultProject' workspace option has been deprecated.
|
|
332
|
+
The project to use will be determined from the current working directory.
|
|
333
|
+
`);
|
|
307
334
|
this.defaultProjectDeprecationWarningShown = true;
|
|
308
335
|
}
|
|
309
336
|
return defaultProjectName;
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
import { Argv } from 'yargs';
|
|
9
9
|
import { CommandContext, CommandModule, CommandModuleImplementation } from '../command-module';
|
|
10
10
|
export declare const demandCommandFailureMessage = "You need to specify a command before moving on. Use '--help' to view the available commands.";
|
|
11
|
-
export declare function addCommandModuleToYargs<T, U extends Partial<CommandModuleImplementation> & {
|
|
11
|
+
export declare function addCommandModuleToYargs<T extends object, U extends Partial<CommandModuleImplementation> & {
|
|
12
12
|
new (context: CommandContext): Partial<CommandModuleImplementation> & CommandModule;
|
|
13
13
|
}>(localYargs: Argv<T>, commandModule: U, context: CommandContext): Argv<T>;
|
|
@@ -161,7 +161,9 @@ function wrap(schematicFile, schematicDirectory, moduleCache, exportName) {
|
|
|
161
161
|
const schematicCode = (0, fs_1.readFileSync)(schematicFile, 'utf8');
|
|
162
162
|
// `module` is required due to @angular/localize ng-add being in UMD format
|
|
163
163
|
const headerCode = '(function() {\nvar exports = {};\nvar module = { exports };\n';
|
|
164
|
-
const footerCode = exportName
|
|
164
|
+
const footerCode = exportName
|
|
165
|
+
? `\nreturn module.exports['${exportName}'];});`
|
|
166
|
+
: '\nreturn module.exports;});';
|
|
165
167
|
const script = new vm_1.Script(headerCode + schematicCode + footerCode, {
|
|
166
168
|
filename: schematicFile,
|
|
167
169
|
lineOffset: 3,
|
|
@@ -14,13 +14,51 @@ exports.CompletionCommandModule = void 0;
|
|
|
14
14
|
const path_1 = require("path");
|
|
15
15
|
const yargs_1 = __importDefault(require("yargs"));
|
|
16
16
|
const command_module_1 = require("../../command-builder/command-module");
|
|
17
|
+
const command_1 = require("../../command-builder/utilities/command");
|
|
18
|
+
const color_1 = require("../../utilities/color");
|
|
19
|
+
const completion_1 = require("../../utilities/completion");
|
|
17
20
|
class CompletionCommandModule extends command_module_1.CommandModule {
|
|
18
21
|
constructor() {
|
|
19
22
|
super(...arguments);
|
|
20
23
|
this.command = 'completion';
|
|
21
|
-
this.describe = '
|
|
24
|
+
this.describe = 'Set up Angular CLI autocompletion for your terminal.';
|
|
22
25
|
this.longDescriptionPath = (0, path_1.join)(__dirname, 'long-description.md');
|
|
23
26
|
}
|
|
27
|
+
builder(localYargs) {
|
|
28
|
+
return (0, command_1.addCommandModuleToYargs)(localYargs, CompletionScriptCommandModule, this.context);
|
|
29
|
+
}
|
|
30
|
+
async run() {
|
|
31
|
+
let rcFile;
|
|
32
|
+
try {
|
|
33
|
+
rcFile = await (0, completion_1.initializeAutocomplete)();
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
this.context.logger.error(err.message);
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
this.context.logger.info(`
|
|
40
|
+
Appended \`source <(ng completion script)\` to \`${rcFile}\`. Restart your terminal or run the following to autocomplete \`ng\` commands:
|
|
41
|
+
|
|
42
|
+
${color_1.colors.yellow('source <(ng completion script)')}
|
|
43
|
+
`.trim());
|
|
44
|
+
if ((await (0, completion_1.hasGlobalCliInstall)()) === false) {
|
|
45
|
+
this.context.logger.warn('Setup completed successfully, but there does not seem to be a global install of the' +
|
|
46
|
+
' Angular CLI. For autocompletion to work, the CLI will need to be on your `$PATH`, which' +
|
|
47
|
+
' is typically done with the `-g` flag in `npm install -g @angular/cli`.' +
|
|
48
|
+
'\n\n' +
|
|
49
|
+
'For more information, see https://angular.io/cli/completion#global-install');
|
|
50
|
+
}
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.CompletionCommandModule = CompletionCommandModule;
|
|
55
|
+
class CompletionScriptCommandModule extends command_module_1.CommandModule {
|
|
56
|
+
constructor() {
|
|
57
|
+
super(...arguments);
|
|
58
|
+
this.command = 'script';
|
|
59
|
+
this.describe = 'Generate a bash and zsh real-time type-ahead autocompletion script.';
|
|
60
|
+
this.longDescriptionPath = undefined;
|
|
61
|
+
}
|
|
24
62
|
builder(localYargs) {
|
|
25
63
|
return localYargs;
|
|
26
64
|
}
|
|
@@ -28,4 +66,3 @@ class CompletionCommandModule extends command_module_1.CommandModule {
|
|
|
28
66
|
yargs_1.default.showCompletionScript();
|
|
29
67
|
}
|
|
30
68
|
}
|
|
31
|
-
exports.CompletionCommandModule = CompletionCommandModule;
|
|
@@ -1 +1,73 @@
|
|
|
1
|
-
|
|
1
|
+
Setting up autocompletion configures your terminal, so pressing the `<TAB>` key while in the middle
|
|
2
|
+
of typing will display various commands and options available to you. This makes it very easy to
|
|
3
|
+
discover and use CLI commands without lots of memorization.
|
|
4
|
+
|
|
5
|
+

|
|
8
|
+
|
|
9
|
+
## Automated setup
|
|
10
|
+
|
|
11
|
+
The CLI should prompt and ask to set up autocompletion for you the first time you use it (v14+).
|
|
12
|
+
Simply answer "Yes" and the CLI will take care of the rest.
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
$ ng serve
|
|
16
|
+
? Would you like to enable autocompletion? This will set up your terminal so pressing TAB while
|
|
17
|
+
typing Angular CLI commands will show possible options and autocomplete arguments. (Enabling
|
|
18
|
+
autocompletion will modify configuration files in your home directory.) Yes
|
|
19
|
+
Appended `source <(ng completion script)` to `/home/my-username/.bashrc`. Restart your terminal or
|
|
20
|
+
run:
|
|
21
|
+
|
|
22
|
+
source <(ng completion script)
|
|
23
|
+
|
|
24
|
+
to autocomplete `ng` commands.
|
|
25
|
+
|
|
26
|
+
# Serve output...
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If you already refused the prompt, it won't ask again. But you can run `ng completion` to
|
|
30
|
+
do the same thing automatically.
|
|
31
|
+
|
|
32
|
+
This modifies your terminal environment to load Angular CLI autocompletion, but can't update your
|
|
33
|
+
current terminal session. Either restart it or run `source <(ng completion script)` directly to
|
|
34
|
+
enable autocompletion in your current session.
|
|
35
|
+
|
|
36
|
+
Test it out by typing `ng ser<TAB>` and it should autocomplete to `ng serve`. Ambiguous arguments
|
|
37
|
+
will show all possible options and their documentation, such as `ng generate <TAB>`.
|
|
38
|
+
|
|
39
|
+
## Manual setup
|
|
40
|
+
|
|
41
|
+
Some users may have highly customized terminal setups, possibly with configuration files checked
|
|
42
|
+
into source control with an opinionated structure. `ng completion` only ever appends Angular's setup
|
|
43
|
+
to an existing configuration file for your current shell, or creates one if none exists. If you want
|
|
44
|
+
more control over exactly where this configuration lives, you can manually set it up by having your
|
|
45
|
+
shell run at startup:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
source <(ng completion script)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This is equivalent to what `ng completion` will automatically set up, and gives power users more
|
|
52
|
+
flexibility in their environments when desired.
|
|
53
|
+
|
|
54
|
+
## Platform support
|
|
55
|
+
|
|
56
|
+
Angular CLI supports autocompletion for the Bash and Zsh shells on MacOS and Linux operating
|
|
57
|
+
systems.
|
|
58
|
+
|
|
59
|
+
Windows does not support autocompletion in native shells, such as Cmd and Powershell. However,
|
|
60
|
+
the Angular CLI supports Git Bash and
|
|
61
|
+
[Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/) using Bash or Zsh.
|
|
62
|
+
|
|
63
|
+
## Global install
|
|
64
|
+
|
|
65
|
+
Autocompletion works by configuring your terminal to invoke the Angular CLI on startup to load the
|
|
66
|
+
setup script. This means the terminal must be able to find and execute the Angular CLI, typically
|
|
67
|
+
through a global install that places the binary on the user's `$PATH`. If you get
|
|
68
|
+
`command not found: ng`, make sure the CLI is installed globally which you can do with the `-g`
|
|
69
|
+
flag:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm install -g @angular/cli
|
|
73
|
+
```
|
|
@@ -83,6 +83,7 @@ class ConfigCommandModule extends command_module_1.CommandModule {
|
|
|
83
83
|
'cli.analytics',
|
|
84
84
|
'cli.analyticsSharing.tracking',
|
|
85
85
|
'cli.analyticsSharing.uuid',
|
|
86
|
+
'cli.completion.prompted',
|
|
86
87
|
]);
|
|
87
88
|
if (options.global &&
|
|
88
89
|
!options.jsonPath.startsWith('schematics.') &&
|
package/src/commands/doc/cli.js
CHANGED
|
@@ -93,8 +93,8 @@ class DocCommandModule extends command_module_1.CommandModule {
|
|
|
93
93
|
catch { }
|
|
94
94
|
}
|
|
95
95
|
await (0, open_1.default)(options.search
|
|
96
|
-
? `https://${domain}/
|
|
97
|
-
: `https://${domain}/
|
|
96
|
+
? `https://${domain}/docs?search=${options.keyword}`
|
|
97
|
+
: `https://${domain}/api?query=${options.keyword}`);
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
exports.DocCommandModule = DocCommandModule;
|
|
@@ -122,7 +122,7 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
122
122
|
alias: ['C'],
|
|
123
123
|
default: false,
|
|
124
124
|
})
|
|
125
|
-
.check(({ packages,
|
|
125
|
+
.check(({ packages, 'allow-dirty': allowDirty, 'migrate-only': migrateOnly }) => {
|
|
126
126
|
const { logger } = this.context;
|
|
127
127
|
// This allows the user to easily reset any changes from the update.
|
|
128
128
|
if ((packages === null || packages === void 0 ? void 0 : packages.length) && !this.checkCleanGit()) {
|
|
@@ -137,9 +137,6 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
137
137
|
if ((packages === null || packages === void 0 ? void 0 : packages.length) !== 1) {
|
|
138
138
|
throw new command_module_1.CommandModuleError(`A single package must be specified when using the 'migrate-only' option.`);
|
|
139
139
|
}
|
|
140
|
-
if (next) {
|
|
141
|
-
logger.warn(`'next' option has no effect when using 'migrate-only' option.`);
|
|
142
|
-
}
|
|
143
140
|
}
|
|
144
141
|
return true;
|
|
145
142
|
})
|
|
@@ -584,8 +581,9 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
584
581
|
}
|
|
585
582
|
}
|
|
586
583
|
const result = await this.executeMigrations(workflow, migration.package, migrations, migration.from, migration.to, options.createCommits);
|
|
587
|
-
|
|
588
|
-
|
|
584
|
+
// A non-zero value is a failure for the package's migrations
|
|
585
|
+
if (result !== 0) {
|
|
586
|
+
return result;
|
|
589
587
|
}
|
|
590
588
|
}
|
|
591
589
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
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.io/license
|
|
7
|
+
*/
|
|
8
|
+
import { logging } from '@angular-devkit/core';
|
|
9
|
+
/**
|
|
10
|
+
* Checks if it is appropriate to prompt the user to setup autocompletion. If not, does nothing. If
|
|
11
|
+
* so prompts and sets up autocompletion for the user. Returns an exit code if the program should
|
|
12
|
+
* terminate, otherwise returns `undefined`.
|
|
13
|
+
* @returns an exit code if the program should terminate, undefined otherwise.
|
|
14
|
+
*/
|
|
15
|
+
export declare function considerSettingUpAutocompletion(command: string, logger: logging.Logger): Promise<number | undefined>;
|
|
16
|
+
/**
|
|
17
|
+
* Sets up autocompletion for the user's terminal. This attempts to find the configuration file for
|
|
18
|
+
* the current shell (`.bashrc`, `.zshrc`, etc.) and append a command which enables autocompletion
|
|
19
|
+
* for the Angular CLI. Supports only Bash and Zsh. Returns whether or not it was successful.
|
|
20
|
+
* @return The full path of the configuration file modified.
|
|
21
|
+
*/
|
|
22
|
+
export declare function initializeAutocomplete(): Promise<string>;
|
|
23
|
+
/**
|
|
24
|
+
* Returns whether the user has a global CLI install or `undefined` if this can't be determined.
|
|
25
|
+
* Execution from `npx` is *not* considered a global CLI install.
|
|
26
|
+
*
|
|
27
|
+
* This does *not* mean the current execution is from a global CLI install, only that a global
|
|
28
|
+
* install exists on the system.
|
|
29
|
+
*/
|
|
30
|
+
export declare function hasGlobalCliInstall(): Promise<boolean | undefined>;
|
|
@@ -0,0 +1,282 @@
|
|
|
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.io/license
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
26
|
+
if (mod && mod.__esModule) return mod;
|
|
27
|
+
var result = {};
|
|
28
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
29
|
+
__setModuleDefault(result, mod);
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.hasGlobalCliInstall = exports.initializeAutocomplete = exports.considerSettingUpAutocompletion = void 0;
|
|
34
|
+
const core_1 = require("@angular-devkit/core");
|
|
35
|
+
const child_process_1 = require("child_process");
|
|
36
|
+
const fs_1 = require("fs");
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const process_1 = require("process");
|
|
39
|
+
const color_1 = require("../utilities/color");
|
|
40
|
+
const config_1 = require("../utilities/config");
|
|
41
|
+
const environment_options_1 = require("../utilities/environment-options");
|
|
42
|
+
const tty_1 = require("../utilities/tty");
|
|
43
|
+
/**
|
|
44
|
+
* Checks if it is appropriate to prompt the user to setup autocompletion. If not, does nothing. If
|
|
45
|
+
* so prompts and sets up autocompletion for the user. Returns an exit code if the program should
|
|
46
|
+
* terminate, otherwise returns `undefined`.
|
|
47
|
+
* @returns an exit code if the program should terminate, undefined otherwise.
|
|
48
|
+
*/
|
|
49
|
+
async function considerSettingUpAutocompletion(command, logger) {
|
|
50
|
+
// Check if we should prompt the user to setup autocompletion.
|
|
51
|
+
const completionConfig = await getCompletionConfig();
|
|
52
|
+
if (!(await shouldPromptForAutocompletionSetup(command, completionConfig))) {
|
|
53
|
+
return undefined; // Already set up or prompted previously, nothing to do.
|
|
54
|
+
}
|
|
55
|
+
// Prompt the user and record their response.
|
|
56
|
+
const shouldSetupAutocompletion = await promptForAutocompletion();
|
|
57
|
+
if (!shouldSetupAutocompletion) {
|
|
58
|
+
// User rejected the prompt and doesn't want autocompletion.
|
|
59
|
+
logger.info(`
|
|
60
|
+
Ok, you won't be prompted again. Should you change your mind, the following command will set up autocompletion for you:
|
|
61
|
+
|
|
62
|
+
${color_1.colors.yellow(`ng completion`)}
|
|
63
|
+
`.trim());
|
|
64
|
+
// Save configuration to remember that the user was prompted and avoid prompting again.
|
|
65
|
+
await setCompletionConfig({ ...completionConfig, prompted: true });
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
// User accepted the prompt, set up autocompletion.
|
|
69
|
+
let rcFile;
|
|
70
|
+
try {
|
|
71
|
+
rcFile = await initializeAutocomplete();
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
// Failed to set up autocompeletion, log the error and abort.
|
|
75
|
+
logger.error(err.message);
|
|
76
|
+
return 1;
|
|
77
|
+
}
|
|
78
|
+
// Notify the user autocompletion was set up successfully.
|
|
79
|
+
logger.info(`
|
|
80
|
+
Appended \`source <(ng completion script)\` to \`${rcFile}\`. Restart your terminal or run the following to autocomplete \`ng\` commands:
|
|
81
|
+
|
|
82
|
+
${color_1.colors.yellow(`source <(ng completion script)`)}
|
|
83
|
+
`.trim());
|
|
84
|
+
if ((await hasGlobalCliInstall()) === false) {
|
|
85
|
+
logger.warn('Setup completed successfully, but there does not seem to be a global install of the' +
|
|
86
|
+
' Angular CLI. For autocompletion to work, the CLI will need to be on your `$PATH`, which' +
|
|
87
|
+
' is typically done with the `-g` flag in `npm install -g @angular/cli`.' +
|
|
88
|
+
'\n\n' +
|
|
89
|
+
'For more information, see https://angular.io/cli/completion#global-install');
|
|
90
|
+
}
|
|
91
|
+
// Save configuration to remember that the user was prompted.
|
|
92
|
+
await setCompletionConfig({ ...completionConfig, prompted: true });
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
exports.considerSettingUpAutocompletion = considerSettingUpAutocompletion;
|
|
96
|
+
async function getCompletionConfig() {
|
|
97
|
+
var _a;
|
|
98
|
+
const wksp = await (0, config_1.getWorkspace)('global');
|
|
99
|
+
return (_a = wksp === null || wksp === void 0 ? void 0 : wksp.getCli()) === null || _a === void 0 ? void 0 : _a['completion'];
|
|
100
|
+
}
|
|
101
|
+
async function setCompletionConfig(config) {
|
|
102
|
+
var _a;
|
|
103
|
+
var _b;
|
|
104
|
+
const wksp = await (0, config_1.getWorkspace)('global');
|
|
105
|
+
if (!wksp) {
|
|
106
|
+
throw new Error(`Could not find global workspace`);
|
|
107
|
+
}
|
|
108
|
+
(_a = (_b = wksp.extensions)['cli']) !== null && _a !== void 0 ? _a : (_b['cli'] = {});
|
|
109
|
+
const cli = wksp.extensions['cli'];
|
|
110
|
+
if (!core_1.json.isJsonObject(cli)) {
|
|
111
|
+
throw new Error(`Invalid config found at ${wksp.filePath}. \`extensions.cli\` should be an object.`);
|
|
112
|
+
}
|
|
113
|
+
cli.completion = config;
|
|
114
|
+
await wksp.save();
|
|
115
|
+
}
|
|
116
|
+
async function shouldPromptForAutocompletionSetup(command, config) {
|
|
117
|
+
// Force whether or not to prompt for autocomplete to give an easy path for e2e testing to skip.
|
|
118
|
+
if (environment_options_1.forceAutocomplete !== undefined) {
|
|
119
|
+
return environment_options_1.forceAutocomplete;
|
|
120
|
+
}
|
|
121
|
+
// Don't prompt on `ng update` or `ng completion`.
|
|
122
|
+
if (command === 'update' || command === 'completion') {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
// Non-interactive and continuous integration systems don't care about autocompletion.
|
|
126
|
+
if (!(0, tty_1.isTTY)()) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
// Skip prompt if the user has already been prompted.
|
|
130
|
+
if (config === null || config === void 0 ? void 0 : config.prompted) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
// `$HOME` variable is necessary to find RC files to modify.
|
|
134
|
+
const home = process_1.env['HOME'];
|
|
135
|
+
if (!home) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
// Get possible RC files for the current shell.
|
|
139
|
+
const shell = process_1.env['SHELL'];
|
|
140
|
+
if (!shell) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
const rcFiles = getShellRunCommandCandidates(shell, home);
|
|
144
|
+
if (!rcFiles) {
|
|
145
|
+
return false; // Unknown shell.
|
|
146
|
+
}
|
|
147
|
+
// Don't prompt if the user is missing a global CLI install. Autocompletion won't work after setup
|
|
148
|
+
// anyway and could be annoying for users running one-off commands via `npx` or using `npm start`.
|
|
149
|
+
if ((await hasGlobalCliInstall()) === false) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
// Check each RC file if they already use `ng completion script` in any capacity and don't prompt.
|
|
153
|
+
for (const rcFile of rcFiles) {
|
|
154
|
+
const contents = await fs_1.promises.readFile(rcFile, 'utf-8').catch(() => undefined);
|
|
155
|
+
if (contents === null || contents === void 0 ? void 0 : contents.includes('ng completion script')) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
async function promptForAutocompletion() {
|
|
162
|
+
// Dynamically load `inquirer` so users don't have to pay the cost of parsing and executing it for
|
|
163
|
+
// the 99% of builds that *don't* prompt for autocompletion.
|
|
164
|
+
const { prompt } = await Promise.resolve().then(() => __importStar(require('inquirer')));
|
|
165
|
+
const { autocomplete } = await prompt([
|
|
166
|
+
{
|
|
167
|
+
name: 'autocomplete',
|
|
168
|
+
type: 'confirm',
|
|
169
|
+
message: `
|
|
170
|
+
Would you like to enable autocompletion? This will set up your terminal so pressing TAB while typing
|
|
171
|
+
Angular CLI commands will show possible options and autocomplete arguments. (Enabling autocompletion
|
|
172
|
+
will modify configuration files in your home directory.)
|
|
173
|
+
`
|
|
174
|
+
.split('\n')
|
|
175
|
+
.join(' ')
|
|
176
|
+
.trim(),
|
|
177
|
+
default: true,
|
|
178
|
+
},
|
|
179
|
+
]);
|
|
180
|
+
return autocomplete;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Sets up autocompletion for the user's terminal. This attempts to find the configuration file for
|
|
184
|
+
* the current shell (`.bashrc`, `.zshrc`, etc.) and append a command which enables autocompletion
|
|
185
|
+
* for the Angular CLI. Supports only Bash and Zsh. Returns whether or not it was successful.
|
|
186
|
+
* @return The full path of the configuration file modified.
|
|
187
|
+
*/
|
|
188
|
+
async function initializeAutocomplete() {
|
|
189
|
+
var _a, _b;
|
|
190
|
+
// Get the currently active `$SHELL` and `$HOME` environment variables.
|
|
191
|
+
const shell = process_1.env['SHELL'];
|
|
192
|
+
if (!shell) {
|
|
193
|
+
throw new Error('`$SHELL` environment variable not set. Angular CLI autocompletion only supports Bash or' +
|
|
194
|
+
" Zsh. If you're on Windows, Cmd and Powershell don't support command autocompletion," +
|
|
195
|
+
' but Git Bash or Windows Subsystem for Linux should work, so please try again in one of' +
|
|
196
|
+
' those environments.');
|
|
197
|
+
}
|
|
198
|
+
const home = process_1.env['HOME'];
|
|
199
|
+
if (!home) {
|
|
200
|
+
throw new Error('`$HOME` environment variable not set. Setting up autocompletion modifies configuration files' +
|
|
201
|
+
' in the home directory and must be set.');
|
|
202
|
+
}
|
|
203
|
+
// Get all the files we can add `ng completion` to which apply to the user's `$SHELL`.
|
|
204
|
+
const runCommandCandidates = getShellRunCommandCandidates(shell, home);
|
|
205
|
+
if (!runCommandCandidates) {
|
|
206
|
+
throw new Error(`Unknown \`$SHELL\` environment variable value (${shell}). Angular CLI autocompletion only supports Bash or Zsh.`);
|
|
207
|
+
}
|
|
208
|
+
// Get the first file that already exists or fallback to a new file of the first candidate.
|
|
209
|
+
const candidates = await Promise.allSettled(runCommandCandidates.map((rcFile) => fs_1.promises.access(rcFile).then(() => rcFile)));
|
|
210
|
+
const rcFile = (_b = (_a = candidates.find((result) => result.status === 'fulfilled')) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : runCommandCandidates[0];
|
|
211
|
+
// Append Angular autocompletion setup to RC file.
|
|
212
|
+
try {
|
|
213
|
+
await fs_1.promises.appendFile(rcFile, '\n\n# Load Angular CLI autocompletion.\nsource <(ng completion script)\n');
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
throw new Error(`Failed to append autocompletion setup to \`${rcFile}\`:\n${err.message}`);
|
|
217
|
+
}
|
|
218
|
+
return rcFile;
|
|
219
|
+
}
|
|
220
|
+
exports.initializeAutocomplete = initializeAutocomplete;
|
|
221
|
+
/** Returns an ordered list of possible candidates of RC files used by the given shell. */
|
|
222
|
+
function getShellRunCommandCandidates(shell, home) {
|
|
223
|
+
if (shell.toLowerCase().includes('bash')) {
|
|
224
|
+
return ['.bashrc', '.bash_profile', '.profile'].map((file) => path.join(home, file));
|
|
225
|
+
}
|
|
226
|
+
else if (shell.toLowerCase().includes('zsh')) {
|
|
227
|
+
return ['.zshrc', '.zsh_profile', '.profile'].map((file) => path.join(home, file));
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Returns whether the user has a global CLI install or `undefined` if this can't be determined.
|
|
235
|
+
* Execution from `npx` is *not* considered a global CLI install.
|
|
236
|
+
*
|
|
237
|
+
* This does *not* mean the current execution is from a global CLI install, only that a global
|
|
238
|
+
* install exists on the system.
|
|
239
|
+
*/
|
|
240
|
+
async function hasGlobalCliInstall() {
|
|
241
|
+
var _a;
|
|
242
|
+
// List all binaries with the `ng` name on the user's `$PATH`.
|
|
243
|
+
const proc = (0, child_process_1.execFile)('which', ['-a', 'ng']);
|
|
244
|
+
let stdout = '';
|
|
245
|
+
(_a = proc.stdout) === null || _a === void 0 ? void 0 : _a.addListener('data', (content) => {
|
|
246
|
+
stdout += content;
|
|
247
|
+
});
|
|
248
|
+
const exitCode = await new Promise((resolve) => {
|
|
249
|
+
proc.addListener('exit', (exitCode) => {
|
|
250
|
+
resolve(exitCode);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
switch (exitCode) {
|
|
254
|
+
case 0:
|
|
255
|
+
// Successfully listed all `ng` binaries on the `$PATH`. Look for at least one line which is a
|
|
256
|
+
// global install. We can't easily identify global installs, but local installs are typically
|
|
257
|
+
// placed in `node_modules/.bin` by NPM / Yarn. `npx` also currently caches files at
|
|
258
|
+
// `~/.npm/_npx/*/node_modules/.bin/`, so the same logic applies.
|
|
259
|
+
const lines = stdout.split('\n').filter((line) => line !== '');
|
|
260
|
+
const hasGlobalInstall = lines.some((line) => {
|
|
261
|
+
// A binary is a local install if it is a direct child of a `node_modules/.bin/` directory.
|
|
262
|
+
const parent = path.parse(path.parse(line).dir);
|
|
263
|
+
const grandparent = path.parse(parent.dir);
|
|
264
|
+
const localInstall = grandparent.base === 'node_modules' && parent.base === '.bin';
|
|
265
|
+
return !localInstall;
|
|
266
|
+
});
|
|
267
|
+
return hasGlobalInstall;
|
|
268
|
+
case 1:
|
|
269
|
+
// No instances of `ng` on the user's `$PATH`.
|
|
270
|
+
return false;
|
|
271
|
+
case null:
|
|
272
|
+
// `which` was killed by a signal and did not exit gracefully. Maybe it hung or something else
|
|
273
|
+
// went very wrong, so treat this as inconclusive.
|
|
274
|
+
return undefined;
|
|
275
|
+
default:
|
|
276
|
+
// `which` returns exit code 2 if an invalid option is specified and `-a` doesn't appear to be
|
|
277
|
+
// supported on all systems. Other exit codes mean unknown errors occurred. Can't tell whether
|
|
278
|
+
// CLI is globally installed, so treat this as inconclusive.
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
exports.hasGlobalCliInstall = hasGlobalCliInstall;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* found in the LICENSE file at https://angular.io/license
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.ngDebug = exports.disableVersionCheck = exports.isCI = exports.analyticsShareDisabled = exports.analyticsDisabled = void 0;
|
|
10
|
+
exports.forceAutocomplete = exports.ngDebug = exports.disableVersionCheck = exports.isCI = exports.analyticsShareDisabled = exports.analyticsDisabled = void 0;
|
|
11
11
|
function isPresent(variable) {
|
|
12
12
|
return typeof variable === 'string' && variable !== '';
|
|
13
13
|
}
|
|
@@ -17,8 +17,15 @@ function isDisabled(variable) {
|
|
|
17
17
|
function isEnabled(variable) {
|
|
18
18
|
return isPresent(variable) && (variable === '1' || variable.toLowerCase() === 'true');
|
|
19
19
|
}
|
|
20
|
+
function optional(variable) {
|
|
21
|
+
if (!isPresent(variable)) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
return isEnabled(variable);
|
|
25
|
+
}
|
|
20
26
|
exports.analyticsDisabled = isDisabled(process.env['NG_CLI_ANALYTICS']);
|
|
21
27
|
exports.analyticsShareDisabled = isDisabled(process.env['NG_CLI_ANALYTICS_SHARE']);
|
|
22
28
|
exports.isCI = isEnabled(process.env['CI']);
|
|
23
29
|
exports.disableVersionCheck = isEnabled(process.env['NG_DISABLE_VERSION_CHECK']);
|
|
24
30
|
exports.ngDebug = isEnabled(process.env['NG_DEBUG']);
|
|
31
|
+
exports.forceAutocomplete = optional(process.env['NG_FORCE_AUTOCOMPLETE']);
|