@react-native-windows/cli 0.0.0-canary.1 → 0.0.0-canary.102

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 (123) hide show
  1. package/README.md +49 -0
  2. package/lib-commonjs/config/configUtils.d.ts +27 -6
  3. package/lib-commonjs/config/configUtils.js +122 -45
  4. package/lib-commonjs/config/configUtils.js.map +1 -1
  5. package/lib-commonjs/config/dependencyConfig.d.ts +3 -3
  6. package/lib-commonjs/config/dependencyConfig.js +128 -54
  7. package/lib-commonjs/config/dependencyConfig.js.map +1 -1
  8. package/lib-commonjs/config/projectConfig.d.ts +6 -3
  9. package/lib-commonjs/config/projectConfig.js +68 -12
  10. package/lib-commonjs/config/projectConfig.js.map +1 -1
  11. package/lib-commonjs/e2etest/autolink.test.d.ts +6 -0
  12. package/lib-commonjs/e2etest/autolink.test.js +435 -0
  13. package/lib-commonjs/e2etest/autolink.test.js.map +1 -0
  14. package/lib-commonjs/e2etest/dependencyConfig.test.d.ts +6 -0
  15. package/lib-commonjs/e2etest/dependencyConfig.test.js +162 -0
  16. package/lib-commonjs/e2etest/dependencyConfig.test.js.map +1 -0
  17. package/lib-commonjs/e2etest/projectConfig.test.d.ts +6 -0
  18. package/lib-commonjs/e2etest/projectConfig.test.js +125 -0
  19. package/lib-commonjs/e2etest/projectConfig.test.js.map +1 -0
  20. package/lib-commonjs/e2etest/projectConfig.utils.d.ts +4 -0
  21. package/lib-commonjs/e2etest/projectConfig.utils.js +63 -0
  22. package/lib-commonjs/e2etest/projectConfig.utils.js.map +1 -0
  23. package/lib-commonjs/e2etest/runWindows.test.d.ts +6 -0
  24. package/lib-commonjs/e2etest/runWindows.test.js +61 -0
  25. package/lib-commonjs/e2etest/runWindows.test.js.map +1 -0
  26. package/lib-commonjs/generator-common/index.d.ts +16 -0
  27. package/lib-commonjs/generator-common/index.js +60 -39
  28. package/lib-commonjs/generator-common/index.js.map +1 -1
  29. package/lib-commonjs/generator-windows/index.d.ts +2 -2
  30. package/lib-commonjs/generator-windows/index.js +250 -145
  31. package/lib-commonjs/generator-windows/index.js.map +1 -1
  32. package/lib-commonjs/healthChecks.d.ts +2 -0
  33. package/lib-commonjs/healthChecks.js +88 -0
  34. package/lib-commonjs/healthChecks.js.map +1 -0
  35. package/lib-commonjs/index.d.ts +5 -0
  36. package/lib-commonjs/index.js +24 -6
  37. package/lib-commonjs/index.js.map +1 -1
  38. package/lib-commonjs/runWindows/runWindows.js +203 -55
  39. package/lib-commonjs/runWindows/runWindows.js.map +1 -1
  40. package/lib-commonjs/runWindows/runWindowsOptions.d.ts +16 -10
  41. package/lib-commonjs/runWindows/runWindowsOptions.js +19 -11
  42. package/lib-commonjs/runWindows/runWindowsOptions.js.map +1 -1
  43. package/lib-commonjs/runWindows/utils/autolink.d.ts +88 -1
  44. package/lib-commonjs/runWindows/utils/autolink.js +592 -250
  45. package/lib-commonjs/runWindows/utils/autolink.js.map +1 -1
  46. package/lib-commonjs/runWindows/utils/build.d.ts +1 -2
  47. package/lib-commonjs/runWindows/utils/build.js +26 -33
  48. package/lib-commonjs/runWindows/utils/build.js.map +1 -1
  49. package/lib-commonjs/runWindows/utils/checkRequirements.js +11 -7
  50. package/lib-commonjs/runWindows/utils/checkRequirements.js.map +1 -1
  51. package/lib-commonjs/runWindows/utils/commandWithProgress.d.ts +11 -3
  52. package/lib-commonjs/runWindows/utils/commandWithProgress.js +57 -22
  53. package/lib-commonjs/runWindows/utils/commandWithProgress.js.map +1 -1
  54. package/lib-commonjs/runWindows/utils/deploy.d.ts +1 -1
  55. package/lib-commonjs/runWindows/utils/deploy.js +164 -82
  56. package/lib-commonjs/runWindows/utils/deploy.js.map +1 -1
  57. package/lib-commonjs/runWindows/utils/info.js +5 -2
  58. package/lib-commonjs/runWindows/utils/info.js.map +1 -1
  59. package/lib-commonjs/runWindows/utils/msbuildtools.d.ts +9 -5
  60. package/lib-commonjs/runWindows/utils/msbuildtools.js +95 -58
  61. package/lib-commonjs/runWindows/utils/msbuildtools.js.map +1 -1
  62. package/lib-commonjs/runWindows/utils/telemetryHelpers.d.ts +29 -0
  63. package/lib-commonjs/runWindows/utils/telemetryHelpers.js +109 -0
  64. package/lib-commonjs/runWindows/utils/telemetryHelpers.js.map +1 -0
  65. package/lib-commonjs/runWindows/utils/version.d.ts +4 -4
  66. package/lib-commonjs/runWindows/utils/version.js.map +1 -1
  67. package/lib-commonjs/runWindows/utils/vsInstalls.d.ts +4 -1
  68. package/lib-commonjs/runWindows/utils/vsInstalls.js +22 -7
  69. package/lib-commonjs/runWindows/utils/vsInstalls.js.map +1 -1
  70. package/lib-commonjs/runWindows/utils/vstools.d.ts +1 -0
  71. package/lib-commonjs/runWindows/utils/vstools.js +44 -25
  72. package/lib-commonjs/runWindows/utils/vstools.js.map +1 -1
  73. package/lib-commonjs/runWindows/utils/winappdeploytool.d.ts +3 -3
  74. package/lib-commonjs/runWindows/utils/winappdeploytool.js +17 -14
  75. package/lib-commonjs/runWindows/utils/winappdeploytool.js.map +1 -1
  76. package/package.json +43 -27
  77. package/powershell/Add-AppDevPackage.ps1 +2 -2
  78. package/powershell/WindowsStoreAppUtils.ps1 +11 -1
  79. package/CHANGELOG.json +0 -20
  80. package/CHANGELOG.md +0 -13
  81. package/templates/_gitignore +0 -92
  82. package/templates/b_gitignore +0 -1
  83. package/templates/cpp/keys/MyApp_TemporaryKey.pfx +0 -0
  84. package/templates/cpp/proj/MyApp.sln +0 -194
  85. package/templates/cpp/proj/MyApp.vcxproj +0 -220
  86. package/templates/cpp/proj/MyApp.vcxproj.filters +0 -63
  87. package/templates/cpp/proj/packages.config +0 -6
  88. package/templates/cpp/src/App.cpp +0 -80
  89. package/templates/cpp/src/App.h +0 -25
  90. package/templates/cpp/src/App.idl +0 -3
  91. package/templates/cpp/src/AutolinkedNativeModules.g.cpp +0 -13
  92. package/templates/cpp/src/AutolinkedNativeModules.g.h +0 -10
  93. package/templates/cpp/src/AutolinkedNativeModules.g.targets +0 -6
  94. package/templates/cpp/src/MainPage.cpp +0 -24
  95. package/templates/cpp/src/MainPage.h +0 -21
  96. package/templates/cpp/src/MainPage.idl +0 -8
  97. package/templates/cpp/src/PropertySheet.props +0 -16
  98. package/templates/cpp/src/ReactPackageProvider.cpp +0 -18
  99. package/templates/cpp/src/ReactPackageProvider.h +0 -15
  100. package/templates/cpp/src/pch.cpp +0 -1
  101. package/templates/cpp/src/pch.h +0 -26
  102. package/templates/cs/keys/MyApp_TemporaryKey.pfx +0 -0
  103. package/templates/cs/proj/MyApp.csproj +0 -189
  104. package/templates/cs/proj/MyApp.sln +0 -208
  105. package/templates/cs/src/App.xaml.cs +0 -56
  106. package/templates/cs/src/AutolinkedNativeModules.g.cs +0 -13
  107. package/templates/cs/src/AutolinkedNativeModules.g.targets +0 -6
  108. package/templates/cs/src/MainPage.xaml.cs +0 -33
  109. package/templates/cs/src/Properties/AssemblyInfo.cs +0 -29
  110. package/templates/cs/src/Properties/Default.rd.xml +0 -32
  111. package/templates/index.windows.bundle +0 -9
  112. package/templates/metro.config.js +0 -29
  113. package/templates/shared/assets/LockScreenLogo.scale-200.png +0 -0
  114. package/templates/shared/assets/SplashScreen.scale-200.png +0 -0
  115. package/templates/shared/assets/Square150x150Logo.scale-200.png +0 -0
  116. package/templates/shared/assets/Square44x44Logo.scale-200.png +0 -0
  117. package/templates/shared/assets/Square44x44Logo.targetsize-24_altform-unplated.png +0 -0
  118. package/templates/shared/assets/StoreLogo.png +0 -0
  119. package/templates/shared/assets/Wide310x150Logo.scale-200.png +0 -0
  120. package/templates/shared/proj/NuGet.Config +0 -13
  121. package/templates/shared/src/App.xaml +0 -10
  122. package/templates/shared/src/MainPage.xaml +0 -21
  123. package/templates/shared/src/Package.appxmanifest +0 -50
@@ -4,118 +4,146 @@
4
4
  * Licensed under the MIT License.
5
5
  * @format
6
6
  */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || function (mod) {
20
+ if (mod && mod.__esModule) return mod;
21
+ var result = {};
22
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
23
+ __setModuleDefault(result, mod);
24
+ return result;
25
+ };
26
+ var __importDefault = (this && this.__importDefault) || function (mod) {
27
+ return (mod && mod.__esModule) ? mod : { "default": mod };
28
+ };
7
29
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.autoLinkCommand = void 0;
9
- const fs = require("fs");
10
- const path = require("path");
11
- const chalk = require("chalk");
30
+ exports.autoLinkCommand = exports.autolinkOptions = exports.autolinkWindowsInternal = exports.AutolinkWindows = void 0;
31
+ // Types in this file are inaccurate compared to usage in terms of falsiness.
32
+ // We should try to rewrite some of this to do automated schema validation to
33
+ // guarantee correct types
34
+ /* eslint-disable @typescript-eslint/no-unnecessary-condition */
35
+ const fs_1 = __importDefault(require("@react-native-windows/fs"));
36
+ const path_1 = __importDefault(require("path"));
37
+ const chalk_1 = __importDefault(require("chalk"));
12
38
  const perf_hooks_1 = require("perf_hooks");
13
39
  const commandWithProgress_1 = require("./commandWithProgress");
14
- const vstools = require("./vstools");
15
- const generatorCommon = require("../../generator-common");
16
- const configUtils = require("../../config/configUtils");
17
- const templateRoot = path.join(__dirname, '../../../templates');
18
- /**
19
- * Logs the given message if verbose is True.
20
- * @param message The message to log.
21
- * @param verbose Whether or not verbose logging is enabled.
22
- */
23
- function verboseMessage(message, verbose) {
24
- if (verbose) {
25
- console.log(message);
26
- }
27
- }
28
- /**
29
- * Loads a source template file and performs the given replacements, normalizing CRLF.
30
- * @param srcFile Path to the source file.
31
- * @param replacements e.g. {'TextToBeReplaced': 'Replacement'}
32
- * @return The contents of the file with the replacements applied.
33
- */
34
- function getNormalizedContents(srcFile, replacements) {
35
- // Template files are CRLF, JS-generated replacements are LF, normalize replacements to CRLF
36
- for (var key in replacements) {
37
- replacements[key] = replacements[key].replace(/\n/g, '\r\n');
38
- }
39
- replacements.useMustache = true;
40
- return generatorCommon.resolveContents(srcFile, replacements);
41
- }
42
- /**
43
- * Updates the target file with the expected contents if it's different.
44
- * @param filePath Path to the target file to update.
45
- * @param expectedContents The expected contents of the file.
46
- * @param verbose If true, enable verbose logging.
47
- * @param checkMode It true, don't make any changes.
48
- * @return Whether any changes were necessary.
49
- */
50
- function updateFile(filePath, expectedContents, verbose, checkMode) {
51
- const fileName = chalk.bold(path.basename(filePath));
52
- verboseMessage(`Reading ${fileName}...`, verbose);
53
- const actualContents = fs.existsSync(filePath)
54
- ? fs.readFileSync(filePath).toString()
55
- : '';
56
- const contentsChanged = expectedContents !== actualContents;
57
- if (contentsChanged) {
58
- verboseMessage(chalk.yellow(`${fileName} needs to be updated.`), verbose);
59
- if (!checkMode) {
60
- verboseMessage(`Writing ${fileName}...`, verbose);
61
- fs.writeFileSync(filePath, expectedContents, {
62
- encoding: 'utf8',
63
- flag: 'w',
64
- });
40
+ const vstools = __importStar(require("./vstools"));
41
+ const generatorCommon = __importStar(require("../../generator-common"));
42
+ const configUtils = __importStar(require("../../config/configUtils"));
43
+ const telemetry_1 = require("@react-native-windows/telemetry");
44
+ const telemetryHelpers_1 = require("./telemetryHelpers");
45
+ const xmldom_1 = require("@xmldom/xmldom");
46
+ const formatter = require('xml-formatter');
47
+ class AutolinkWindows {
48
+ constructor(projectConfig, dependenciesConfig, options) {
49
+ this.projectConfig = projectConfig;
50
+ this.dependenciesConfig = dependenciesConfig;
51
+ this.options = options;
52
+ /** Cache of dependencies */
53
+ this.windowsDependencies = {};
54
+ this.changesNecessary = false;
55
+ if (!('windows' in this.projectConfig) ||
56
+ this.projectConfig.windows === null) {
57
+ throw new telemetry_1.CodedError('NoWindowsConfig', 'Windows auto-link only supported on Windows app projects');
65
58
  }
59
+ this.windowsAppConfig = projectConfig.windows;
66
60
  }
67
- else {
68
- verboseMessage(`No changes to ${fileName}.`, verbose);
61
+ areChangesNeeded() {
62
+ return this.changesNecessary;
69
63
  }
70
- return contentsChanged;
71
- }
72
- /**
73
- * Exits the script with the given status code.
74
- * @param statusCode The status code.
75
- * @param loggingWasEnabled Whether or not verbose lossing was enabled.
76
- */
77
- function exitProcessWithStatusCode(statusCode, loggingWasEnabled) {
78
- if (!loggingWasEnabled && statusCode !== 0) {
79
- console.log(`Error: Re-run the command with ${chalk.bold('--logging')} for more information.`);
64
+ getWindowsConfig() {
65
+ return this.windowsAppConfig;
80
66
  }
81
- process.exit(statusCode);
82
- }
83
- /**
84
- * Performs auto-linking for RNW native modules and apps.
85
- * @param args Unprocessed args passed from react-native CLI.
86
- * @param config Config passed from react-native CLI.
87
- * @param options Options passed from react-native CLI.
88
- */
89
- async function updateAutoLink(args, config, options) {
90
- const startTime = perf_hooks_1.performance.now();
91
- const verbose = options.logging;
92
- const checkMode = options.check;
93
- var changesNecessary = false;
94
- const spinner = commandWithProgress_1.newSpinner(checkMode ? 'Checking auto-linked files...' : 'Auto-linking...');
95
- verboseMessage('', verbose);
96
- try {
67
+ getSolutionFile() {
68
+ return path_1.default.join(this.getWindowsConfig().folder, this.getWindowsConfig().sourceDir, this.getWindowsConfig().solutionFile);
69
+ }
70
+ async run(spinner) {
71
+ const verbose = this.options.logging;
72
+ verboseMessage('', verbose);
97
73
  verboseMessage('Parsing project...', verbose);
98
- const projectConfig = config.project;
99
- if (!('windows' in projectConfig) || projectConfig.windows === null) {
100
- throw new Error('Windows auto-link only supported on Windows app projects');
74
+ const rnwRoot = resolveRnwRoot(this.windowsAppConfig);
75
+ const templateRoot = resolveTemplateRoot(this.windowsAppConfig);
76
+ this.fixUpForSlnOption();
77
+ this.fixUpForProjOption();
78
+ verboseMessage('Found Windows app project, config:', verbose);
79
+ verboseMessage(this.windowsAppConfig, verbose);
80
+ this.validateRequiredAppProperties();
81
+ const solutionFile = this.getSolutionFile();
82
+ const windowsAppProjectConfig = this.windowsAppConfig.project;
83
+ this.validateRequiredProjectProperties();
84
+ const projectFile = this.getProjectFile();
85
+ const projectDir = path_1.default.dirname(projectFile);
86
+ const projectLang = windowsAppProjectConfig.projectLang;
87
+ verboseMessage('Parsing dependencies...', verbose);
88
+ this.changesNecessary =
89
+ (await this.ensureXAMLDialect()) || this.changesNecessary;
90
+ // Generating cs/cpp files for app code consumption
91
+ if (projectLang === 'cs') {
92
+ this.changesNecessary =
93
+ (await this.generateCSAutolinking(templateRoot, projectLang, projectDir)) || this.changesNecessary;
94
+ }
95
+ else if (projectLang === 'cpp') {
96
+ this.changesNecessary =
97
+ (await this.generateCppAutolinking(templateRoot, projectLang, projectDir)) || this.changesNecessary;
101
98
  }
102
- var windowsAppConfig = projectConfig.windows;
103
- if (options.sln) {
104
- const slnFile = path.join(windowsAppConfig.folder, options.sln);
105
- windowsAppConfig.solutionFile = path.relative(path.join(windowsAppConfig.folder, windowsAppConfig.sourceDir), slnFile);
99
+ // Generating props for app project consumption
100
+ let propertiesForProps = '';
101
+ let csModuleNames = [];
102
+ if (projectLang === 'cpp') {
103
+ csModuleNames = this.getCSModules();
104
+ if (csModuleNames.length > 0) {
105
+ propertiesForProps += `
106
+ <!-- Set due to dependency on C# module(s): ${csModuleNames.join()} -->
107
+ <ConsumeCSharpModules Condition="'$(ConsumeCSharpModules)'==''">true</ConsumeCSharpModules>`;
108
+ }
106
109
  }
107
- if (options.proj) {
108
- const projFile = path.join(windowsAppConfig.folder, options.proj);
110
+ this.changesNecessary =
111
+ (await this.generateAutolinkProps(templateRoot, projectDir, propertiesForProps)) || this.changesNecessary;
112
+ // Generating targets for app project consumption
113
+ this.changesNecessary =
114
+ (await this.generateAutolinkTargets(projectDir, templateRoot)) ||
115
+ this.changesNecessary;
116
+ // Generating project entries for solution
117
+ this.changesNecessary =
118
+ this.updateSolution(rnwRoot, solutionFile) || this.changesNecessary;
119
+ spinner.succeed();
120
+ }
121
+ /**
122
+ * Handles the --proj command-line option by consuming its value into the windowsAppConfig
123
+ */
124
+ fixUpForProjOption() {
125
+ if (this.options.proj) {
126
+ const projFile = path_1.default.join(this.windowsAppConfig.folder, this.options.proj);
109
127
  const projectContents = configUtils.readProjectFile(projFile);
110
- windowsAppConfig.project = {
111
- projectFile: path.relative(path.join(windowsAppConfig.folder, windowsAppConfig.sourceDir), projFile),
128
+ this.windowsAppConfig.project = {
129
+ projectFile: path_1.default.relative(path_1.default.join(this.windowsAppConfig.folder, this.windowsAppConfig.sourceDir), projFile),
112
130
  projectName: configUtils.getProjectName(projFile, projectContents),
113
131
  projectLang: configUtils.getProjectLanguage(projFile),
114
- projectGuid: configUtils.getProjectGuid(projFile, projectContents),
132
+ projectGuid: configUtils.getProjectGuid(projectContents),
115
133
  };
116
134
  }
117
- verboseMessage('Found Windows app project, config:', verbose);
118
- verboseMessage(windowsAppConfig, verbose);
135
+ }
136
+ /**
137
+ * Handles the --sln command-line option by consuming its value into the windowsAppConfig
138
+ */
139
+ fixUpForSlnOption() {
140
+ if (this.options.sln) {
141
+ const slnFile = path_1.default.join(this.windowsAppConfig.folder, this.options.sln);
142
+ this.windowsAppConfig.solutionFile = path_1.default.relative(path_1.default.join(this.windowsAppConfig.folder, this.windowsAppConfig.sourceDir), slnFile);
143
+ }
144
+ }
145
+ /** Validates the all of the required app (solution) properties are present and valid */
146
+ validateRequiredAppProperties() {
119
147
  const alwaysRequired = [
120
148
  'folder',
121
149
  'sourceDir',
@@ -123,16 +151,24 @@ async function updateAutoLink(args, config, options) {
123
151
  'project',
124
152
  ];
125
153
  alwaysRequired.forEach(item => {
126
- if (!(item in windowsAppConfig) || windowsAppConfig[item] === null) {
127
- throw new Error(`${item} is required but not specified by react-native config`);
154
+ if (!(item in this.windowsAppConfig) ||
155
+ this.windowsAppConfig[item] === null) {
156
+ throw new telemetry_1.CodedError('IncompleteConfig', `${item} is required but not specified by react-native config`, { item: item });
128
157
  }
129
- else if (typeof windowsAppConfig[item] === 'string' &&
130
- windowsAppConfig[item].startsWith('Error: ')) {
131
- throw new Error(`${item} invalid. ${windowsAppConfig[item]}`);
158
+ else if (typeof this.windowsAppConfig[item] === 'string' &&
159
+ this.windowsAppConfig[item].startsWith('Error: ')) {
160
+ throw new telemetry_1.CodedError('InvalidConfig', `${item} invalid. ${this.windowsAppConfig[item]}`, { item: item });
132
161
  }
133
162
  });
134
- const solutionFile = path.join(windowsAppConfig.folder, windowsAppConfig.sourceDir, windowsAppConfig.solutionFile);
135
- const windowsAppProjectConfig = windowsAppConfig.project;
163
+ }
164
+ /** @return the full path to the project file (.vcxproj or .csproj) */
165
+ getProjectFile() {
166
+ const windowsAppConfig = this.getWindowsConfig();
167
+ return path_1.default.join(windowsAppConfig.folder, windowsAppConfig.sourceDir, windowsAppConfig.project.projectFile);
168
+ }
169
+ /** Validates that all of the required app _project_ properties are present and valid */
170
+ validateRequiredProjectProperties() {
171
+ const windowsAppProjectConfig = this.windowsAppConfig.project;
136
172
  const projectRequired = [
137
173
  'projectFile',
138
174
  'projectName',
@@ -142,144 +178,214 @@ async function updateAutoLink(args, config, options) {
142
178
  projectRequired.forEach(item => {
143
179
  if (!(item in windowsAppProjectConfig) ||
144
180
  windowsAppProjectConfig[item] === null) {
145
- throw new Error(`project.${item} is required but not specified by react-native config`);
181
+ throw new telemetry_1.CodedError('IncompleteConfig', `project.${item} is required but not specified by react-native config`, { item: item });
146
182
  }
147
183
  else if (typeof windowsAppProjectConfig[item] === 'string' &&
148
184
  windowsAppProjectConfig[item].startsWith('Error: ')) {
149
- throw new Error(`project.${item} invalid. ${windowsAppProjectConfig[item]}`);
185
+ throw new telemetry_1.CodedError('InvalidConfig', `project.${item} invalid. ${windowsAppProjectConfig[item]}`, { item: item });
150
186
  }
151
187
  });
152
- const projectFile = path.join(windowsAppConfig.folder, windowsAppConfig.sourceDir, windowsAppConfig.project.projectFile);
153
- const projectDir = path.dirname(projectFile);
154
- const projectLang = windowsAppConfig.project.projectLang;
155
- verboseMessage('Parsing dependencies...', verbose);
156
- const dependenciesConfig = config.dependencies;
157
- let windowsDependencies = {};
158
- for (const dependencyName in dependenciesConfig) {
159
- const windowsDependency = dependenciesConfig[dependencyName].platforms.windows;
160
- if (windowsDependency) {
161
- verboseMessage(`${chalk.bold(dependencyName)} has Windows implementation, config:`, verbose);
162
- verboseMessage(windowsDependency, verbose);
163
- var dependencyIsValid = true;
164
- dependencyIsValid = !!(dependencyIsValid &&
165
- 'sourceDir' in windowsDependency &&
166
- windowsDependency.sourceDir &&
167
- !windowsDependency.sourceDir.startsWith('Error: '));
168
- if ('projects' in windowsDependency &&
169
- Array.isArray(windowsDependency.projects)) {
170
- windowsDependency.projects.forEach(project => {
171
- const itemsToCheck = [
172
- 'projectFile',
173
- 'directDependency',
174
- ];
175
- itemsToCheck.forEach(item => {
176
- dependencyIsValid = !!(dependencyIsValid &&
177
- item in project &&
178
- project[item] &&
179
- !project[item].toString().startsWith('Error: '));
180
- });
188
+ }
189
+ async generateCppAutolinking(templateRoot, projectLang, projectDir) {
190
+ const { cppPackageProviders, cppIncludes } = this.getCppReplacements();
191
+ const cppFileName = 'AutolinkedNativeModules.g.cpp';
192
+ const srcCppFile = path_1.default.join(templateRoot, `${projectLang}-app`, 'src', cppFileName);
193
+ const destCppFile = path_1.default.join(projectDir, cppFileName);
194
+ verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(destCppFile))}...`, this.options.logging);
195
+ const cppContents = generatorCommon.resolveContents(srcCppFile, {
196
+ useMustache: true,
197
+ autolinkCppIncludes: cppIncludes,
198
+ autolinkCppPackageProviders: cppPackageProviders,
199
+ });
200
+ return await this.updateFile(destCppFile, cppContents);
201
+ }
202
+ getCppReplacements() {
203
+ let cppIncludes = '';
204
+ let cppPackageProviders = '';
205
+ const windowsDependencies = this.getWindowsDependencies();
206
+ for (const dependencyName of Object.keys(windowsDependencies)) {
207
+ windowsDependencies[dependencyName].projects.forEach(project => {
208
+ if (project.directDependency) {
209
+ cppIncludes += `\n\n// Includes from ${dependencyName}`;
210
+ project.cppHeaders.forEach(header => {
211
+ cppIncludes += `\n#include <${header}>`;
212
+ });
213
+ cppPackageProviders += `\n // IReactPackageProviders from ${dependencyName}`;
214
+ project.cppPackageProviders.forEach(packageProvider => {
215
+ cppPackageProviders += `\n packageProviders.Append(winrt::${packageProvider}());`;
181
216
  });
182
217
  }
183
- if (dependencyIsValid) {
184
- verboseMessage(`Adding ${chalk.bold(dependencyName)}.`, verbose);
185
- windowsDependencies[dependencyName] = windowsDependency;
218
+ });
219
+ }
220
+ if (cppPackageProviders === '') {
221
+ // There are no windows dependencies, this would result in warning. C4100: 'packageProviders': unreferenced formal parameter.
222
+ // therefore add a usage.
223
+ cppPackageProviders = '\n UNREFERENCED_PARAMETER(packageProviders);'; // CODESYNC: vnext\local-cli\generator-windows\index.js
224
+ }
225
+ return { cppPackageProviders, cppIncludes };
226
+ }
227
+ generateCSAutolinking(templateRoot, projectLang, projectDir) {
228
+ const { csUsingNamespaces, csReactPackageProviders, } = this.getCsReplacements();
229
+ const csFileName = 'AutolinkedNativeModules.g.cs';
230
+ const srcCsFile = path_1.default.join(templateRoot, `${projectLang}-app`, 'src', csFileName);
231
+ const destCsFile = path_1.default.join(projectDir, csFileName);
232
+ verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(destCsFile))}...`, this.options.logging);
233
+ const csContents = generatorCommon.resolveContents(srcCsFile, {
234
+ useMustache: true,
235
+ autolinkCsUsingNamespaces: csUsingNamespaces,
236
+ autolinkCsReactPackageProviders: csReactPackageProviders,
237
+ });
238
+ return this.updateFile(destCsFile, csContents);
239
+ }
240
+ getCsReplacements() {
241
+ let csUsingNamespaces = '';
242
+ let csReactPackageProviders = '';
243
+ const windowsDependencies = this.getWindowsDependencies();
244
+ for (const dependencyName of Object.keys(windowsDependencies)) {
245
+ windowsDependencies[dependencyName].projects.forEach(project => {
246
+ if (project.directDependency) {
247
+ csUsingNamespaces += `\n\n// Namespaces from ${dependencyName}`;
248
+ project.csNamespaces.forEach(namespace => {
249
+ csUsingNamespaces += `\nusing ${namespace};`;
250
+ });
251
+ csReactPackageProviders += `\n // IReactPackageProviders from ${dependencyName}`;
252
+ project.csPackageProviders.forEach(packageProvider => {
253
+ csReactPackageProviders += `\n packageProviders.Add(new ${packageProvider}());`;
254
+ });
186
255
  }
187
- }
256
+ });
188
257
  }
189
- // Generating cs/h files for app code consumption
190
- if (projectLang === 'cs') {
191
- let csUsingNamespaces = '';
192
- let csReactPacakgeProviders = '';
193
- for (const dependencyName in windowsDependencies) {
194
- windowsDependencies[dependencyName].projects.forEach(project => {
195
- if (project.directDependency) {
196
- csUsingNamespaces += `\n\n// Namespaces from ${dependencyName}`;
197
- project.csNamespaces.forEach(namespace => {
198
- csUsingNamespaces += `\nusing ${namespace};`;
199
- });
200
- csReactPacakgeProviders += `\n // IReactPackageProviders from ${dependencyName}`;
201
- project.csPackageProviders.forEach(packageProvider => {
202
- csReactPacakgeProviders += `\n packageProviders.Add(new ${packageProvider}());`;
258
+ return { csUsingNamespaces, csReactPackageProviders };
259
+ }
260
+ getWindowsDependencies() {
261
+ if (Object.keys(this.windowsDependencies).length === 0) {
262
+ for (const dependencyName of Object.keys(this.dependenciesConfig)) {
263
+ const windowsDependency = this
264
+ .dependenciesConfig[dependencyName].platforms.windows;
265
+ if (windowsDependency) {
266
+ verboseMessage(`${chalk_1.default.bold(dependencyName)} has Windows implementation, config:`, this.options.logging);
267
+ verboseMessage(windowsDependency, this.options.logging);
268
+ let dependencyIsValid = true;
269
+ dependencyIsValid = !!(dependencyIsValid &&
270
+ 'sourceDir' in windowsDependency &&
271
+ windowsDependency.sourceDir &&
272
+ !windowsDependency.sourceDir.startsWith('Error: '));
273
+ if ('projects' in windowsDependency &&
274
+ Array.isArray(windowsDependency.projects)) {
275
+ if (windowsDependency.projects.length === 0 &&
276
+ dependencyName.includes('react-native')) {
277
+ // the dependency is probably a react native module, but we didn't find a module project
278
+ throw new telemetry_1.CodedError('Autolinking', `Found a Windows solution for ${dependencyName} but no React Native for Windows native module projects`);
279
+ }
280
+ windowsDependency.projects.forEach(project => {
281
+ const itemsToCheck = [
282
+ 'projectFile',
283
+ 'directDependency',
284
+ ];
285
+ itemsToCheck.forEach(item => {
286
+ dependencyIsValid = !!(dependencyIsValid &&
287
+ item in project &&
288
+ project[item] !== '' &&
289
+ !project[item].toString().startsWith('Error: '));
290
+ });
203
291
  });
204
292
  }
205
- });
293
+ if (dependencyIsValid) {
294
+ verboseMessage(`Adding ${chalk_1.default.bold(dependencyName)}.`, this.options.logging);
295
+ this.windowsDependencies[dependencyName] = windowsDependency;
296
+ }
297
+ else {
298
+ verboseMessage(`Invalid dependency configuration for dependency ${dependencyName}`, this.options.logging);
299
+ }
300
+ }
206
301
  }
207
- const csFileName = 'AutolinkedNativeModules.g.cs';
208
- const srcCsFile = path.join(templateRoot, projectLang, 'src', csFileName);
209
- const destCsFile = path.join(projectDir, csFileName);
210
- verboseMessage(`Calculating ${chalk.bold(path.basename(destCsFile))}...`, verbose);
211
- const csContents = getNormalizedContents(srcCsFile, {
212
- autolinkCsUsingNamespaces: csUsingNamespaces,
213
- autolinkCsReactPacakgeProviders: csReactPacakgeProviders,
214
- });
215
- changesNecessary =
216
- updateFile(destCsFile, csContents, verbose, checkMode) ||
217
- changesNecessary;
218
302
  }
219
- else if (projectLang === 'cpp') {
220
- let cppIncludes = '';
221
- let cppPackageProviders = '';
222
- for (const dependencyName in windowsDependencies) {
223
- windowsDependencies[dependencyName].projects.forEach(project => {
224
- if (project.directDependency) {
225
- cppIncludes += `\n\n// Includes from ${dependencyName}`;
226
- project.cppHeaders.forEach(header => {
227
- cppIncludes += `\n#include <${header}>`;
228
- });
229
- cppPackageProviders += `\n // IReactPackageProviders from ${dependencyName}`;
230
- project.cppPackageProviders.forEach(packageProvider => {
231
- cppPackageProviders += `\n packageProviders.Append(winrt::${packageProvider}());`;
232
- });
233
- }
303
+ return this.windowsDependencies;
304
+ }
305
+ /**
306
+ * Updates the target file with the expected contents if it's different.
307
+ * @param filePath Path to the target file to update.
308
+ * @param expectedContents The expected contents of the file.
309
+ * @return Whether any changes were necessary.
310
+ */
311
+ async updateFile(filePath, expectedContents) {
312
+ const fileName = chalk_1.default.bold(path_1.default.basename(filePath));
313
+ verboseMessage(`Reading ${fileName}...`, this.options.logging);
314
+ const actualContents = fs_1.default.existsSync(filePath)
315
+ ? (await fs_1.default.readFile(filePath)).toString()
316
+ : '';
317
+ const contentsChanged = expectedContents !== actualContents;
318
+ if (contentsChanged) {
319
+ verboseMessage(chalk_1.default.yellow(`${fileName} needs to be updated.`), this.options.logging);
320
+ if (!this.options.check) {
321
+ verboseMessage(`Writing ${fileName}...`, this.options.logging);
322
+ await fs_1.default.writeFile(filePath, expectedContents, {
323
+ encoding: 'utf8',
324
+ flag: 'w',
234
325
  });
235
326
  }
236
- if (cppPackageProviders === '') {
237
- // There are no windows dependencies, this would result in warning. C4100: 'packageProviders': unreferenced formal parameter.
238
- // therefore add a usage.
239
- cppPackageProviders = '\n UNREFERENCED_PARAMETER(packageProviders);'; // CODESYNC: vnext\local-cli\generator-windows\index.js
240
- }
241
- const cppFileName = 'AutolinkedNativeModules.g.cpp';
242
- const srcCppFile = path.join(templateRoot, projectLang, 'src', cppFileName);
243
- const destCppFile = path.join(projectDir, cppFileName);
244
- verboseMessage(`Calculating ${chalk.bold(path.basename(destCppFile))}...`, verbose);
245
- const cppContents = getNormalizedContents(srcCppFile, {
246
- autolinkCppIncludes: cppIncludes,
247
- autolinkCppPackageProviders: cppPackageProviders,
248
- });
249
- changesNecessary =
250
- updateFile(destCppFile, cppContents, verbose, checkMode) ||
251
- changesNecessary;
252
327
  }
253
- // Generating targets for app project consumption
328
+ else {
329
+ verboseMessage(`No changes to ${fileName}.`, this.options.logging);
330
+ }
331
+ return contentsChanged;
332
+ }
333
+ generateAutolinkTargets(projectDir, templateRoot) {
254
334
  let projectReferencesForTargets = '';
255
- for (const dependencyName in windowsDependencies) {
335
+ const windowsDependencies = this.getWindowsDependencies();
336
+ for (const dependencyName of Object.keys(windowsDependencies)) {
256
337
  windowsDependencies[dependencyName].projects.forEach(project => {
257
338
  if (project.directDependency) {
258
- const dependencyProjectFile = path.join(windowsDependencies[dependencyName].folder, windowsDependencies[dependencyName].sourceDir, project.projectFile);
259
- const relDependencyProjectFile = path.relative(projectDir, dependencyProjectFile);
260
- projectReferencesForTargets += `\n <!-- Projects from ${dependencyName} -->`;
261
- projectReferencesForTargets += `\n <ProjectReference Include="$(ProjectDir)${relDependencyProjectFile}">
339
+ const dependencyProjectFile = path_1.default.join(windowsDependencies[dependencyName].folder, windowsDependencies[dependencyName].sourceDir, project.projectFile);
340
+ const relDependencyProjectFile = path_1.default.relative(projectDir, dependencyProjectFile);
341
+ projectReferencesForTargets += `
342
+ <!-- Projects from ${dependencyName} -->
343
+ <ProjectReference Include="$(ProjectDir)${relDependencyProjectFile}">
262
344
  <Project>${project.projectGuid}</Project>
263
345
  </ProjectReference>`;
264
346
  }
265
347
  });
266
348
  }
267
349
  const targetFileName = 'AutolinkedNativeModules.g.targets';
268
- const srcTargetFile = path.join(templateRoot, projectLang, 'src', targetFileName);
269
- const destTargetFile = path.join(projectDir, targetFileName);
270
- verboseMessage(`Calculating ${chalk.bold(path.basename(destTargetFile))}...`, verbose);
271
- const targetContents = getNormalizedContents(srcTargetFile, {
350
+ const srcTargetFile = path_1.default.join(templateRoot, `shared-app`, 'src', targetFileName);
351
+ const destTargetFile = path_1.default.join(projectDir, targetFileName);
352
+ verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(destTargetFile))}...`, this.options.logging);
353
+ const targetContents = generatorCommon.resolveContents(srcTargetFile, {
354
+ useMustache: true,
272
355
  autolinkProjectReferencesForTargets: projectReferencesForTargets,
273
356
  });
274
- changesNecessary =
275
- updateFile(destTargetFile, targetContents, verbose, checkMode) ||
276
- changesNecessary;
277
- // Generating project entries for solution
278
- let projectsForSolution = [];
279
- for (const dependencyName in windowsDependencies) {
280
- // Process projects
357
+ return this.updateFile(destTargetFile, targetContents);
358
+ }
359
+ generateAutolinkProps(templateRoot, projectDir, propertiesForProps) {
360
+ const propsFileName = 'AutolinkedNativeModules.g.props';
361
+ const srcPropsFile = path_1.default.join(templateRoot, `shared-app`, 'src', propsFileName);
362
+ const destPropsFile = path_1.default.join(projectDir, propsFileName);
363
+ verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(destPropsFile))}...`, this.options.logging);
364
+ const propsContents = generatorCommon.resolveContents(srcPropsFile, {
365
+ useMustache: true,
366
+ autolinkPropertiesForProps: propertiesForProps,
367
+ });
368
+ return this.updateFile(destPropsFile, propsContents);
369
+ }
370
+ getCSModules() {
371
+ const csModuleNames = [];
372
+ const windowsDependencies = this.getWindowsDependencies();
373
+ for (const dependencyName of Object.keys(windowsDependencies)) {
281
374
  windowsDependencies[dependencyName].projects.forEach(project => {
282
- const dependencyProjectFile = path.join(windowsDependencies[dependencyName].folder, windowsDependencies[dependencyName].sourceDir, project.projectFile);
375
+ if (project.directDependency && project.projectLang === 'cs') {
376
+ csModuleNames.push(project.projectName);
377
+ }
378
+ });
379
+ }
380
+ return csModuleNames;
381
+ }
382
+ updateSolution(rnwRoot, solutionFile) {
383
+ const projectsForSolution = [];
384
+ const windowsDependencies = this.getWindowsDependencies();
385
+ for (const dependencyName of Object.keys(windowsDependencies)) {
386
+ // Process dependency projects
387
+ windowsDependencies[dependencyName].projects.forEach(project => {
388
+ const dependencyProjectFile = path_1.default.join(windowsDependencies[dependencyName].folder, windowsDependencies[dependencyName].sourceDir, project.projectFile);
283
389
  projectsForSolution.push({
284
390
  projectFile: dependencyProjectFile,
285
391
  projectName: project.projectName,
@@ -288,56 +394,292 @@ async function updateAutoLink(args, config, options) {
288
394
  });
289
395
  });
290
396
  }
291
- verboseMessage(`Calculating ${chalk.bold(path.basename(solutionFile))} changes...`, verbose);
397
+ const csModuleNames = this.getCSModules();
398
+ if (csModuleNames.length > 0) {
399
+ // Add managed projects
400
+ projectsForSolution.push({
401
+ projectFile: path_1.default.join(rnwRoot, 'Microsoft.ReactNative.Managed/Microsoft.ReactNative.Managed.csproj'),
402
+ projectName: 'Microsoft.ReactNative.Managed',
403
+ projectLang: 'cs',
404
+ projectGuid: '{F2824844-CE15-4242-9420-308923CD76C3}',
405
+ });
406
+ projectsForSolution.push({
407
+ projectFile: path_1.default.join(rnwRoot, 'Microsoft.ReactNative.Managed.CodeGen//Microsoft.ReactNative.Managed.CodeGen.csproj'),
408
+ projectName: 'Microsoft.ReactNative.Managed.CodeGen',
409
+ projectLang: 'cs',
410
+ projectGuid: '{ADED4FBE-887D-4271-AF24-F0823BCE7961}',
411
+ projectTypeGuid: vstools.dotNetCoreProjectTypeGuid,
412
+ });
413
+ }
414
+ verboseMessage(`Calculating ${chalk_1.default.bold(path_1.default.basename(solutionFile))} changes...`, this.options.logging);
415
+ let changesNecessary = false;
292
416
  projectsForSolution.forEach(project => {
293
- const contentsChanged = vstools.addProjectToSolution(solutionFile, project, verbose, checkMode);
417
+ const contentsChanged = vstools.addProjectToSolution(solutionFile, project, this.options.logging, this.options.check);
294
418
  changesNecessary = changesNecessary || contentsChanged;
295
419
  });
296
- spinner.succeed();
297
- var endTime = perf_hooks_1.performance.now();
298
- if (!changesNecessary) {
299
- console.log(`${chalk.green('Success:')} No auto-linking changes necessary. (${Math.round(endTime - startTime)}ms)`);
420
+ return changesNecessary;
421
+ }
422
+ getExperimentalFeaturesPropsXml() {
423
+ const experimentalFeaturesProps = path_1.default.join(path_1.default.dirname(this.getSolutionFile()), 'ExperimentalFeatures.props');
424
+ if (fs_1.default.existsSync(experimentalFeaturesProps)) {
425
+ const experimentalFeaturesContents = configUtils.readProjectFile(experimentalFeaturesProps);
426
+ return {
427
+ path: experimentalFeaturesProps,
428
+ content: experimentalFeaturesContents,
429
+ };
430
+ }
431
+ return undefined;
432
+ }
433
+ async ensureXAMLDialect() {
434
+ var _a, _b;
435
+ let changesNeeded = false;
436
+ const useWinUI3FromConfig = this.getWindowsConfig().useWinUI3;
437
+ const experimentalFeatures = this.getExperimentalFeaturesPropsXml();
438
+ if (experimentalFeatures) {
439
+ const useWinUI3FromExperimentalFeatures = ((_a = configUtils
440
+ .tryFindPropertyValue(experimentalFeatures.content, 'UseWinUI3')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'true';
441
+ // Check if WinUI2xVersion is specified in experimental features
442
+ const targetWinUI2xVersion = configUtils.tryFindPropertyValue(experimentalFeatures.content, 'WinUI2xVersion');
443
+ // Check if WinUI3Version is specified in experimental features
444
+ const targetWinUI3xVersion = configUtils.tryFindPropertyValue(experimentalFeatures.content, 'WinUI3Version');
445
+ // Use the UseWinUI3 value in react-native.config.js, or if not present, the value from ExperimentalFeatures.props
446
+ changesNeeded = await this.updatePackagesConfigXAMLDialect(useWinUI3FromConfig !== undefined
447
+ ? useWinUI3FromConfig
448
+ : useWinUI3FromExperimentalFeatures, targetWinUI2xVersion, targetWinUI3xVersion);
449
+ if (useWinUI3FromConfig !== undefined) {
450
+ // Make sure ExperimentalFeatures.props matches the value that comes from react-native.config.js
451
+ const node = experimentalFeatures.content.getElementsByTagName('UseWinUI3');
452
+ const newValue = useWinUI3FromConfig ? 'true' : 'false';
453
+ changesNeeded = ((_b = node.item(0)) === null || _b === void 0 ? void 0 : _b.textContent) !== newValue || changesNeeded;
454
+ if (!this.options.check && changesNeeded) {
455
+ node.item(0).textContent = newValue;
456
+ const experimentalFeaturesOutput = new xmldom_1.XMLSerializer().serializeToString(experimentalFeatures.content);
457
+ await this.updateFile(experimentalFeatures.path, experimentalFeaturesOutput);
458
+ }
459
+ }
460
+ }
461
+ return changesNeeded;
462
+ }
463
+ getPackagesConfigXml() {
464
+ const projectFile = this.getProjectFile();
465
+ const packagesConfig = path_1.default.join(path_1.default.dirname(projectFile), 'packages.config');
466
+ if (fs_1.default.existsSync(packagesConfig)) {
467
+ return {
468
+ path: packagesConfig,
469
+ content: configUtils.readProjectFile(packagesConfig),
470
+ };
471
+ }
472
+ return undefined;
473
+ }
474
+ async updatePackagesConfigXAMLDialect(useWinUI3, targetWinUI2xVersion, targetWinUI3xVersion) {
475
+ let changed = false;
476
+ const packagesConfig = this.getPackagesConfigXml();
477
+ if (packagesConfig) {
478
+ // if we don't have a packages.config, then this is a C# project, in which case we use <PackageReference> and dynamically pick the right XAML package.
479
+ const project = this.getWindowsConfig();
480
+ const winUIPropsPath = path_1.default.join(resolveRnwRoot(project), 'PropertySheets/WinUI.props');
481
+ const winuiPropsContents = configUtils.readProjectFile(winUIPropsPath);
482
+ // Use the given WinUI2xVersion, otherwise fallback to WinUI.props
483
+ const winui2xVersion = targetWinUI2xVersion !== null && targetWinUI2xVersion !== void 0 ? targetWinUI2xVersion : configUtils.tryFindPropertyValue(winuiPropsContents, 'WinUI2xVersion');
484
+ // Use the given WinUI3Version, otherwise fallback to WinUI.props
485
+ const winui3Version = targetWinUI3xVersion !== null && targetWinUI3xVersion !== void 0 ? targetWinUI3xVersion : configUtils.tryFindPropertyValue(winuiPropsContents, 'WinUI3Version');
486
+ const dialects = [
487
+ { id: 'Microsoft.WinUI', version: winui3Version },
488
+ { id: 'Microsoft.UI.Xaml', version: winui2xVersion },
489
+ ];
490
+ const keepPkg = useWinUI3 ? dialects[0] : dialects[1];
491
+ const removePkg = useWinUI3 ? dialects[1] : dialects[0];
492
+ changed = this.updatePackagesConfig(packagesConfig, [removePkg], [keepPkg]);
493
+ if (!this.options.check && changed) {
494
+ const serializer = new xmldom_1.XMLSerializer();
495
+ const output = serializer.serializeToString(packagesConfig.content);
496
+ const formattedXml = formatter(output, { indentation: ' ' });
497
+ await this.updateFile(packagesConfig.path, formattedXml);
498
+ }
499
+ }
500
+ return changed;
501
+ }
502
+ updatePackagesConfig(packagesConfig, removePkgs, keepPkgs) {
503
+ let changed = false;
504
+ const packageElements = packagesConfig.content.documentElement.getElementsByTagName('package');
505
+ const nodesToRemove = [];
506
+ for (let i = 0; i < packageElements.length; i++) {
507
+ const packageElement = packageElements.item(i);
508
+ const idAttr = packageElement.getAttributeNode('id');
509
+ const id = idAttr.value;
510
+ const keepPkg = keepPkgs.find(pkg => pkg.id === id);
511
+ if (removePkgs.find(pkg => pkg.id === id)) {
512
+ nodesToRemove.push(packageElement);
513
+ changed = true;
514
+ }
515
+ else if (keepPkg) {
516
+ changed =
517
+ changed || keepPkg.version !== packageElement.getAttribute('version');
518
+ packageElement.setAttribute('version', keepPkg.version);
519
+ keepPkgs = keepPkgs.filter(pkg => pkg.id !== keepPkg.id);
520
+ }
521
+ }
522
+ nodesToRemove.forEach(pkg => packagesConfig.content.documentElement.removeChild(pkg));
523
+ keepPkgs.forEach(keepPkg => {
524
+ const newPkg = packagesConfig.content.createElement('package');
525
+ Object.entries(keepPkg).forEach(([attr, value]) => {
526
+ newPkg.setAttribute(attr, value);
527
+ });
528
+ newPkg.setAttribute('targetFramework', 'native');
529
+ packagesConfig.content.documentElement.appendChild(newPkg);
530
+ changed = true;
531
+ });
532
+ return changed;
533
+ }
534
+ /** @return The CLI command to invoke autolink-windows independently */
535
+ getAutolinkWindowsCommand() {
536
+ const folder = this.windowsAppConfig.folder;
537
+ const autolinkCommand = 'npx react-native autolink-windows';
538
+ const autolinkArgs = `--sln "${path_1.default.relative(folder, this.getSolutionFile())}" --proj "${path_1.default.relative(folder, this.getProjectFile())}"`;
539
+ return `${autolinkCommand} ${autolinkArgs}`;
540
+ }
541
+ }
542
+ exports.AutolinkWindows = AutolinkWindows;
543
+ /**
544
+ * Locates the react-native-windows directory
545
+ * @param config project configuration
546
+ */
547
+ function resolveRnwRoot(projectConfig) {
548
+ const rnwPackage = path_1.default.dirname(require.resolve('react-native-windows/package.json', {
549
+ paths: [projectConfig.folder],
550
+ }));
551
+ return rnwPackage;
552
+ }
553
+ /**
554
+ * Locates the react-native-windows directory containing template files
555
+ * @param config project configuration
556
+ */
557
+ function resolveTemplateRoot(projectConfig) {
558
+ const rnwPackage = resolveRnwRoot(projectConfig);
559
+ return path_1.default.join(rnwPackage, 'template');
560
+ }
561
+ /**
562
+ * Logs the given message if verbose is True.
563
+ * @param message The message to log.
564
+ * @param verbose Whether or not verbose logging is enabled.
565
+ */
566
+ function verboseMessage(message, verbose) {
567
+ if (verbose) {
568
+ console.log(message);
569
+ }
570
+ }
571
+ /**
572
+ * Sanitizes the given option for telemetery.
573
+ * @param key The key of the option.
574
+ * @param value The unsanitized value of the option.
575
+ * @returns The sanitized value of the option.
576
+ */
577
+ function optionSanitizer(key, value) {
578
+ // Do not add a default case here.
579
+ // Strings risking PII should just return true if present, false otherwise.
580
+ // All others should return the value (or false if undefined).
581
+ switch (key) {
582
+ case 'sln':
583
+ case 'proj':
584
+ return value === undefined ? false : true; // Strip PII
585
+ case 'logging':
586
+ case 'check':
587
+ case 'telemetry':
588
+ return value === undefined ? false : value; // Return value
589
+ }
590
+ }
591
+ /**
592
+ * Get the extra props to add to the `autolink-windows` telemetry event.
593
+ * @returns The extra props.
594
+ */
595
+ async function getExtraProps() {
596
+ const extraProps = {};
597
+ return extraProps;
598
+ }
599
+ /**
600
+ * The function run when calling `react-native autolink-windows`.
601
+ * @param args Unprocessed args passed from react-native CLI.
602
+ * @param config Config passed from react-native CLI.
603
+ * @param options Options passed from react-native CLI.
604
+ */
605
+ async function autolinkWindows(args, config, options) {
606
+ await (0, telemetryHelpers_1.startTelemetrySession)('autolink-windows', config, options, (0, telemetryHelpers_1.getDefaultOptions)(config, exports.autolinkOptions), optionSanitizer);
607
+ let autolinkWindowsError;
608
+ try {
609
+ await autolinkWindowsInternal(args, config, options);
610
+ }
611
+ catch (ex) {
612
+ autolinkWindowsError =
613
+ ex instanceof Error ? ex : new Error(String(ex));
614
+ telemetry_1.Telemetry.trackException(autolinkWindowsError);
615
+ }
616
+ await (0, telemetryHelpers_1.endTelemetrySession)(autolinkWindowsError, getExtraProps);
617
+ (0, commandWithProgress_1.setExitProcessWithError)(options.logging, autolinkWindowsError);
618
+ }
619
+ /**
620
+ * Performs auto-linking for RNW native modules and apps.
621
+ * @param args Unprocessed args passed from react-native CLI.
622
+ * @param config Config passed from react-native CLI.
623
+ * @param options Options passed from react-native CLI.
624
+ */
625
+ async function autolinkWindowsInternal(args, config, options) {
626
+ const startTime = perf_hooks_1.performance.now();
627
+ const spinner = (0, commandWithProgress_1.newSpinner)(options.check ? 'Checking auto-linked files...' : 'Auto-linking...');
628
+ try {
629
+ const autolink = new AutolinkWindows(config.project, config.dependencies, options);
630
+ await autolink.run(spinner);
631
+ const endTime = perf_hooks_1.performance.now();
632
+ if (!autolink.areChangesNeeded()) {
633
+ console.log(`${chalk_1.default.green('Success:')} No auto-linking changes necessary. (${Math.round(endTime - startTime)}ms)`);
300
634
  }
301
- else if (checkMode) {
302
- console.log(`${chalk.yellow('Warning:')} Auto-linking changes were necessary but ${chalk.bold('--check')} specified. Run ${chalk.bold("'npx react-native autolink-windows'")} to apply the changes. (${Math.round(endTime - startTime)}ms)`);
303
- exitProcessWithStatusCode(0, verbose);
635
+ else if (options.check) {
636
+ const autolinkCommand = autolink.getAutolinkWindowsCommand();
637
+ console.log(`${chalk_1.default.yellow('Warning:')} Auto-linking changes were necessary but ${chalk_1.default.bold('--check')} specified. Run '${chalk_1.default.bold(`${autolinkCommand}`)}' to apply the changes. (${Math.round(endTime - startTime)}ms)`);
638
+ throw new telemetry_1.CodedError('NeedAutolinking', `Auto-linking changes were necessary but --check was specified. Run '${autolinkCommand}' to apply the changes`);
304
639
  }
305
640
  else {
306
- console.log(`${chalk.green('Success:')} Auto-linking changes completed. (${Math.round(endTime - startTime)}ms)`);
641
+ console.log(`${chalk_1.default.green('Success:')} Auto-linking changes completed. (${Math.round(endTime - startTime)}ms)`);
307
642
  }
308
643
  }
309
644
  catch (e) {
310
645
  spinner.fail();
311
- var endTime = perf_hooks_1.performance.now();
312
- console.log(`${chalk.red('Error:')} ${e.toString()}. (${Math.round(endTime - startTime)}ms)`);
313
- exitProcessWithStatusCode(1, verbose);
646
+ const endTime = perf_hooks_1.performance.now();
647
+ console.log(`${chalk_1.default.red('Error:')} ${e.toString()}. (${Math.round(endTime - startTime)}ms)`);
648
+ throw e;
314
649
  }
315
650
  }
651
+ exports.autolinkWindowsInternal = autolinkWindowsInternal;
652
+ exports.autolinkOptions = [
653
+ {
654
+ name: '--logging',
655
+ description: 'Verbose output logging',
656
+ },
657
+ {
658
+ name: '--check',
659
+ description: 'Only check whether any autolinked files need to change',
660
+ },
661
+ {
662
+ name: '--sln [string]',
663
+ description: "Override the app solution file determined by 'react-native config', e.g. windows\\myApp.sln",
664
+ default: undefined,
665
+ },
666
+ {
667
+ name: '--proj [string]',
668
+ description: "Override the app project file determined by 'react-native config', e.g. windows\\myApp\\myApp.vcxproj",
669
+ default: undefined,
670
+ },
671
+ {
672
+ name: '--no-telemetry',
673
+ description: 'Disables sending telemetry that allows analysis of usage and failures of the react-native-windows CLI',
674
+ },
675
+ ];
676
+ /**
677
+ * Performs auto-linking for RNW native modules and apps.
678
+ */
316
679
  exports.autoLinkCommand = {
317
680
  name: 'autolink-windows',
318
681
  description: 'performs autolinking',
319
- func: updateAutoLink,
320
- options: [
321
- {
322
- name: '--logging',
323
- description: 'Verbose output logging',
324
- default: false,
325
- },
326
- {
327
- name: '--check',
328
- description: 'Only check whether any autolinked files need to change',
329
- default: false,
330
- },
331
- {
332
- name: '--sln [string]',
333
- description: "Override the app solution file determined by 'react-native config', e.g. windows\\myApp.sln",
334
- default: undefined,
335
- },
336
- {
337
- name: '--proj [string]',
338
- description: "Override the app project file determined by 'react-native config', e.g. windows\\myApp\\myApp.vcxproj",
339
- default: undefined,
340
- },
341
- ],
682
+ func: autolinkWindows,
683
+ options: exports.autolinkOptions,
342
684
  };
343
685
  //# sourceMappingURL=autolink.js.map